diff options
Diffstat (limited to 'Source/WebCore/rendering/RenderLayer.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderLayer.cpp | 4435 |
1 files changed, 2317 insertions, 2118 deletions
diff --git a/Source/WebCore/rendering/RenderLayer.cpp b/Source/WebCore/rendering/RenderLayer.cpp index 65e0d231d..f911119b2 100644 --- a/Source/WebCore/rendering/RenderLayer.cpp +++ b/Source/WebCore/rendering/RenderLayer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved. + * Copyright (C) 2006-2017 Apple Inc. All rights reserved. * * Portions are Copyright (C) 1998 Netscape Communications Corporation. * @@ -44,18 +44,23 @@ #include "config.h" #include "RenderLayer.h" -#include "AnimationController.h" -#include "ColumnInfo.h" +#include "BoxShape.h" +#include "CSSAnimationController.h" #include "CSSPropertyNames.h" #include "Chrome.h" +#include "DebugPageOverlays.h" #include "Document.h" #include "DocumentEventQueue.h" +#include "DocumentMarkerController.h" #include "Element.h" #include "EventHandler.h" -#include "FeatureObserver.h" +#include "FEColorMatrix.h" +#include "FEMerge.h" +#include "FilterEffectRenderer.h" #include "FloatConversion.h" #include "FloatPoint3D.h" #include "FloatRect.h" +#include "FloatRoundedRect.h" #include "FlowThreadController.h" #include "FocusController.h" #include "Frame.h" @@ -66,14 +71,17 @@ #include "FrameView.h" #include "Gradient.h" #include "GraphicsContext.h" +#include "HTMLFormControlElement.h" #include "HTMLFrameElement.h" #include "HTMLFrameOwnerElement.h" +#include "HTMLIFrameElement.h" #include "HTMLNames.h" -#include "HistogramSupport.h" #include "HitTestingTransformState.h" #include "HitTestRequest.h" #include "HitTestResult.h" -#include "InspectorInstrumentation.h" +#include "Logging.h" +#include "MainFrame.h" +#include "NoEventDispatchAssertion.h" #include "OverflowEvent.h" #include "OverlapTestRequestClient.h" #include "Page.h" @@ -81,7 +89,12 @@ #include "RenderFlowThread.h" #include "RenderGeometryMap.h" #include "RenderInline.h" +#include "RenderIterator.h" +#include "RenderLayerBacking.h" +#include "RenderLayerCompositor.h" +#include "RenderLayerFilterInfo.h" #include "RenderMarquee.h" +#include "RenderMultiColumnFlowThread.h" #include "RenderNamedFlowFragment.h" #include "RenderNamedFlowThread.h" #include "RenderRegion.h" @@ -91,9 +104,11 @@ #include "RenderScrollbarPart.h" #include "RenderTableCell.h" #include "RenderTableRow.h" +#include "RenderText.h" #include "RenderTheme.h" #include "RenderTreeAsText.h" #include "RenderView.h" +#include "SVGNames.h" #include "ScaleTransformOperation.h" #include "ScrollAnimator.h" #include "Scrollbar.h" @@ -107,23 +122,13 @@ #include "TextStream.h" #include "TransformationMatrix.h" #include "TranslateTransformOperation.h" +#include "WheelEventTestTrigger.h" +#include <stdio.h> #include <wtf/StdLibExtras.h> #include <wtf/text/CString.h> -#if ENABLE(CSS_FILTERS) -#include "FEColorMatrix.h" -#include "FEMerge.h" -#include "FilterEffectRenderer.h" -#include "RenderLayerFilterInfo.h" -#endif - -#if USE(ACCELERATED_COMPOSITING) -#include "RenderLayerBacking.h" -#include "RenderLayerCompositor.h" -#endif - -#if ENABLE(SVG) -#include "SVGNames.h" +#if ENABLE(CSS_SCROLL_SNAP) +#include "AxisScrollSnapOffsets.h" #endif #define MIN_INTERSECT_FOR_REVEAL 32 @@ -132,14 +137,125 @@ namespace WebCore { using namespace HTMLNames; -bool ClipRect::intersects(const HitTestLocation& hitTestLocation) const -{ - return hitTestLocation.intersects(m_rect); -} +class ClipRects : public RefCounted<ClipRects> { + WTF_MAKE_FAST_ALLOCATED; +public: + static Ref<ClipRects> create() + { + return adoptRef(*new ClipRects); + } + + static Ref<ClipRects> create(const ClipRects& other) + { + return adoptRef(*new ClipRects(other)); + } + + void reset() + { + m_overflowClipRect.reset(); + m_fixedClipRect.reset(); + m_posClipRect.reset(); + m_fixed = false; + } + + const ClipRect& overflowClipRect() const { return m_overflowClipRect; } + void setOverflowClipRect(const ClipRect& clipRect) { m_overflowClipRect = clipRect; } + + const ClipRect& fixedClipRect() const { return m_fixedClipRect; } + void setFixedClipRect(const ClipRect& clipRect) { m_fixedClipRect = clipRect; } + + const ClipRect& posClipRect() const { return m_posClipRect; } + void setPosClipRect(const ClipRect& clipRect) { m_posClipRect = clipRect; } + + bool fixed() const { return m_fixed; } + void setFixed(bool fixed) { m_fixed = fixed; } + + bool operator==(const ClipRects& other) const + { + return m_overflowClipRect == other.overflowClipRect() + && m_fixedClipRect == other.fixedClipRect() + && m_posClipRect == other.posClipRect() + && m_fixed == other.fixed(); + } + + ClipRects& operator=(const ClipRects& other) + { + m_overflowClipRect = other.overflowClipRect(); + m_fixedClipRect = other.fixedClipRect(); + m_posClipRect = other.posClipRect(); + m_fixed = other.fixed(); + return *this; + } + +private: + ClipRects() = default; + + ClipRects(const LayoutRect& clipRect) + : m_overflowClipRect(clipRect) + , m_fixedClipRect(clipRect) + , m_posClipRect(clipRect) + { + } + + ClipRects(const ClipRects& other) + : RefCounted() + , m_fixed(other.fixed()) + , m_overflowClipRect(other.overflowClipRect()) + , m_fixedClipRect(other.fixedClipRect()) + , m_posClipRect(other.posClipRect()) + { + } + + bool m_fixed { false }; + ClipRect m_overflowClipRect; + ClipRect m_fixedClipRect; + ClipRect m_posClipRect; +}; + +class ClipRectsCache { + WTF_MAKE_FAST_ALLOCATED; +public: + ClipRectsCache() + { +#ifndef NDEBUG + for (int i = 0; i < NumCachedClipRectsTypes; ++i) { + m_clipRectsRoot[i] = 0; + m_scrollbarRelevancy[i] = IgnoreOverlayScrollbarSize; + } +#endif + } + + ClipRects* getClipRects(ClipRectsType clipRectsType, ShouldRespectOverflowClip respectOverflow) const + { + return m_clipRects[getIndex(clipRectsType, respectOverflow)].get(); + } + + void setClipRects(ClipRectsType clipRectsType, ShouldRespectOverflowClip respectOverflow, RefPtr<ClipRects>&& clipRects) + { + m_clipRects[getIndex(clipRectsType, respectOverflow)] = WTFMove(clipRects); + } + +#ifndef NDEBUG + const RenderLayer* m_clipRectsRoot[NumCachedClipRectsTypes]; + OverlayScrollbarSizeRelevancy m_scrollbarRelevancy[NumCachedClipRectsTypes]; +#endif + +private: + unsigned getIndex(ClipRectsType clipRectsType, ShouldRespectOverflowClip respectOverflow) const + { + unsigned index = static_cast<unsigned>(clipRectsType); + if (respectOverflow == RespectOverflowClip) + index += static_cast<unsigned>(NumCachedClipRectsTypes); + ASSERT_WITH_SECURITY_IMPLICATION(index < NumCachedClipRectsTypes * 2); + return index; + } + + RefPtr<ClipRects> m_clipRects[NumCachedClipRectsTypes * 2]; +}; void makeMatrixRenderable(TransformationMatrix& matrix, bool has3DRendering) { -#if !ENABLE(3D_RENDERING) +#if !ENABLE(3D_TRANSFORMS) UNUSED_PARAM(has3DRendering); matrix.makeAffine(); #else @@ -149,7 +265,9 @@ void makeMatrixRenderable(TransformationMatrix& matrix, bool has3DRendering) } RenderLayer::RenderLayer(RenderLayerModelObject& rendererLayerModelObject) - : m_inResizeMode(false) + : m_isRootLayer(rendererLayerModelObject.isRenderView()) + , m_forcedStackingContext(rendererLayerModelObject.isMedia()) + , m_inResizeMode(false) , m_scrollDimensionsDirty(true) , m_normalFlowListDirty(true) , m_hasSelfPaintingLayerDescendant(false) @@ -158,7 +276,6 @@ RenderLayer::RenderLayer(RenderLayerModelObject& rendererLayerModelObject) , m_hasOutOfFlowPositionedDescendantDirty(true) , m_needsCompositedScrolling(false) , m_descendantsAreContiguousInStackingOrder(false) - , m_isRootLayer(rendererLayerModelObject.isRenderView()) , m_usedTransparency(false) , m_paintingInsideReflection(false) , m_inOverflowRelayout(false) @@ -167,17 +284,21 @@ RenderLayer::RenderLayer(RenderLayerModelObject& rendererLayerModelObject) , m_hasVisibleContent(false) , m_visibleDescendantStatusDirty(false) , m_hasVisibleDescendant(false) - , m_isPaginated(false) + , m_registeredScrollableArea(false) , m_3DTransformedDescendantStatusDirty(true) , m_has3DTransformedDescendant(false) -#if USE(ACCELERATED_COMPOSITING) , m_hasCompositingDescendant(false) - , m_indirectCompositingReason(NoIndirectCompositingReason) + , m_hasTransformedAncestor(false) + , m_has3DTransformedAncestor(false) + , m_indirectCompositingReason(static_cast<unsigned>(IndirectCompositingReason::None)) , m_viewportConstrainedNotCompositedReason(NoNotCompositedReason) -#endif #if PLATFORM(IOS) , m_adjustForIOSCaretWhenScrolling(false) +#endif +#if PLATFORM(IOS) +#if ENABLE(IOS_TOUCH_EVENTS) , m_registeredAsTouchEventListenerForScrolling(false) +#endif , m_inUserScroll(false) , m_requiresScrollBoundsOriginUpdate(false) #endif @@ -186,21 +307,23 @@ RenderLayer::RenderLayer(RenderLayerModelObject& rendererLayerModelObject) #if !ASSERT_DISABLED , m_layerListMutationAllowed(true) #endif -#if ENABLE(CSS_FILTERS) , m_hasFilterInfo(false) -#endif + , m_hasComputedRepaintRect(false) #if ENABLE(CSS_COMPOSITING) , m_blendMode(BlendModeNormal) + , m_hasNotIsolatedCompositedBlendingDescendants(false) + , m_hasNotIsolatedBlendingDescendants(false) + , m_hasNotIsolatedBlendingDescendantsStatusDirty(false) #endif , m_renderer(rendererLayerModelObject) - , m_parent(0) - , m_previous(0) - , m_next(0) - , m_first(0) - , m_last(0) + , m_parent(nullptr) + , m_previous(nullptr) + , m_next(nullptr) + , m_first(nullptr) + , m_last(nullptr) , m_staticInlinePosition(0) , m_staticBlockPosition(0) - , m_enclosingPaginationLayer(0) + , m_enclosingPaginationLayer(nullptr) { m_isNormalFlowOnly = shouldBeNormalFlowOnly(); m_isSelfPaintingLayer = shouldBeSelfPaintingLayer(); @@ -209,8 +332,6 @@ RenderLayer::RenderLayer(RenderLayerModelObject& rendererLayerModelObject) // there is no need to dirty / recompute these lists. m_zOrderListsDirty = isStackingContainer(); - ScrollableArea::setConstrainsScrollingToContentEdge(false); - if (!renderer().firstChild()) { m_visibleContentStatusDirty = false; m_hasVisibleContent = renderer().style().visibility() == VISIBLE; @@ -218,59 +339,54 @@ RenderLayer::RenderLayer(RenderLayerModelObject& rendererLayerModelObject) if (Element* element = renderer().element()) { // We save and restore only the scrollOffset as the other scroll values are recalculated. - m_scrollOffset = element->savedLayerScrollOffset(); - if (!m_scrollOffset.isZero()) - scrollAnimator()->setCurrentPosition(FloatPoint(m_scrollOffset.width(), m_scrollOffset.height())); - element->setSavedLayerScrollOffset(IntSize()); + m_scrollPosition = element->savedLayerScrollPosition(); + if (!m_scrollPosition.isZero()) + scrollAnimator().setCurrentPosition(m_scrollPosition); + element->setSavedLayerScrollPosition(IntPoint()); } } RenderLayer::~RenderLayer() { - if (inResizeMode() && !renderer().documentBeingDestroyed()) + if (inResizeMode()) renderer().frame().eventHandler().resizeLayerDestroyed(); - renderer().view().frameView().removeScrollableArea(this); + ASSERT(m_registeredScrollableArea == renderer().view().frameView().containsScrollableArea(this)); - if (!renderer().documentBeingDestroyed()) { -#if PLATFORM(IOS) - unregisterAsTouchEventListenerForScrolling(); + if (m_registeredScrollableArea) + renderer().view().frameView().removeScrollableArea(this); + +#if ENABLE(IOS_TOUCH_EVENTS) + unregisterAsTouchEventListenerForScrolling(); #endif - if (Element* element = renderer().element()) - element->setSavedLayerScrollOffset(m_scrollOffset); - } + if (Element* element = renderer().element()) + element->setSavedLayerScrollPosition(m_scrollPosition); destroyScrollbar(HorizontalScrollbar); destroyScrollbar(VerticalScrollbar); - if (renderer().frame().page()) { - if (ScrollingCoordinator* scrollingCoordinator = renderer().frame().page()->scrollingCoordinator()) - scrollingCoordinator->willDestroyScrollableArea(this); - } + if (auto* scrollingCoordinator = renderer().page().scrollingCoordinator()) + scrollingCoordinator->willDestroyScrollableArea(*this); if (m_reflection) removeReflection(); - -#if ENABLE(CSS_FILTERS) + FilterInfo::remove(*this); -#endif // Child layers will be deleted by their corresponding render objects, so // we don't need to delete them ourselves. -#if USE(ACCELERATED_COMPOSITING) clearBacking(true); -#endif } String RenderLayer::name() const { StringBuilder name; - name.append(renderer().renderName()); if (Element* element = renderer().element()) { - name.append(' '); - name.append(element->tagName()); + name.append(" <"); + name.append(element->tagName().convertToLowercaseWithoutLocale()); + name.append('>'); if (element->hasID()) { name.appendLiteral(" id=\'"); @@ -280,14 +396,25 @@ String RenderLayer::name() const if (element->hasClass()) { name.appendLiteral(" class=\'"); - for (size_t i = 0; i < element->classNames().size(); ++i) { + size_t classNamesToDump = element->classNames().size(); + const size_t maxNumClassNames = 7; + bool addEllipsis = false; + if (classNamesToDump > maxNumClassNames) { + classNamesToDump = maxNumClassNames; + addEllipsis = true; + } + + for (size_t i = 0; i < classNamesToDump; ++i) { if (i > 0) name.append(' '); name.append(element->classNames()[i]); } + if (addEllipsis) + name.append("..."); name.append('\''); } - } + } else + name.append(renderer().renderName()); if (isReflection()) name.appendLiteral(" (reflection)"); @@ -295,8 +422,6 @@ String RenderLayer::name() const return name.toString(); } -#if USE(ACCELERATED_COMPOSITING) - RenderLayerCompositor& RenderLayer::compositor() const { return renderer().view().compositor(); @@ -304,33 +429,20 @@ RenderLayerCompositor& RenderLayer::compositor() const void RenderLayer::contentChanged(ContentChangeType changeType) { - // This can get called when video becomes accelerated, so the layers may change. - if ((changeType == CanvasChanged || changeType == VideoChanged || changeType == FullScreenChanged) && compositor().updateLayerCompositingState(*this)) + if ((changeType == CanvasChanged || changeType == VideoChanged || changeType == FullScreenChanged || changeType == ImageChanged) && compositor().updateLayerCompositingState(*this)) compositor().setCompositingLayersNeedRebuild(); if (m_backing) m_backing->contentChanged(changeType); } -#endif // USE(ACCELERATED_COMPOSITING) - bool RenderLayer::canRender3DTransforms() const { -#if USE(ACCELERATED_COMPOSITING) return compositor().canRender3DTransforms(); -#else - return false; -#endif } -#if ENABLE(CSS_FILTERS) - bool RenderLayer::paintsWithFilters() const { - // FIXME: Eventually there will be cases where we paint with filters even without accelerated compositing, - // and this whole function won't be inside the #if below. - -#if USE(ACCELERATED_COMPOSITING) if (!renderer().hasFilter()) return false; @@ -339,7 +451,6 @@ bool RenderLayer::paintsWithFilters() const if (!m_backing || !m_backing->canCompositeFilters()) return true; -#endif return false; } @@ -348,45 +459,21 @@ bool RenderLayer::requiresFullLayerImageForFilters() const { if (!paintsWithFilters()) return false; - FilterEffectRenderer* renderer = filterRenderer(); + auto* renderer = filterRenderer(); return renderer && renderer->hasFilterThatMovesPixels(); } FilterEffectRenderer* RenderLayer::filterRenderer() const { - FilterInfo* filterInfo = FilterInfo::getIfExists(*this); - return filterInfo ? filterInfo->renderer() : 0; -} - -#endif - -LayoutPoint RenderLayer::computeOffsetFromRoot(bool& hasLayerOffset) const -{ - hasLayerOffset = true; - - if (!parent()) - return LayoutPoint(); - - // This is similar to root() but we check if an ancestor layer would - // prevent the optimization from working. - const RenderLayer* rootLayer = 0; - for (const RenderLayer* parentLayer = parent(); parentLayer; rootLayer = parentLayer, parentLayer = parentLayer->parent()) { - hasLayerOffset = parentLayer->canUseConvertToLayerCoords(); - if (!hasLayerOffset) - return LayoutPoint(); - } - ASSERT(rootLayer == root()); - - LayoutPoint offset; - parent()->convertToLayerCoords(rootLayer, offset); - return offset; + auto* filterInfo = FilterInfo::getIfExists(*this); + return filterInfo ? filterInfo->renderer() : nullptr; } void RenderLayer::updateLayerPositionsAfterLayout(const RenderLayer* rootLayer, UpdateLayerPositionsFlags flags) { RenderGeometryMap geometryMap(UseTransforms); if (this != rootLayer) - geometryMap.pushMappingsToAncestor(parent(), 0); + geometryMap.pushMappingsToAncestor(parent(), nullptr); updateLayerPositions(&geometryMap, flags); } @@ -402,26 +489,24 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLay clearClipRects(); if (hasOverflowControls()) { - LayoutPoint offsetFromRoot; + LayoutSize offsetFromRoot; if (geometryMap) - offsetFromRoot = LayoutPoint(geometryMap->absolutePoint(FloatPoint())); + offsetFromRoot = LayoutSize(toFloatSize(geometryMap->absolutePoint(FloatPoint()))); else { // FIXME: It looks suspicious to call convertToLayerCoords here // as canUseConvertToLayerCoords may be true for an ancestor layer. - convertToLayerCoords(root(), offsetFromRoot); + offsetFromRoot = offsetFromAncestor(root()); } - positionOverflowControls(toIntSize(roundedIntPoint(offsetFromRoot))); + positionOverflowControls(roundedIntSize(offsetFromRoot)); } updateDescendantDependentFlags(); if (flags & UpdatePagination) updatePagination(); - else { - m_isPaginated = false; - m_enclosingPaginationLayer = 0; - } - + else + m_enclosingPaginationLayer = nullptr; + if (m_hasVisibleContent) { // FIXME: LayoutState does not work with RenderLayers as there is not a 1-to-1 // mapping between them and the RenderObjects. It would be neat to enable @@ -435,13 +520,13 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLay // FIXME: Should ASSERT that value calculated for m_outlineBox using the cached offset is the same // as the value not using the cached offset, but we can't due to https://bugs.webkit.org/show_bug.cgi?id=37048 - if (flags & CheckForRepaint) { + if ((flags & CheckForRepaint) && m_hasComputedRepaintRect) { if (!renderer().view().printing()) { bool didRepaint = false; if (m_repaintStatus & NeedsFullRepaint) { - renderer().repaintUsingContainer(repaintContainer, pixelSnappedIntRect(oldRepaintRect)); + renderer().repaintUsingContainer(repaintContainer, oldRepaintRect); if (m_repaintRect != oldRepaintRect) { - renderer().repaintUsingContainer(repaintContainer, pixelSnappedIntRect(m_repaintRect)); + renderer().repaintUsingContainer(repaintContainer, m_repaintRect); didRepaint = true; } } else if (shouldRepaintAfterLayout()) { @@ -452,9 +537,9 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLay if (didRepaint && renderer().isRenderNamedFlowFragmentContainer()) { // If we just repainted a region, we must also repaint the flow thread since it is the one // doing the actual painting of the flowed content. - RenderNamedFlowFragment* region = toRenderBlockFlow(&renderer())->renderNamedFlowFragment(); - if (region->flowThread()) - region->flowThread()->layer()->repaintIncludingDescendants(); + RenderNamedFlowFragment& region = *downcast<RenderBlockFlow>(renderer()).renderNamedFlowFragment(); + if (region.isValid()) + region.flowThread()->layer()->repaintIncludingDescendants(); } } } @@ -462,30 +547,32 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLay clearRepaintRects(); m_repaintStatus = NeedsNormalRepaint; + m_hasTransformedAncestor = flags & SeenTransformedLayer; + m_has3DTransformedAncestor = flags & Seen3DTransformedLayer; - // Go ahead and update the reflection's position and size. + // Update the reflection's position and size. if (m_reflection) m_reflection->layout(); -#if USE(ACCELERATED_COMPOSITING) // Clear the IsCompositingUpdateRoot flag once we've found the first compositing layer in this update. bool isUpdateRoot = (flags & IsCompositingUpdateRoot); if (isComposited()) flags &= ~IsCompositingUpdateRoot; -#endif - if (useRegionBasedColumns() && renderer().isInFlowRenderFlowThread()) { + if (renderer().isInFlowRenderFlowThread()) { updatePagination(); flags |= UpdatePagination; } - - if (renderer().hasColumns()) - flags |= UpdatePagination; + + if (transform()) { + flags |= SeenTransformedLayer; + if (!transform()->isAffine()) + flags |= Seen3DTransformedLayer; + } for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) child->updateLayerPositions(geometryMap, flags); -#if USE(ACCELERATED_COMPOSITING) if ((flags & UpdateCompositingLayers) && isComposited()) { RenderLayerBacking::UpdateAfterLayoutFlags updateFlags = RenderLayerBacking::CompositingChildrenOnly; if (flags & NeedsFullRepaintInBacking) @@ -494,11 +581,10 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLay updateFlags |= RenderLayerBacking::IsUpdateRoot; backing()->updateAfterLayout(updateFlags); } -#endif // With all our children positioned, now update our marquee if we need to. if (m_marquee) { - // FIXME: would like to use TemporaryChange<> but it doesn't work with bitfields. + // FIXME: would like to use SetForScope<> but it doesn't work with bitfields. bool oldUpdatingMarqueePosition = m_updatingMarqueePosition; m_updatingMarqueePosition = true; m_marquee->updateMarqueePosition(); @@ -507,6 +593,8 @@ void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLay if (geometryMap) geometryMap->popMappingsToAncestor(parent()); + + renderer().document().markers().invalidateRectsForAllMarkers(); } LayoutRect RenderLayer::repaintRectIncludingNonCompositingDescendants() const @@ -517,7 +605,7 @@ LayoutRect RenderLayer::repaintRectIncludingNonCompositingDescendants() const if (child->isComposited()) continue; - repaintRect.unite(child->repaintRectIncludingNonCompositingDescendants()); + repaintRect.uniteIfNonZero(child->repaintRectIncludingNonCompositingDescendants()); } return repaintRect; } @@ -548,7 +636,7 @@ void RenderLayer::dirtyAncestorChainHasSelfPaintingLayerDescendantStatus() bool RenderLayer::acceleratedCompositingForOverflowScrollEnabled() const { - return renderer().frame().settings().acceleratedCompositingForOverflowScrollEnabled(); + return renderer().settings().acceleratedCompositingForOverflowScrollEnabled(); } // If we are a stacking container, then this function will determine if our @@ -636,8 +724,8 @@ void RenderLayer::updateDescendantsAreContiguousInStackingOrder() ASSERT(!m_normalFlowListDirty); ASSERT(!m_zOrderListsDirty); - OwnPtr<Vector<RenderLayer*>> posZOrderList; - OwnPtr<Vector<RenderLayer*>> negZOrderList; + std::unique_ptr<Vector<RenderLayer*>> posZOrderList; + std::unique_ptr<Vector<RenderLayer*>> negZOrderList; rebuildZOrderLists(StopAtStackingContexts, posZOrderList, negZOrderList); // Create a reverse lookup. @@ -697,14 +785,10 @@ void RenderLayer::updateDescendantsAreContiguousInStackingOrderRecursive(const H if (!isStackingContext()) { bool newValue = maxIndex - minIndex == count; -#if USE(ACCELERATED_COMPOSITING) bool didUpdate = newValue != m_descendantsAreContiguousInStackingOrder; -#endif m_descendantsAreContiguousInStackingOrder = newValue; -#if USE(ACCELERATED_COMPOSITING) if (didUpdate) updateNeedsCompositedScrolling(); -#endif } } @@ -712,6 +796,12 @@ void RenderLayer::computeRepaintRects(const RenderLayerModelObject* repaintConta { ASSERT(!m_visibleContentStatusDirty); + if (!isSelfPaintingLayer()) { + clearRepaintRects(); + return; + } + + m_hasComputedRepaintRect = true; m_repaintRect = renderer().clippedOverflowRectForRepaint(repaintContainer); m_outlineBox = renderer().outlineBoundsForRepaint(repaintContainer, geometryMap); } @@ -730,17 +820,19 @@ void RenderLayer::computeRepaintRectsIncludingDescendants() void RenderLayer::clearRepaintRects() { - ASSERT(!m_hasVisibleContent); ASSERT(!m_visibleContentStatusDirty); - m_repaintRect = IntRect(); - m_outlineBox = IntRect(); + m_hasComputedRepaintRect = false; + m_repaintRect = LayoutRect(); + m_outlineBox = LayoutRect(); } void RenderLayer::updateLayerPositionsAfterDocumentScroll() { ASSERT(this == renderer().view().layer()); + LOG(Scrolling, "RenderLayer::updateLayerPositionsAfterDocumentScroll"); + RenderGeometryMap geometryMap(UseTransforms); updateLayerPositionsAfterScroll(&geometryMap); } @@ -749,7 +841,7 @@ void RenderLayer::updateLayerPositionsAfterOverflowScroll() { RenderGeometryMap geometryMap(UseTransforms); if (this != renderer().view().layer()) - geometryMap.pushMappingsToAncestor(parent(), 0); + geometryMap.pushMappingsToAncestor(parent(), nullptr); // FIXME: why is it OK to not check the ancestors of this layer in order to // initialize the HasSeenViewportConstrainedAncestor and HasSeenAncestorWithOverflowClip flags? @@ -772,9 +864,6 @@ void RenderLayer::updateLayerPositionsAfterScroll(RenderGeometryMap* geometryMap if (positionChanged) flags |= HasChangedAncestor; - if (geometryMap) - geometryMap->pushMappingsToAncestor(this, parent()); - if (flags & HasChangedAncestor || flags & HasSeenViewportConstrainedAncestor || flags & IsOverflowScroll) clearClipRects(); @@ -783,15 +872,23 @@ void RenderLayer::updateLayerPositionsAfterScroll(RenderGeometryMap* geometryMap if (renderer().hasOverflowClip()) flags |= HasSeenAncestorWithOverflowClip; + + bool shouldComputeRepaintRects = (flags & HasSeenViewportConstrainedAncestor || (flags & IsOverflowScroll && flags & HasSeenAncestorWithOverflowClip)) && isSelfPaintingLayer(); + bool isVisuallyEmpty = !isVisuallyNonEmpty(); + bool shouldPushAndPopMappings = geometryMap && ((shouldComputeRepaintRects && !isVisuallyEmpty) || firstChild()); + if (shouldPushAndPopMappings) + geometryMap->pushMappingsToAncestor(this, parent()); - if (flags & HasSeenViewportConstrainedAncestor - || (flags & IsOverflowScroll && flags & HasSeenAncestorWithOverflowClip)) { - // FIXME: We could track the repaint container as we walk down the tree. - computeRepaintRects(renderer().containerForRepaint(), geometryMap); + if (shouldComputeRepaintRects) { + // When scrolling, we don't compute repaint rects for visually non-empty layers. + if (isVisuallyEmpty) + clearRepaintRects(); + else // FIXME: We could track the repaint container as we walk down the tree. + computeRepaintRects(renderer().containerForRepaint(), geometryMap); } else { // Check that our cached rects are correct. - ASSERT(m_repaintRect == renderer().clippedOverflowRectForRepaint(renderer().containerForRepaint())); - ASSERT(m_outlineBox == renderer().outlineBoundsForRepaint(renderer().containerForRepaint(), geometryMap)); + ASSERT(!m_hasComputedRepaintRect || (m_repaintRect == renderer().clippedOverflowRectForRepaint(renderer().containerForRepaint()))); + ASSERT(!m_hasComputedRepaintRect || m_outlineBox == renderer().outlineBoundsForRepaint(renderer().containerForRepaint())); } for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) @@ -808,11 +905,11 @@ void RenderLayer::updateLayerPositionsAfterScroll(RenderGeometryMap* geometryMap m_updatingMarqueePosition = oldUpdatingMarqueePosition; } - if (geometryMap) + if (shouldPushAndPopMappings) geometryMap->popMappingsToAncestor(parent()); -} -#if USE(ACCELERATED_COMPOSITING) + renderer().document().markers().invalidateRectsForAllMarkers(); +} void RenderLayer::positionNewlyCreatedOverflowControls() { @@ -821,41 +918,69 @@ void RenderLayer::positionNewlyCreatedOverflowControls() RenderGeometryMap geometryMap(UseTransforms); if (this != renderer().view().layer() && parent()) - geometryMap.pushMappingsToAncestor(parent(), 0); + geometryMap.pushMappingsToAncestor(parent(), nullptr); LayoutPoint offsetFromRoot = LayoutPoint(geometryMap.absolutePoint(FloatPoint())); positionOverflowControls(toIntSize(roundedIntPoint(offsetFromRoot))); } -#endif - #if ENABLE(CSS_COMPOSITING) void RenderLayer::updateBlendMode() { + bool hadBlendMode = m_blendMode != BlendModeNormal; + if (parent() && hadBlendMode != hasBlendMode()) { + if (hasBlendMode()) + parent()->updateAncestorChainHasBlendingDescendants(); + else + parent()->dirtyAncestorChainHasBlendingDescendants(); + } + BlendMode newBlendMode = renderer().style().blendMode(); - if (newBlendMode != m_blendMode) { + if (newBlendMode != m_blendMode) m_blendMode = newBlendMode; - if (backing()) - backing()->setBlendMode(newBlendMode); +} + +void RenderLayer::updateAncestorChainHasBlendingDescendants() +{ + for (auto* layer = this; layer; layer = layer->parent()) { + if (!layer->hasNotIsolatedBlendingDescendantsStatusDirty() && layer->hasNotIsolatedBlendingDescendants()) + break; + layer->m_hasNotIsolatedBlendingDescendants = true; + layer->m_hasNotIsolatedBlendingDescendantsStatusDirty = false; + + layer->updateSelfPaintingLayer(); + + if (layer->isStackingContext()) + break; } } +void RenderLayer::dirtyAncestorChainHasBlendingDescendants() +{ + for (auto* layer = this; layer; layer = layer->parent()) { + if (layer->hasNotIsolatedBlendingDescendantsStatusDirty()) + break; + + layer->m_hasNotIsolatedBlendingDescendantsStatusDirty = true; + + if (layer->isStackingContext()) + break; + } +} #endif void RenderLayer::updateTransform() { - // hasTransform() on the renderer is also true when there is transform-style: preserve-3d or perspective set, - // so check style too. - bool hasTransform = renderer().hasTransform() && renderer().style().hasTransform(); + bool hasTransform = renderer().hasTransform(); bool had3DTransform = has3DTransform(); - bool hadTransform = m_transform; + bool hadTransform = !!m_transform; if (hasTransform != hadTransform) { if (hasTransform) - m_transform = adoptPtr(new TransformationMatrix); + m_transform = std::make_unique<TransformationMatrix>(); else - m_transform.clear(); + m_transform = nullptr; // Layers with transforms act as clip rects roots, so clear the cached clip rects here. clearClipRectsIncludingDescendants(); @@ -865,7 +990,7 @@ void RenderLayer::updateTransform() RenderBox* box = renderBox(); ASSERT(box); m_transform->makeIdentity(); - box->style().applyTransform(*m_transform, box->pixelSnappedBorderBoxRect().size(), RenderStyle::IncludeTransformOrigin); + box->style().applyTransform(*m_transform, snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor()), RenderStyle::IncludeTransformOrigin); makeMatrixRenderable(*m_transform, canRender3DTransforms()); } @@ -877,27 +1002,26 @@ TransformationMatrix RenderLayer::currentTransform(RenderStyle::ApplyTransformOr { if (!m_transform) return TransformationMatrix(); + + RenderBox* box = renderBox(); -#if USE(ACCELERATED_COMPOSITING) - if (renderer().style().isRunningAcceleratedAnimation()) { + if (renderer().animation().isRunningAcceleratedAnimationOnRenderer(renderer(), CSSPropertyTransform, AnimationBase::Running | AnimationBase::Paused)) { TransformationMatrix currTransform; - RefPtr<RenderStyle> style = renderer().animation().getAnimatedStyleForRenderer(&renderer()); - style->applyTransform(currTransform, renderBox()->pixelSnappedBorderBoxRect().size(), applyOrigin); + FloatRect pixelSnappedBorderRect = snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor()); + std::unique_ptr<RenderStyle> style = renderer().animation().getAnimatedStyleForRenderer(renderer()); + style->applyTransform(currTransform, pixelSnappedBorderRect, applyOrigin); makeMatrixRenderable(currTransform, canRender3DTransforms()); return currTransform; } // m_transform includes transform-origin, so we need to recompute the transform here. if (applyOrigin == RenderStyle::ExcludeTransformOrigin) { - RenderBox* box = renderBox(); TransformationMatrix currTransform; - box->style().applyTransform(currTransform, box->pixelSnappedBorderBoxRect().size(), RenderStyle::ExcludeTransformOrigin); + FloatRect pixelSnappedBorderRect = snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor()); + box->style().applyTransform(currTransform, pixelSnappedBorderRect, RenderStyle::ExcludeTransformOrigin); makeMatrixRenderable(currTransform, canRender3DTransforms()); return currTransform; } -#else - UNUSED_PARAM(applyOrigin); -#endif return *m_transform; } @@ -925,102 +1049,85 @@ RenderLayer* RenderLayer::enclosingOverflowClipLayer(IncludeSelfOrNot includeSel layer = layer->parent(); } - return 0; + return nullptr; } -static bool checkContainingBlockChainForPagination(RenderLayerModelObject* renderer, RenderBox* ancestorColumnsRenderer) +// FIXME: This is terrible. Bring back a cached bit for this someday. This crawl is going to slow down all +// painting of content inside paginated layers. +bool RenderLayer::hasCompositedLayerInEnclosingPaginationChain() const { - RenderLayerModelObject* prevBlock = renderer; - RenderBlock* containingBlock; - for (containingBlock = renderer->containingBlock(); - containingBlock && containingBlock != &renderer->view() && containingBlock != ancestorColumnsRenderer; - containingBlock = containingBlock->containingBlock()) { - prevBlock = containingBlock; - } - - // If the columns block wasn't in our containing block chain, then we aren't paginated by it. - if (containingBlock != ancestorColumnsRenderer) + // No enclosing layer means no compositing in the chain. + if (!m_enclosingPaginationLayer) return false; - - // If the previous block is absolutely positioned, then we can't be paginated by the columns block. - if (prevBlock->isOutOfFlowPositioned()) + + // If the enclosing layer is composited, we don't have to check anything in between us and that + // layer. + if (m_enclosingPaginationLayer->isComposited()) + return true; + + // If we are the enclosing pagination layer, then we can't be composited or we'd have passed the + // previous check. + if (m_enclosingPaginationLayer == this) return false; - - // Otherwise we are paginated by the columns block. - return true; -} -bool RenderLayer::useRegionBasedColumns() const -{ - const Settings& settings = renderer().frame().settings(); - return settings.regionBasedColumnsEnabled(); + // The enclosing paginated layer is our ancestor and is not composited, so we have to check + // intermediate layers between us and the enclosing pagination layer. Start with our own layer. + if (isComposited()) + return true; + + // For normal flow layers, we can recur up the layer tree. + if (isNormalFlowOnly()) + return parent()->hasCompositedLayerInEnclosingPaginationChain(); + + // Otherwise we have to go up the containing block chain. Find the first enclosing + // containing block layer ancestor, and check that. + for (const auto* containingBlock = renderer().containingBlock(); containingBlock && !is<RenderView>(*containingBlock); containingBlock = containingBlock->containingBlock()) { + if (containingBlock->hasLayer()) + return containingBlock->layer()->hasCompositedLayerInEnclosingPaginationChain(); + } + return false; } void RenderLayer::updatePagination() { - m_isPaginated = false; - m_enclosingPaginationLayer = 0; - - if (isComposited() || !parent()) - return; // FIXME: We will have to deal with paginated compositing layers someday. - // FIXME: For now the RenderView can't be paginated. Eventually printing will move to a model where it is though. - - // The main difference between the paginated booleans for the old column code and the new column code - // is that each paginated layer has to paint on its own with the new code. There is no - // recurring into child layers. This means that the m_isPaginated bits for the new column code can't just be set on - // "roots" that get split and paint all their descendants. Instead each layer has to be checked individually and + m_enclosingPaginationLayer = nullptr; + + if (!parent()) + return; + + // Each layer that is inside a multicolumn flow thread has to be checked individually and // genuinely know if it is going to have to split itself up when painting only its contents (and not any other descendant // layers). We track an enclosingPaginationLayer instead of using a simple bit, since we want to be able to get back // to that layer easily. - bool regionBasedColumnsUsed = useRegionBasedColumns(); - if (regionBasedColumnsUsed && renderer().isInFlowRenderFlowThread()) { + if (renderer().isInFlowRenderFlowThread()) { m_enclosingPaginationLayer = this; return; } if (isNormalFlowOnly()) { - if (regionBasedColumnsUsed) { - // Content inside a transform is not considered to be paginated, since we simply - // paint the transform multiple times in each column, so we don't have to use - // fragments for the transformed content. - m_enclosingPaginationLayer = parent()->enclosingPaginationLayer(); - if (m_enclosingPaginationLayer && m_enclosingPaginationLayer->hasTransform()) - m_enclosingPaginationLayer = 0; - } else - m_isPaginated = parent()->renderer().hasColumns(); + // Content inside a transform is not considered to be paginated, since we simply + // paint the transform multiple times in each column, so we don't have to use + // fragments for the transformed content. + if (parent()->hasTransform()) + m_enclosingPaginationLayer = nullptr; + else + m_enclosingPaginationLayer = parent()->enclosingPaginationLayer(IncludeCompositedPaginatedLayers); return; } // For the new columns code, we want to walk up our containing block chain looking for an enclosing layer. Once // we find one, then we just check its pagination status. - if (regionBasedColumnsUsed) { - RenderView* renderView = &renderer().view(); - RenderBlock* containingBlock; - for (containingBlock = renderer().containingBlock(); - containingBlock && containingBlock != renderView; - containingBlock = containingBlock->containingBlock()) { - if (containingBlock->hasLayer()) { - // Content inside a transform is not considered to be paginated, since we simply - // paint the transform multiple times in each column, so we don't have to use - // fragments for the transformed content. - m_enclosingPaginationLayer = containingBlock->layer()->enclosingPaginationLayer(); - if (m_enclosingPaginationLayer && m_enclosingPaginationLayer->hasTransform()) - m_enclosingPaginationLayer = 0; - return; - } - } - return; - } - - // If we're not normal flow, then we need to look for a multi-column object between us and our stacking container. - RenderLayer* ancestorStackingContainer = stackingContainer(); - for (RenderLayer* curr = parent(); curr; curr = curr->parent()) { - if (curr->renderer().hasColumns()) { - m_isPaginated = checkContainingBlockChainForPagination(&renderer(), curr->renderBox()); + for (const auto* containingBlock = renderer().containingBlock(); containingBlock && !is<RenderView>(*containingBlock); containingBlock = containingBlock->containingBlock()) { + if (containingBlock->hasLayer()) { + // Content inside a transform is not considered to be paginated, since we simply + // paint the transform multiple times in each column, so we don't have to use + // fragments for the transformed content. + if (containingBlock->layer()->hasTransform()) + m_enclosingPaginationLayer = nullptr; + else + m_enclosingPaginationLayer = containingBlock->layer()->enclosingPaginationLayer(IncludeCompositedPaginatedLayers); return; } - if (curr == ancestorStackingContainer) - return; } } @@ -1087,10 +1194,13 @@ void RenderLayer::setAncestorChainHasVisibleDescendant() void RenderLayer::updateDescendantDependentFlags(HashSet<const RenderObject*>* outOfFlowDescendantContainingBlocks) { - if (m_visibleDescendantStatusDirty || m_hasSelfPaintingLayerDescendantDirty || m_hasOutOfFlowPositionedDescendantDirty) { - m_hasVisibleDescendant = false; - m_hasSelfPaintingLayerDescendant = false; - m_hasOutOfFlowPositionedDescendant = false; + if (m_visibleDescendantStatusDirty || m_hasSelfPaintingLayerDescendantDirty || m_hasOutOfFlowPositionedDescendantDirty || hasNotIsolatedBlendingDescendantsStatusDirty()) { + bool hasVisibleDescendant = false; + bool hasSelfPaintingLayerDescendant = false; + bool hasOutOfFlowPositionedDescendant = false; +#if ENABLE(CSS_COMPOSITING) + bool hasNotIsolatedBlendingDescendants = false; +#endif HashSet<const RenderObject*> childOutOfFlowDescendantContainingBlocks; for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { @@ -1107,29 +1217,41 @@ void RenderLayer::updateDescendantDependentFlags(HashSet<const RenderObject*>* o outOfFlowDescendantContainingBlocks->add(*it); } - bool hasVisibleDescendant = child->m_hasVisibleContent || child->m_hasVisibleDescendant; - bool hasSelfPaintingLayerDescendant = child->isSelfPaintingLayer() || child->hasSelfPaintingLayerDescendant(); - bool hasOutOfFlowPositionedDescendant = !childOutOfFlowDescendantContainingBlocks.isEmpty(); - - m_hasVisibleDescendant |= hasVisibleDescendant; - m_hasSelfPaintingLayerDescendant |= hasSelfPaintingLayerDescendant; - m_hasOutOfFlowPositionedDescendant |= hasOutOfFlowPositionedDescendant; + hasVisibleDescendant |= child->m_hasVisibleContent || child->m_hasVisibleDescendant; + hasSelfPaintingLayerDescendant |= child->isSelfPaintingLayer() || child->hasSelfPaintingLayerDescendant(); + hasOutOfFlowPositionedDescendant |= !childOutOfFlowDescendantContainingBlocks.isEmpty(); +#if ENABLE(CSS_COMPOSITING) + hasNotIsolatedBlendingDescendants |= child->hasBlendMode() || (child->hasNotIsolatedBlendingDescendants() && !child->isolatesBlending()); +#endif - if (m_hasVisibleDescendant && m_hasSelfPaintingLayerDescendant && m_hasOutOfFlowPositionedDescendant) + bool allFlagsSet = hasVisibleDescendant && hasSelfPaintingLayerDescendant && hasOutOfFlowPositionedDescendant; +#if ENABLE(CSS_COMPOSITING) + allFlagsSet &= hasNotIsolatedBlendingDescendants; +#endif + if (allFlagsSet) break; } if (outOfFlowDescendantContainingBlocks) outOfFlowDescendantContainingBlocks->remove(&renderer()); + m_hasVisibleDescendant = hasVisibleDescendant; m_visibleDescendantStatusDirty = false; + m_hasSelfPaintingLayerDescendant = hasSelfPaintingLayerDescendant; m_hasSelfPaintingLayerDescendantDirty = false; -#if USE(ACCELERATED_COMPOSITING) + m_hasOutOfFlowPositionedDescendant = hasOutOfFlowPositionedDescendant; if (m_hasOutOfFlowPositionedDescendantDirty) updateNeedsCompositedScrolling(); -#endif + m_hasOutOfFlowPositionedDescendantDirty = false; +#if ENABLE(CSS_COMPOSITING) + m_hasNotIsolatedBlendingDescendants = hasNotIsolatedBlendingDescendants; + if (m_hasNotIsolatedBlendingDescendantsStatusDirty) { + m_hasNotIsolatedBlendingDescendantsStatusDirty = false; + updateSelfPaintingLayer(); + } +#endif } if (m_visibleContentStatusDirty) { @@ -1144,7 +1266,7 @@ void RenderLayer::updateDescendantDependentFlags(HashSet<const RenderObject*>* o m_hasVisibleContent = true; break; } - RenderObject* child; + RenderObject* child = nullptr; if (!r->hasLayer() && (child = r->firstChildSlow())) r = child; else if (r->nextSibling()) @@ -1153,7 +1275,7 @@ void RenderLayer::updateDescendantDependentFlags(HashSet<const RenderObject*>* o do { r = r->parent(); if (r == &renderer()) - r = 0; + r = nullptr; } while (r && !r->nextSibling()); if (r) r = r->nextSibling(); @@ -1164,23 +1286,6 @@ void RenderLayer::updateDescendantDependentFlags(HashSet<const RenderObject*>* o } } -#if USE(ACCELERATED_COMPOSITING) -// Return true if the new clipping behaviour requires layer update. -bool RenderLayer::checkIfDescendantClippingContextNeedsUpdate(bool isClipping) -{ - for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { - RenderLayerBacking* backing = child->backing(); - // Layer subtree needs update when new clipping is added or existing clipping is removed. - if (backing && (isClipping || backing->hasAncestorClippingLayer())) - return true; - - if (child->checkIfDescendantClippingContextNeedsUpdate(isClipping)) - return true; - } - return false; -} -#endif - void RenderLayer::dirty3DTransformedDescendantStatus() { RenderLayer* curr = stackingContainer(); @@ -1206,14 +1311,14 @@ bool RenderLayer::update3DTransformedDescendantStatus() // Transformed or preserve-3d descendants can only be in the z-order lists, not // in the normal flow list, so we only need to check those. if (Vector<RenderLayer*>* positiveZOrderList = posZOrderList()) { - for (unsigned i = 0; i < positiveZOrderList->size(); ++i) - m_has3DTransformedDescendant |= positiveZOrderList->at(i)->update3DTransformedDescendantStatus(); + for (auto* layer : *positiveZOrderList) + m_has3DTransformedDescendant |= layer->update3DTransformedDescendantStatus(); } // Now check our negative z-index children. if (Vector<RenderLayer*>* negativeZOrderList = negZOrderList()) { - for (unsigned i = 0; i < negativeZOrderList->size(); ++i) - m_has3DTransformedDescendant |= negativeZOrderList->at(i)->update3DTransformedDescendantStatus(); + for (auto* layer : *negativeZOrderList) + m_has3DTransformedDescendant |= layer->update3DTransformedDescendantStatus(); } m_3DTransformedDescendantStatusDirty = false; @@ -1231,60 +1336,55 @@ bool RenderLayer::updateLayerPosition() { LayoutPoint localPoint; LayoutSize inlineBoundingBoxOffset; // We don't put this into the RenderLayer x/y for inlines, so we need to subtract it out when done. - if (renderer().isInline() && renderer().isRenderInline()) { - RenderInline& inlineFlow = toRenderInline(renderer()); + if (renderer().isInline() && is<RenderInline>(renderer())) { + auto& inlineFlow = downcast<RenderInline>(renderer()); IntRect lineBox = inlineFlow.linesBoundingBox(); setSize(lineBox.size()); - inlineBoundingBoxOffset = toSize(lineBox.location()); + inlineBoundingBoxOffset = toLayoutSize(lineBox.location()); localPoint += inlineBoundingBoxOffset; } else if (RenderBox* box = renderBox()) { // FIXME: Is snapping the size really needed here for the RenderBox case? - setSize(pixelSnappedIntSize(box->size(), box->location())); - localPoint += box->topLeftLocationOffset(); + setSize(snappedIntRect(box->frameRect()).size()); + box->applyTopLeftLocationOffset(localPoint); } - if (!renderer().isOutOfFlowPositioned() && renderer().parent()) { + if (!renderer().isOutOfFlowPositioned()) { + auto* ancestor = renderer().parent(); // We must adjust our position by walking up the render tree looking for the // nearest enclosing object with a layer. - RenderElement* curr = renderer().parent(); - while (curr && !curr->hasLayer()) { - if (curr->isBox() && !curr->isTableRow()) { + while (ancestor && !ancestor->hasLayer()) { + if (is<RenderBox>(*ancestor) && !is<RenderTableRow>(*ancestor)) { // Rows and cells share the same coordinate space (that of the section). // Omit them when computing our xpos/ypos. - localPoint += toRenderBox(curr)->topLeftLocationOffset(); + localPoint += downcast<RenderBox>(*ancestor).topLeftLocationOffset(); } - curr = curr->parent(); + ancestor = ancestor->parent(); } - if (curr->isBox() && curr->isTableRow()) { + if (is<RenderTableRow>(ancestor)) { // Put ourselves into the row coordinate space. - localPoint -= toRenderBox(curr)->topLeftLocationOffset(); + localPoint -= downcast<RenderTableRow>(*ancestor).topLeftLocationOffset(); } } // Subtract our parent's scroll offset. - if (renderer().isOutOfFlowPositioned() && enclosingPositionedAncestor()) { - RenderLayer* positionedParent = enclosingPositionedAncestor(); - + RenderLayer* positionedParent; + if (renderer().isOutOfFlowPositioned() && (positionedParent = enclosingAncestorForPosition(renderer().style().position()))) { // For positioned layers, we subtract out the enclosing positioned layer's scroll offset. - if (positionedParent->renderer().hasOverflowClip()) { - LayoutSize offset = positionedParent->scrolledContentOffset(); - localPoint -= offset; - } + if (positionedParent->renderer().hasOverflowClip()) + localPoint -= toLayoutSize(positionedParent->scrollPosition()); - if (renderer().isOutOfFlowPositioned() && positionedParent->renderer().isInFlowPositioned() && positionedParent->renderer().isRenderInline()) { - LayoutSize offset = toRenderInline(positionedParent->renderer()).offsetForInFlowPositionedInline(&toRenderBox(renderer())); + if (renderer().isOutOfFlowPositioned() && positionedParent->renderer().isInFlowPositioned() && is<RenderInline>(positionedParent->renderer())) { + LayoutSize offset = downcast<RenderInline>(positionedParent->renderer()).offsetForInFlowPositionedInline(&downcast<RenderBox>(renderer())); localPoint += offset; } } else if (parent()) { - if (parent()->renderer().hasOverflowClip()) { - IntSize scrollOffset = parent()->scrolledContentOffset(); - localPoint -= scrollOffset; - } + if (parent()->renderer().hasOverflowClip()) + localPoint -= toLayoutSize(parent()->scrollPosition()); } bool positionOrOffsetChanged = false; if (renderer().isInFlowPositioned()) { - LayoutSize newOffset = toRenderBoxModelObject(renderer()).offsetForInFlowPosition(); + LayoutSize newOffset = downcast<RenderBoxModelObject>(renderer()).offsetForInFlowPosition(); positionOrOffsetChanged = newOffset != m_offsetForInFlowPosition; m_offsetForInFlowPosition = newOffset; localPoint.move(m_offsetForInFlowPosition); @@ -1302,25 +1402,26 @@ bool RenderLayer::updateLayerPosition() TransformationMatrix RenderLayer::perspectiveTransform() const { - if (!renderer().hasTransform()) + RenderBox* box = renderBox(); + if (!box) + return TransformationMatrix(); + + if (!box->hasTransformRelatedProperty()) return TransformationMatrix(); - const RenderStyle& style = renderer().style(); + const RenderStyle& style = box->style(); if (!style.hasPerspective()) return TransformationMatrix(); // Maybe fetch the perspective from the backing? - const IntRect borderBox = toRenderBox(renderer()).pixelSnappedBorderBoxRect(); - const float boxWidth = borderBox.width(); - const float boxHeight = borderBox.height(); - - float perspectiveOriginX = floatValueForLength(style.perspectiveOriginX(), boxWidth); - float perspectiveOriginY = floatValueForLength(style.perspectiveOriginY(), boxHeight); + const FloatRect borderBox = snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor()); + float perspectiveOriginX = floatValueForLength(style.perspectiveOriginX(), borderBox.width()); + float perspectiveOriginY = floatValueForLength(style.perspectiveOriginY(), borderBox.height()); // A perspective origin of 0,0 makes the vanishing point in the center of the element. // We want it to be in the top-left, so subtract half the height and width. - perspectiveOriginX -= boxWidth / 2.0f; - perspectiveOriginY -= boxHeight / 2.0f; + perspectiveOriginX -= borderBox.width() / 2.0f; + perspectiveOriginY -= borderBox.height() / 2.0f; TransformationMatrix t; t.translate(perspectiveOriginX, perspectiveOriginY); @@ -1332,10 +1433,10 @@ TransformationMatrix RenderLayer::perspectiveTransform() const FloatPoint RenderLayer::perspectiveOrigin() const { - if (!renderer().hasTransform()) + if (!renderer().hasTransformRelatedProperty()) return FloatPoint(); - const LayoutRect borderBox = toRenderBox(renderer()).borderBoxRect(); + const LayoutRect borderBox = downcast<RenderBox>(renderer()).borderBoxRect(); const RenderStyle& style = renderer().style(); return FloatPoint(floatValueForLength(style.perspectiveOriginX(), borderBox.width()), @@ -1352,55 +1453,77 @@ RenderLayer* RenderLayer::stackingContainer() const return layer; } -static inline bool isPositionedContainer(RenderLayer* layer) +static inline bool isContainerForPositioned(RenderLayer& layer, EPosition position) { - return layer->isRootLayer() || layer->renderer().isPositioned() || layer->hasTransform(); -} + switch (position) { + case FixedPosition: + return layer.renderer().canContainFixedPositionObjects(); -static inline bool isFixedPositionedContainer(RenderLayer* layer) -{ - return layer->isRootLayer() || layer->hasTransform(); + case AbsolutePosition: + return layer.renderer().canContainAbsolutelyPositionedObjects(); + + default: + ASSERT_NOT_REACHED(); + return false; + } } -RenderLayer* RenderLayer::enclosingPositionedAncestor() const +RenderLayer* RenderLayer::enclosingAncestorForPosition(EPosition position) const { RenderLayer* curr = parent(); - while (curr && !isPositionedContainer(curr)) + while (curr && !isContainerForPositioned(*curr, position)) curr = curr->parent(); return curr; } -static RenderLayer* parentLayerCrossFrame(const RenderLayer* layer) +static RenderLayer* parentLayerCrossFrame(const RenderLayer& layer) { - ASSERT(layer); - if (layer->parent()) - return layer->parent(); + if (layer.parent()) + return layer.parent(); - HTMLFrameOwnerElement* ownerElement = layer->renderer().document().ownerElement(); + HTMLFrameOwnerElement* ownerElement = layer.renderer().document().ownerElement(); if (!ownerElement) - return 0; + return nullptr; RenderElement* ownerRenderer = ownerElement->renderer(); if (!ownerRenderer) - return 0; + return nullptr; return ownerRenderer->enclosingLayer(); } RenderLayer* RenderLayer::enclosingScrollableLayer() const { - for (RenderLayer* nextLayer = parentLayerCrossFrame(this); nextLayer; nextLayer = parentLayerCrossFrame(nextLayer)) { - if (nextLayer->renderer().isBox() && toRenderBox(nextLayer->renderer()).canBeScrolledAndHasScrollableArea()) + for (RenderLayer* nextLayer = parentLayerCrossFrame(*this); nextLayer; nextLayer = parentLayerCrossFrame(*nextLayer)) { + if (is<RenderBox>(nextLayer->renderer()) && downcast<RenderBox>(nextLayer->renderer()).canBeScrolledAndHasScrollableArea()) return nextLayer; } - return 0; + return nullptr; } -IntRect RenderLayer::scrollableAreaBoundingBox() const +IntRect RenderLayer::scrollableAreaBoundingBox(bool* isInsideFixed) const { - return renderer().absoluteBoundingBoxRect(); + return renderer().absoluteBoundingBoxRect(/* useTransforms */ true, isInsideFixed); +} + +bool RenderLayer::isRubberBandInProgress() const +{ +#if ENABLE(RUBBER_BANDING) + if (!scrollsOverflow()) + return false; + + if (ScrollAnimator* scrollAnimator = existingScrollAnimator()) + return scrollAnimator->isRubberBandInProgress(); +#endif + + return false; +} + +bool RenderLayer::forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const +{ + return renderer().settings().forceUpdateScrollbarsOnMainThreadForPerformanceTesting(); } RenderLayer* RenderLayer::enclosingTransformedAncestor() const @@ -1412,14 +1535,13 @@ RenderLayer* RenderLayer::enclosingTransformedAncestor() const return curr; } -static inline const RenderLayer* compositingContainer(const RenderLayer* layer) +static inline const RenderLayer* compositingContainer(const RenderLayer& layer) { - return layer->isNormalFlowOnly() ? layer->parent() : layer->stackingContainer(); + return layer.isNormalFlowOnly() ? layer.parent() : layer.stackingContainer(); } inline bool RenderLayer::shouldRepaintAfterLayout() const { -#if USE(ACCELERATED_COMPOSITING) if (m_repaintStatus == NeedsNormalRepaint) return true; @@ -1427,16 +1549,11 @@ inline bool RenderLayer::shouldRepaintAfterLayout() const // layout, don't need to be repainted. They just need to be recomposited. ASSERT(m_repaintStatus == NeedsFullRepaintForPositionedMovementLayout); return !isComposited() || backing()->paintsIntoCompositedAncestor(); -#else - return true; -#endif } -#if USE(ACCELERATED_COMPOSITING) - -static bool compositedWithOwnBackingStore(const RenderLayer* layer) +bool compositedWithOwnBackingStore(const RenderLayer& layer) { - return layer->isComposited() && !layer->backing()->paintsIntoCompositedAncestor(); + return layer.isComposited() && !layer.backing()->paintsIntoCompositedAncestor(); } RenderLayer* RenderLayer::enclosingCompositingLayer(IncludeSelfOrNot includeSelf) const @@ -1444,31 +1561,27 @@ RenderLayer* RenderLayer::enclosingCompositingLayer(IncludeSelfOrNot includeSelf if (includeSelf == IncludeSelf && isComposited()) return const_cast<RenderLayer*>(this); - for (const RenderLayer* curr = compositingContainer(this); curr; curr = compositingContainer(curr)) { + for (const RenderLayer* curr = compositingContainer(*this); curr; curr = compositingContainer(*curr)) { if (curr->isComposited()) return const_cast<RenderLayer*>(curr); } - return 0; + return nullptr; } RenderLayer* RenderLayer::enclosingCompositingLayerForRepaint(IncludeSelfOrNot includeSelf) const { - if (includeSelf == IncludeSelf && isComposited() && !backing()->paintsIntoCompositedAncestor()) + if (includeSelf == IncludeSelf && compositedWithOwnBackingStore(*this)) return const_cast<RenderLayer*>(this); - for (const RenderLayer* curr = compositingContainer(this); curr; curr = compositingContainer(curr)) { - if (compositedWithOwnBackingStore(curr)) + for (const RenderLayer* curr = compositingContainer(*this); curr; curr = compositingContainer(*curr)) { + if (compositedWithOwnBackingStore(*curr)) return const_cast<RenderLayer*>(curr); } - return 0; + return nullptr; } -#endif - -#if ENABLE(CSS_FILTERS) - RenderLayer* RenderLayer::enclosingFilterLayer(IncludeSelfOrNot includeSelf) const { const RenderLayer* curr = (includeSelf == IncludeSelf) ? this : parent(); @@ -1477,24 +1590,19 @@ RenderLayer* RenderLayer::enclosingFilterLayer(IncludeSelfOrNot includeSelf) con return const_cast<RenderLayer*>(curr); } - return 0; + return nullptr; } RenderLayer* RenderLayer::enclosingFilterRepaintLayer() const { for (const RenderLayer* curr = this; curr; curr = curr->parent()) { - if ((curr != this && curr->requiresFullLayerImageForFilters()) -#if USE(ACCELERATED_COMPOSITING) - || compositedWithOwnBackingStore(curr) -#endif - || curr->isRootLayer() - ) + if ((curr != this && curr->requiresFullLayerImageForFilters()) || compositedWithOwnBackingStore(*curr) || curr->isRootLayer()) return const_cast<RenderLayer*>(curr); } - return 0; + return nullptr; } -void RenderLayer::setFilterBackendNeedsRepaintingInRect(const LayoutRect& rect, bool immediate) +void RenderLayer::setFilterBackendNeedsRepaintingInRect(const LayoutRect& rect) { if (rect.isEmpty()) return; @@ -1509,8 +1617,7 @@ void RenderLayer::setFilterBackendNeedsRepaintingInRect(const LayoutRect& rect, ASSERT(parentLayer); FloatQuad repaintQuad(rectForRepaint); LayoutRect parentLayerRect = renderer().localToContainerQuad(repaintQuad, &parentLayer->renderer()).enclosingBoundingBox(); - -#if USE(ACCELERATED_COMPOSITING) + if (parentLayer->isComposited()) { if (!parentLayer->backing()->paintsIntoWindow()) { parentLayer->setBackingNeedsRepaintInRect(parentLayerRect); @@ -1520,15 +1627,14 @@ void RenderLayer::setFilterBackendNeedsRepaintingInRect(const LayoutRect& rect, parentLayer = renderer().view().layer(); parentLayerRect = renderer().localToContainerQuad(repaintQuad, &parentLayer->renderer()).enclosingBoundingBox(); } -#endif if (parentLayer->paintsWithFilters()) { - parentLayer->setFilterBackendNeedsRepaintingInRect(parentLayerRect, immediate); + parentLayer->setFilterBackendNeedsRepaintingInRect(parentLayerRect); return; } if (parentLayer->isRootLayer()) { - toRenderView(parentLayer->renderer()).repaintViewRectangle(parentLayerRect, immediate); + downcast<RenderView>(parentLayer->renderer()).repaintViewRectangle(parentLayerRect); return; } @@ -1544,38 +1650,30 @@ bool RenderLayer::hasAncestorWithFilterOutsets() const return false; } -#endif - RenderLayer* RenderLayer::clippingRootForPainting() const { -#if USE(ACCELERATED_COMPOSITING) if (isComposited()) return const_cast<RenderLayer*>(this); -#endif const RenderLayer* current = this; while (current) { if (current->isRootLayer()) return const_cast<RenderLayer*>(current); - current = compositingContainer(current); + current = compositingContainer(*current); ASSERT(current); - if (current->transform() -#if USE(ACCELERATED_COMPOSITING) - || compositedWithOwnBackingStore(current) -#endif - ) + if (current->transform() || compositedWithOwnBackingStore(*current)) return const_cast<RenderLayer*>(current); } ASSERT_NOT_REACHED(); - return 0; + return nullptr; } LayoutPoint RenderLayer::absoluteToContents(const LayoutPoint& absolutePoint) const { // We don't use convertToLayerCoords because it doesn't know about transforms - return roundedLayoutPoint(renderer().absoluteToLocal(absolutePoint, UseTransforms)); + return LayoutPoint(renderer().absoluteToLocal(absolutePoint, UseTransforms)); } bool RenderLayer::cannotBlitToWindow() const @@ -1587,27 +1685,18 @@ bool RenderLayer::cannotBlitToWindow() const return parent()->cannotBlitToWindow(); } -bool RenderLayer::isTransparent() const -{ -#if ENABLE(SVG) - if (renderer().element() && renderer().element()->isSVGElement()) - return false; -#endif - return renderer().isTransparent() || renderer().hasMask(); -} - RenderLayer* RenderLayer::transparentPaintingAncestor() { if (isComposited()) - return 0; + return nullptr; for (RenderLayer* curr = parent(); curr; curr = curr->parent()) { if (curr->isComposited()) - return 0; + return nullptr; if (curr->isTransparent()) return curr; } - return 0; + return nullptr; } enum TransparencyClipBoxBehavior { @@ -1620,23 +1709,19 @@ enum TransparencyClipBoxMode { RootOfTransparencyClipBox }; -static LayoutRect transparencyClipBox(const RenderLayer*, const RenderLayer* rootLayer, TransparencyClipBoxBehavior, TransparencyClipBoxMode, PaintBehavior = 0); +static LayoutRect transparencyClipBox(const RenderLayer&, const RenderLayer* rootLayer, TransparencyClipBoxBehavior, TransparencyClipBoxMode, PaintBehavior = 0); -static void expandClipRectForRegionAndReflection(LayoutRect& clipRect, const RenderLayer* layer, const RenderLayer* rootLayer, +static void expandClipRectForRegionAndReflection(LayoutRect& clipRect, const RenderLayer& layer, const RenderLayer* rootLayer, TransparencyClipBoxBehavior transparencyBehavior, PaintBehavior paintBehavior) { // If this is a region, then the painting is actually done by its flow thread's layer. - if (layer->renderer().isRenderNamedFlowFragmentContainer()) { - RenderBlockFlow* regionContainer = toRenderBlockFlow(&layer->renderer()); - RenderNamedFlowFragment* region = regionContainer->renderNamedFlowFragment(); - RenderLayer* flowThreadLayer = region->flowThread()->layer(); - if (!layer->reflection() || layer->reflectionLayer() != flowThreadLayer) { - LayoutRect flowThreadClipRect = transparencyClipBox(flowThreadLayer, rootLayer, transparencyBehavior, DescendantsOfTransparencyClipBox, paintBehavior); - - LayoutPoint offsetFromRoot; - layer->convertToLayerCoords(flowThreadLayer, offsetFromRoot); - - LayoutSize moveOffset = (offsetFromRoot + regionContainer->contentBoxRect().location()) - region->flowThreadPortionRect().location(); + if (layer.renderer().isRenderNamedFlowFragmentContainer()) { + RenderBlockFlow& regionContainer = downcast<RenderBlockFlow>(layer.renderer()); + RenderNamedFlowFragment& region = *regionContainer.renderNamedFlowFragment(); + RenderLayer* flowThreadLayer = region.flowThread()->layer(); + if (flowThreadLayer && (!layer.reflection() || layer.reflectionLayer() != flowThreadLayer)) { + LayoutRect flowThreadClipRect = transparencyClipBox(*flowThreadLayer, rootLayer, transparencyBehavior, DescendantsOfTransparencyClipBox, paintBehavior); + LayoutSize moveOffset = (regionContainer.contentBoxRect().location() + layer.offsetFromAncestor(flowThreadLayer)) - region.flowThreadPortionRect().location(); flowThreadClipRect.move(moveOffset); clipRect.unite(flowThreadClipRect); @@ -1644,17 +1729,17 @@ static void expandClipRectForRegionAndReflection(LayoutRect& clipRect, const Ren } } -static void expandClipRectForDescendantsAndReflection(LayoutRect& clipRect, const RenderLayer* layer, const RenderLayer* rootLayer, +static void expandClipRectForDescendantsAndReflection(LayoutRect& clipRect, const RenderLayer& layer, const RenderLayer* rootLayer, TransparencyClipBoxBehavior transparencyBehavior, PaintBehavior paintBehavior) { // If we have a mask, then the clip is limited to the border box area (and there is // no need to examine child layers). - if (!layer->renderer().hasMask()) { + if (!layer.renderer().hasMask()) { // Note: we don't have to walk z-order lists since transparent elements always establish // a stacking container. This means we can just walk the layer tree directly. - for (RenderLayer* curr = layer->firstChild(); curr; curr = curr->nextSibling()) { - if (!layer->reflection() || layer->reflectionLayer() != curr) - clipRect.unite(transparencyClipBox(curr, rootLayer, transparencyBehavior, DescendantsOfTransparencyClipBox, paintBehavior)); + for (RenderLayer* curr = layer.firstChild(); curr; curr = curr->nextSibling()) { + if (!layer.reflection() || layer.reflectionLayer() != curr) + clipRect.unite(transparencyClipBox(*curr, rootLayer, transparencyBehavior, DescendantsOfTransparencyClipBox, paintBehavior)); } } @@ -1664,42 +1749,39 @@ static void expandClipRectForDescendantsAndReflection(LayoutRect& clipRect, cons // current transparencyClipBox to catch all child layers. // FIXME: Accelerated compositing will eventually want to do something smart here to avoid incorporating this // size into the parent layer. - if (layer->renderer().hasReflection()) { - LayoutPoint delta; - layer->convertToLayerCoords(rootLayer, delta); - clipRect.move(-delta.x(), -delta.y()); - clipRect.unite(layer->renderBox()->reflectedRect(clipRect)); - clipRect.moveBy(delta); + if (layer.renderer().hasReflection()) { + LayoutSize delta = layer.offsetFromAncestor(rootLayer); + clipRect.move(-delta); + clipRect.unite(layer.renderBox()->reflectedRect(clipRect)); + clipRect.move(delta); } } -static LayoutRect transparencyClipBox(const RenderLayer* layer, const RenderLayer* rootLayer, TransparencyClipBoxBehavior transparencyBehavior, +static LayoutRect transparencyClipBox(const RenderLayer& layer, const RenderLayer* rootLayer, TransparencyClipBoxBehavior transparencyBehavior, TransparencyClipBoxMode transparencyMode, PaintBehavior paintBehavior) { // FIXME: Although this function completely ignores CSS-imposed clipping, we did already intersect with the // paintDirtyRect, and that should cut down on the amount we have to paint. Still it // would be better to respect clips. - if (rootLayer != layer && ((transparencyBehavior == PaintingTransparencyClipBox && layer->paintsWithTransform(paintBehavior)) - || (transparencyBehavior == HitTestingTransparencyClipBox && layer->hasTransform()))) { + if (rootLayer != &layer && ((transparencyBehavior == PaintingTransparencyClipBox && layer.paintsWithTransform(paintBehavior)) + || (transparencyBehavior == HitTestingTransparencyClipBox && layer.hasTransform()))) { // The best we can do here is to use enclosed bounding boxes to establish a "fuzzy" enough clip to encompass // the transformed layer and all of its children. - const RenderLayer* paginationLayer = transparencyMode == DescendantsOfTransparencyClipBox ? layer->enclosingPaginationLayer() : 0; + RenderLayer::PaginationInclusionMode mode = transparencyBehavior == HitTestingTransparencyClipBox ? RenderLayer::IncludeCompositedPaginatedLayers : RenderLayer::ExcludeCompositedPaginatedLayers; + const RenderLayer* paginationLayer = transparencyMode == DescendantsOfTransparencyClipBox ? layer.enclosingPaginationLayer(mode) : nullptr; const RenderLayer* rootLayerForTransform = paginationLayer ? paginationLayer : rootLayer; - LayoutPoint delta; - layer->convertToLayerCoords(rootLayerForTransform, delta); + LayoutSize delta = layer.offsetFromAncestor(rootLayerForTransform); TransformationMatrix transform; - transform.translate(delta.x(), delta.y()); - transform = transform * *layer->transform(); + transform.translate(delta.width(), delta.height()); + transform.multiply(*layer.transform()); // We don't use fragment boxes when collecting a transformed layer's bounding box, since it always // paints unfragmented. - LayoutRect clipRect = layer->boundingBox(layer); - expandClipRectForDescendantsAndReflection(clipRect, layer, layer, transparencyBehavior, paintBehavior); -#if ENABLE(CSS_FILTERS) - layer->renderer().style().filterOutsets().expandRect(clipRect); -#endif + LayoutRect clipRect = layer.boundingBox(&layer); + expandClipRectForDescendantsAndReflection(clipRect, layer, &layer, transparencyBehavior, paintBehavior); + layer.renderer().style().filterOutsets().expandRect(clipRect); LayoutRect result = transform.mapRect(clipRect); if (!paginationLayer) return result; @@ -1707,46 +1789,58 @@ static LayoutRect transparencyClipBox(const RenderLayer* layer, const RenderLaye // We have to break up the transformed extent across our columns. // Split our box up into the actual fragment boxes that render in the columns/pages and unite those together to // get our true bounding box. - RenderFlowThread& enclosingFlowThread = toRenderFlowThread(paginationLayer->renderer()); + auto& enclosingFlowThread = downcast<RenderFlowThread>(paginationLayer->renderer()); result = enclosingFlowThread.fragmentsBoundingBox(result); - - LayoutPoint rootLayerDelta; - paginationLayer->convertToLayerCoords(rootLayer, rootLayerDelta); - result.moveBy(rootLayerDelta); + result.move(paginationLayer->offsetFromAncestor(rootLayer)); return result; } - LayoutRect clipRect = layer->boundingBox(rootLayer, RenderLayer::UseFragmentBoxes); + LayoutRect clipRect = layer.boundingBox(rootLayer, layer.offsetFromAncestor(rootLayer), transparencyBehavior == HitTestingTransparencyClipBox ? RenderLayer::UseFragmentBoxesIncludingCompositing : RenderLayer::UseFragmentBoxesExcludingCompositing); expandClipRectForDescendantsAndReflection(clipRect, layer, rootLayer, transparencyBehavior, paintBehavior); -#if ENABLE(CSS_FILTERS) - layer->renderer().style().filterOutsets().expandRect(clipRect); -#endif + layer.renderer().style().filterOutsets().expandRect(clipRect); + return clipRect; } -LayoutRect RenderLayer::paintingExtent(const RenderLayer* rootLayer, const LayoutRect& paintDirtyRect, PaintBehavior paintBehavior) +static LayoutRect paintingExtent(const RenderLayer& currentLayer, const RenderLayer* rootLayer, const LayoutRect& paintDirtyRect, PaintBehavior paintBehavior) { - return intersection(transparencyClipBox(this, rootLayer, PaintingTransparencyClipBox, RootOfTransparencyClipBox, paintBehavior), paintDirtyRect); + return intersection(transparencyClipBox(currentLayer, rootLayer, PaintingTransparencyClipBox, RootOfTransparencyClipBox, paintBehavior), paintDirtyRect); } -void RenderLayer::beginTransparencyLayers(GraphicsContext* context, const RenderLayer* rootLayer, const LayoutRect& paintDirtyRect, PaintBehavior paintBehavior) +void RenderLayer::beginTransparencyLayers(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, const LayoutRect& dirtyRect) { - if (context->paintingDisabled() || (paintsWithTransparency(paintBehavior) && m_usedTransparency)) + if (context.paintingDisabled() || (paintsWithTransparency(paintingInfo.paintBehavior) && m_usedTransparency)) return; - + RenderLayer* ancestor = transparentPaintingAncestor(); if (ancestor) - ancestor->beginTransparencyLayers(context, rootLayer, paintDirtyRect, paintBehavior); + ancestor->beginTransparencyLayers(context, paintingInfo, dirtyRect); - if (paintsWithTransparency(paintBehavior)) { + if (paintsWithTransparency(paintingInfo.paintBehavior)) { + ASSERT(isStackingContext()); m_usedTransparency = true; - context->save(); - LayoutRect clipRect = paintingExtent(rootLayer, paintDirtyRect, paintBehavior); - context->clip(clipRect); - context->beginTransparencyLayer(renderer().opacity()); + context.save(); + LayoutRect adjustedClipRect = paintingExtent(*this, paintingInfo.rootLayer, dirtyRect, paintingInfo.paintBehavior); + adjustedClipRect.move(paintingInfo.subpixelOffset); + FloatRect pixelSnappedClipRect = snapRectToDevicePixels(adjustedClipRect, renderer().document().deviceScaleFactor()); + context.clip(pixelSnappedClipRect); + +#if ENABLE(CSS_COMPOSITING) + bool usesCompositeOperation = hasBlendMode() && !(renderer().isSVGRoot() && parent() && parent()->isRootLayer()); + if (usesCompositeOperation) + context.setCompositeOperation(context.compositeOperation(), blendMode()); +#endif + + context.beginTransparencyLayer(renderer().opacity()); + +#if ENABLE(CSS_COMPOSITING) + if (usesCompositeOperation) + context.setCompositeOperation(context.compositeOperation(), BlendModeNormal); +#endif + #ifdef REVEAL_TRANSPARENCY_LAYERS - context->setFillColor(Color(0.0f, 0.0f, 0.5f, 0.2f), ColorSpaceDeviceRGB); - context->fillRect(clipRect); + context.setFillColor(Color(0.0f, 0.0f, 0.5f, 0.2f)); + context.fillRect(pixelSnappedClipRect); #endif } } @@ -1798,17 +1892,18 @@ void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild) if (child->renderer().isOutOfFlowPositioned() || child->hasOutOfFlowPositionedDescendant()) setAncestorChainHasOutOfFlowPositionedDescendant(child->renderer().containingBlock()); -#if USE(ACCELERATED_COMPOSITING) - compositor().layerWasAdded(*this, *child); +#if ENABLE(CSS_COMPOSITING) + if (child->hasBlendMode() || (child->hasNotIsolatedBlendingDescendants() && !child->isolatesBlending())) + updateAncestorChainHasBlendingDescendants(); #endif + + compositor().layerWasAdded(*this, *child); } RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) { -#if USE(ACCELERATED_COMPOSITING) - if (!renderer().documentBeingDestroyed()) + if (!renderer().renderTreeBeingDestroyed()) compositor().layerWillBeRemoved(*this, *oldChild); -#endif // remove the child if (oldChild->previousSibling()) @@ -1833,9 +1928,9 @@ RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) if (oldChild->renderer().isOutOfFlowPositioned() || oldChild->hasOutOfFlowPositionedDescendant()) dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus(); - oldChild->setPreviousSibling(0); - oldChild->setNextSibling(0); - oldChild->setParent(0); + oldChild->setPreviousSibling(nullptr); + oldChild->setNextSibling(nullptr); + oldChild->setParent(nullptr); oldChild->updateDescendantDependentFlags(); if (oldChild->m_hasVisibleContent || oldChild->m_hasVisibleDescendant) @@ -1844,6 +1939,11 @@ RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild) if (oldChild->isSelfPaintingLayer() || oldChild->hasSelfPaintingLayerDescendant()) dirtyAncestorChainHasSelfPaintingLayerDescendantStatus(); +#if ENABLE(CSS_COMPOSITING) + if (oldChild->hasBlendMode() || (oldChild->hasNotIsolatedBlendingDescendants() && !oldChild->isolatesBlending())) + dirtyAncestorChainHasBlendingDescendants(); +#endif + return oldChild; } @@ -1856,9 +1956,7 @@ void RenderLayer::removeOnlyThisLayer() // walks ignore this layer while we're removing it. renderer().setHasLayer(false); -#if USE(ACCELERATED_COMPOSITING) compositor().layerWillBeRemoved(*m_parent, *this); -#endif // Dirty the clip rects. clearClipRectsIncludingDescendants(); @@ -1879,7 +1977,7 @@ void RenderLayer::removeOnlyThisLayer() current->setRepaintStatus(NeedsFullRepaint); // updateLayerPositions depends on hasLayer() already being false for proper layout. ASSERT(!renderer().hasLayer()); - current->updateLayerPositions(0); // FIXME: use geometry map. + current->updateLayerPositions(); // FIXME: use geometry map. current = next; } @@ -1895,15 +1993,13 @@ void RenderLayer::insertOnlyThisLayer() // Find our enclosingLayer and add ourselves. RenderLayer* parentLayer = renderer().parent()->enclosingLayer(); ASSERT(parentLayer); - RenderLayer* beforeChild = parentLayer->reflectionLayer() != this ? renderer().parent()->findNextLayer(parentLayer, &renderer()) : 0; + RenderLayer* beforeChild = parentLayer->reflectionLayer() != this ? renderer().parent()->findNextLayer(parentLayer, &renderer()) : nullptr; parentLayer->addChild(this, beforeChild); } // Remove all descendant layers from the hierarchy and add them to the new position. - for (RenderObject* curr = renderer().firstChild(); curr; curr = curr->nextSibling()) { - if (curr->isRenderElement()) - toRenderElement(curr)->moveLayers(m_parent, this); - } + for (auto& child : childrenOfType<RenderElement>(renderer())) + child.moveLayers(m_parent, this); // Clear out all the clip rects. clearClipRectsIncludingDescendants(); @@ -1911,18 +2007,10 @@ void RenderLayer::insertOnlyThisLayer() void RenderLayer::convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntPoint& roundedLocation, ColumnOffsetAdjustment adjustForColumns) const { - LayoutPoint location = roundedLocation; - convertToLayerCoords(ancestorLayer, location, adjustForColumns); + LayoutPoint location = convertToLayerCoords(ancestorLayer, roundedLocation, adjustForColumns); roundedLocation = roundedIntPoint(location); } -void RenderLayer::convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntRect& roundedRect, ColumnOffsetAdjustment adjustForColumns) const -{ - LayoutRect rect = roundedRect; - convertToLayerCoords(ancestorLayer, rect, adjustForColumns); - roundedRect = pixelSnappedIntRect(rect); -} - // Returns the layer reached on the walk up towards the ancestor. static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer, LayoutPoint& location, RenderLayer::ColumnOffsetAdjustment adjustForColumns) { @@ -1932,9 +2020,9 @@ static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLay EPosition position = renderer.style().position(); // FIXME: Special casing RenderFlowThread so much for fixed positioning here is not great. - RenderFlowThread* fixedFlowThreadContainer = position == FixedPosition ? renderer.flowThreadContainingBlock() : 0; + RenderFlowThread* fixedFlowThreadContainer = position == FixedPosition ? renderer.flowThreadContainingBlock() : nullptr; if (fixedFlowThreadContainer && !fixedFlowThreadContainer->isOutOfFlowPositioned()) - fixedFlowThreadContainer = 0; + fixedFlowThreadContainer = nullptr; // FIXME: Positioning of out-of-flow(fixed, absolute) elements collected in a RenderFlowThread // may need to be revisited in a future patch. @@ -1954,15 +2042,15 @@ static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLay // element in render flow thread, we will hit the fixed positioned container before hitting the ancestor layer. if (position == FixedPosition && !fixedFlowThreadContainer) { // For a fixed layers, we need to walk up to the root to see if there's a fixed position container - // (e.g. a transformed layer). It's an error to call convertToLayerCoords() across a layer with a transform, + // (e.g. a transformed layer). It's an error to call offsetFromAncestor() across a layer with a transform, // so we should always find the ancestor at or before we find the fixed position container. - RenderLayer* fixedPositionContainerLayer = 0; + RenderLayer* fixedPositionContainerLayer = nullptr; bool foundAncestor = false; for (RenderLayer* currLayer = layer->parent(); currLayer; currLayer = currLayer->parent()) { if (currLayer == ancestorLayer) foundAncestor = true; - if (isFixedPositionedContainer(currLayer)) { + if (isContainerForPositioned(*currLayer, FixedPosition)) { fixedPositionContainerLayer = currLayer; ASSERT_UNUSED(foundAncestor, foundAncestor); break; @@ -1972,12 +2060,8 @@ static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLay ASSERT(fixedPositionContainerLayer); // We should have hit the RenderView's layer at least. if (fixedPositionContainerLayer != ancestorLayer) { - LayoutPoint fixedContainerCoords; - layer->convertToLayerCoords(fixedPositionContainerLayer, fixedContainerCoords); - - LayoutPoint ancestorCoords; - ancestorLayer->convertToLayerCoords(fixedPositionContainerLayer, ancestorCoords); - + LayoutSize fixedContainerCoords = layer->offsetFromAncestor(fixedPositionContainerLayer); + LayoutSize ancestorCoords = ancestorLayer->offsetFromAncestor(fixedPositionContainerLayer); location += (fixedContainerCoords - ancestorCoords); return ancestorLayer; } @@ -1986,13 +2070,13 @@ static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLay if (position == FixedPosition && fixedFlowThreadContainer) { ASSERT(ancestorLayer); if (ancestorLayer->isOutOfFlowRenderFlowThread()) { - location += toSize(layer->location()); + location += toLayoutSize(layer->location()); return ancestorLayer; } if (ancestorLayer == renderer.view().layer()) { // Add location in flow thread coordinates. - location += toSize(layer->location()); + location += toLayoutSize(layer->location()); // Add flow thread offset in view coordinates since the view may be scrolled. FloatPoint absPos = renderer.view().localToAbsolute(FloatPoint(), IsFixed); @@ -2003,14 +2087,14 @@ static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLay RenderLayer* parentLayer; if (position == AbsolutePosition || position == FixedPosition) { - // Do what enclosingPositionedAncestor() does, but check for ancestorLayer along the way. + // Do what enclosingAncestorForPosition() does, but check for ancestorLayer along the way. parentLayer = layer->parent(); bool foundAncestorFirst = false; while (parentLayer) { // RenderFlowThread is a positioned container, child of RenderView, positioned at (0,0). // This implies that, for out-of-flow positioned elements inside a RenderFlowThread, // we are bailing out before reaching root layer. - if (isPositionedContainer(parentLayer)) + if (isContainerForPositioned(*parentLayer, position)) break; if (parentLayer == ancestorLayer) { @@ -2028,15 +2112,10 @@ static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLay if (foundAncestorFirst) { // Found ancestorLayer before the abs. positioned container, so compute offset of both relative - // to enclosingPositionedAncestor and subtract. - RenderLayer* positionedAncestor = parentLayer->enclosingPositionedAncestor(); - - LayoutPoint thisCoords; - layer->convertToLayerCoords(positionedAncestor, thisCoords); - - LayoutPoint ancestorCoords; - ancestorLayer->convertToLayerCoords(positionedAncestor, ancestorCoords); - + // to enclosingAncestorForPosition and subtract. + RenderLayer* positionedAncestor = parentLayer->enclosingAncestorForPosition(position); + LayoutSize thisCoords = layer->offsetFromAncestor(positionedAncestor); + LayoutSize ancestorCoords = ancestorLayer->offsetFromAncestor(positionedAncestor); location += (thisCoords - ancestorCoords); return ancestorLayer; } @@ -2044,36 +2123,38 @@ static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLay parentLayer = layer->parent(); if (!parentLayer) - return 0; + return nullptr; - location += toSize(layer->location()); + location += toLayoutSize(layer->location()); if (adjustForColumns == RenderLayer::AdjustForColumns) { if (RenderLayer* parentLayer = layer->parent()) { - LayoutSize layerColumnOffset; - parentLayer->renderer().adjustForColumns(layerColumnOffset, location); - location += layerColumnOffset; + if (is<RenderMultiColumnFlowThread>(parentLayer->renderer())) { + RenderRegion* region = downcast<RenderMultiColumnFlowThread>(parentLayer->renderer()).physicalTranslationFromFlowToRegion(location); + if (region) + location.moveBy(region->topLeftLocation() + -parentLayer->renderBox()->topLeftLocation()); + } } } return parentLayer; } -void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutPoint& location, ColumnOffsetAdjustment adjustForColumns) const +LayoutPoint RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, const LayoutPoint& location, ColumnOffsetAdjustment adjustForColumns) const { if (ancestorLayer == this) - return; + return location; const RenderLayer* currLayer = this; + LayoutPoint locationInLayerCoords = location; while (currLayer && currLayer != ancestorLayer) - currLayer = accumulateOffsetTowardsAncestor(currLayer, ancestorLayer, location, adjustForColumns); + currLayer = accumulateOffsetTowardsAncestor(currLayer, ancestorLayer, locationInLayerCoords, adjustForColumns); + return locationInLayerCoords; } -void RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, LayoutRect& rect, ColumnOffsetAdjustment adjustForColumns) const +LayoutSize RenderLayer::offsetFromAncestor(const RenderLayer* ancestorLayer, ColumnOffsetAdjustment adjustForColumns) const { - LayoutPoint delta; - convertToLayerCoords(ancestorLayer, delta, adjustForColumns); - rect.move(-delta.x(), -delta.y()); + return toLayoutSize(convertToLayerCoords(ancestorLayer, LayoutPoint(), adjustForColumns)); } #if PLATFORM(IOS) @@ -2082,36 +2163,36 @@ bool RenderLayer::hasAcceleratedTouchScrolling() const #if ENABLE(ACCELERATED_OVERFLOW_SCROLLING) if (!scrollsOverflow()) return false; - - Settings* settings = renderer().document().settings(); - - // FIXME: settings should not be null at this point. If you find a reliable way to hit this assertion, please file a bug. - // See <rdar://problem/10266101>. - ASSERT(settings); - - return renderer().style().useTouchOverflowScrolling() || (settings && settings->alwaysUseAcceleratedOverflowScroll()); + return renderer().style().useTouchOverflowScrolling() || renderer().settings().alwaysUseAcceleratedOverflowScroll(); #else return false; #endif } -#if PLATFORM(IOS) && ENABLE(TOUCH_EVENTS) +bool RenderLayer::hasTouchScrollableOverflow() const +{ + return hasAcceleratedTouchScrolling() && (hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow()); +} + +#if ENABLE(TOUCH_EVENTS) bool RenderLayer::handleTouchEvent(const PlatformTouchEvent& touchEvent) { // If we have accelerated scrolling, let the scrolling be handled outside of WebKit. - if (hasAcceleratedTouchScrolling()) + if (hasTouchScrollableOverflow()) return false; return ScrollableArea::handleTouchEvent(touchEvent); } #endif +#endif // PLATFORM(IOS) +#if ENABLE(IOS_TOUCH_EVENTS) void RenderLayer::registerAsTouchEventListenerForScrolling() { if (!renderer().element() || m_registeredAsTouchEventListenerForScrolling) return; - renderer().document().addTouchEventListener(renderer().element()); + renderer().document().addTouchEventHandler(renderer().element()); m_registeredAsTouchEventListenerForScrolling = true; } @@ -2120,18 +2201,21 @@ void RenderLayer::unregisterAsTouchEventListenerForScrolling() if (!renderer().element() || !m_registeredAsTouchEventListenerForScrolling) return; - renderer().document().removeTouchEventListener(renderer().element()); + renderer().document().removeTouchEventHandler(renderer().element()); m_registeredAsTouchEventListenerForScrolling = false; } -#endif // PLATFORM(IOS) - -#if USE(ACCELERATED_COMPOSITING) +#endif // ENABLE(IOS_TOUCH_EVENTS) bool RenderLayer::usesCompositedScrolling() const { return isComposited() && backing()->scrollingLayer(); } +bool RenderLayer::usesAsyncScrolling() const +{ + return hasAcceleratedTouchScrolling() && usesCompositedScrolling(); +} + bool RenderLayer::needsCompositedScrolling() const { return m_needsCompositedScrolling; @@ -2155,12 +2239,6 @@ void RenderLayer::updateNeedsCompositedScrolling() // layers in WebCore, because we use UIKit to composite our scroll bars. m_needsCompositedScrolling = forceUseCompositedScrolling; #endif - // We gather a boolean value for use with Google UMA histograms to - // quantify the actual effects of a set of patches attempting to - // relax composited scrolling requirements, thereby increasing the - // number of composited overflow divs. - if (acceleratedCompositingForOverflowScrollEnabled()) - HistogramSupport::histogramEnumeration("Renderer.NeedsCompositedScrolling", m_needsCompositedScrolling, 2); } if (oldNeedsCompositedScrolling != m_needsCompositedScrolling) { @@ -2177,8 +2255,6 @@ void RenderLayer::updateNeedsCompositedScrolling() } } -#endif - static inline int adjustedScrollDelta(int beginningDelta) { // This implemention matches Firefox's. // http://mxr.mozilla.org/firefox/source/toolkit/content/widgets/browser.xml#856. @@ -2219,6 +2295,7 @@ void RenderLayer::panScrollFromPoint(const IntPoint& sourcePoint) scrollByRecursively(adjustedScrollDelta(delta), ScrollOffsetClamped); } +// FIXME: unify with the scrollRectToVisible() code below. void RenderLayer::scrollByRecursively(const IntSize& delta, ScrollOffsetClamping clamp, ScrollableArea** scrolledArea) { if (delta.isZero()) @@ -2229,7 +2306,7 @@ void RenderLayer::scrollByRecursively(const IntSize& delta, ScrollOffsetClamping restrictedByLineClamp = !renderer().parent()->style().lineClamp().isNone(); if (renderer().hasOverflowClip() && !restrictedByLineClamp) { - IntSize newScrollOffset = scrollOffset() + delta; + ScrollOffset newScrollOffset = scrollOffset() + delta; scrollToOffset(newScrollOffset, clamp); if (scrolledArea) *scrolledArea = this; @@ -2254,72 +2331,78 @@ void RenderLayer::scrollByRecursively(const IntSize& delta, ScrollOffsetClamping } } -IntSize RenderLayer::clampScrollOffset(const IntSize& scrollOffset) const +void RenderLayer::scrollToXPosition(int x, ScrollOffsetClamping clamp) { - RenderBox* box = renderBox(); - ASSERT(box); + ScrollPosition position(x, m_scrollPosition.y()); + scrollToOffset(scrollOffsetFromPosition(position), clamp); +} - int maxX = scrollWidth() - box->pixelSnappedClientWidth(); - int maxY = scrollHeight() - box->pixelSnappedClientHeight(); +void RenderLayer::scrollToYPosition(int y, ScrollOffsetClamping clamp) +{ + ScrollPosition position(m_scrollPosition.x(), y); + scrollToOffset(scrollOffsetFromPosition(position), clamp); +} - int x = std::max(std::min(scrollOffset.width(), maxX), 0); - int y = std::max(std::min(scrollOffset.height(), maxY), 0); - return IntSize(x, y); +ScrollOffset RenderLayer::clampScrollOffset(const ScrollOffset& scrollOffset) const +{ + return scrollOffset.constrainedBetween(IntPoint(), maximumScrollOffset()); } -void RenderLayer::scrollToOffset(const IntSize& scrollOffset, ScrollOffsetClamping clamp) +void RenderLayer::scrollToOffset(const ScrollOffset& scrollOffset, ScrollOffsetClamping clamp) { - IntSize newScrollOffset = clamp == ScrollOffsetClamped ? clampScrollOffset(scrollOffset) : scrollOffset; + ScrollOffset newScrollOffset = clamp == ScrollOffsetClamped ? clampScrollOffset(scrollOffset) : scrollOffset; if (newScrollOffset != this->scrollOffset()) - scrollToOffsetWithoutAnimation(IntPoint(newScrollOffset)); + scrollToOffsetWithoutAnimation(newScrollOffset); } -void RenderLayer::scrollTo(int x, int y) +void RenderLayer::scrollTo(const ScrollPosition& position) { RenderBox* box = renderBox(); if (!box) return; - if (box->style().overflowX() != OMARQUEE) { + LOG_WITH_STREAM(Scrolling, stream << "RenderLayer::scrollTo " << position); + + ScrollPosition newPosition = position; + if (!box->isHTMLMarquee()) { // Ensure that the dimensions will be computed if they need to be (for overflow:hidden blocks). if (m_scrollDimensionsDirty) computeScrollDimensions(); #if PLATFORM(IOS) if (adjustForIOSCaretWhenScrolling()) { - int maxX = scrollWidth() - box->clientWidth(); - if (x > maxX - caretWidth) { - x += caretWidth; - if (x <= caretWidth) - x = 0; - } else if (x < m_scrollOffset.width() - caretWidth) - x -= caretWidth; + // FIXME: It's not clear what this code is trying to do. Behavior seems reasonable with it removed. + int maxOffset = scrollWidth() - roundToInt(box->clientWidth()); + ScrollOffset newOffset = scrollOffsetFromPosition(newPosition); + int scrollXOffset = newOffset.x(); + if (scrollXOffset > maxOffset - caretWidth) { + scrollXOffset += caretWidth; + if (scrollXOffset <= caretWidth) + scrollXOffset = 0; + } else if (scrollXOffset < m_scrollPosition.x() - caretWidth) + scrollXOffset -= caretWidth; + + newOffset.setX(scrollXOffset); + newPosition = scrollPositionFromOffset(newOffset); } #endif } - // FIXME: Eventually, we will want to perform a blit. For now never - // blit, since the check for blitting is going to be very - // complicated (since it will involve testing whether our layer - // is either occluded by another layer or clipped by an enclosing - // layer or contains fixed backgrounds, etc.). - IntSize newScrollOffset = IntSize(x - scrollOrigin().x(), y - scrollOrigin().y()); - if (m_scrollOffset == newScrollOffset) { + if (m_scrollPosition == newPosition) { #if PLATFORM(IOS) if (m_requiresScrollBoundsOriginUpdate) updateCompositingLayersAfterScroll(); #endif return; } - m_scrollOffset = newScrollOffset; - - InspectorInstrumentation::willScrollLayer(&renderer().frame()); + + ScrollPosition oldPosition = IntPoint(m_scrollPosition); + m_scrollPosition = newPosition; RenderView& view = renderer().view(); // Update the positions of our child layers (if needed as only fixed layers should be impacted by a scroll). // We don't update compositing layers, because we need to do a deep update from the compositing ancestor. - bool inLayout = view.frameView().isInLayout(); - if (!inLayout) { + if (!view.frameView().isInRenderTreeLayout()) { // If we're in the middle of layout, we'll just update layers once layout has finished. updateLayerPositionsAfterOverflowScroll(); // Update regions, scrolling may change the clip of a particular region. @@ -2339,154 +2422,171 @@ void RenderLayer::scrollTo(int x, int y) #if PLATFORM(IOS) && ENABLE(TOUCH_EVENTS) renderer().document().dirtyTouchEventRects(); #endif + DebugPageOverlays::didLayout(renderer().frame()); } Frame& frame = renderer().frame(); RenderLayerModelObject* repaintContainer = renderer().containerForRepaint(); // The caret rect needs to be invalidated after scrolling frame.selection().setCaretRectNeedsUpdate(); + + LayoutRect rectForRepaint = m_hasComputedRepaintRect ? m_repaintRect : renderer().clippedOverflowRectForRepaint(repaintContainer); - FloatQuad quadForFakeMouseMoveEvent = FloatQuad(m_repaintRect); + FloatQuad quadForFakeMouseMoveEvent = FloatQuad(rectForRepaint); if (repaintContainer) quadForFakeMouseMoveEvent = repaintContainer->localToAbsoluteQuad(quadForFakeMouseMoveEvent); frame.eventHandler().dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseMoveEvent); bool requiresRepaint = true; - -#if USE(ACCELERATED_COMPOSITING) if (compositor().inCompositingMode() && usesCompositedScrolling()) requiresRepaint = false; -#endif // Just schedule a full repaint of our object. -#if PLATFORM(IOS) - if (!hasAcceleratedTouchScrolling()) -#else if (requiresRepaint) -#endif - renderer().repaintUsingContainer(repaintContainer, pixelSnappedIntRect(m_repaintRect)); + renderer().repaintUsingContainer(repaintContainer, rectForRepaint); - // Schedule the scroll DOM event. - if (Element* element = renderer().element()) + // Schedule the scroll and scroll-related DOM events. + if (Element* element = renderer().element()) { element->document().eventQueue().enqueueOrDispatchScrollEvent(*element); + element->document().sendWillRevealEdgeEventsIfNeeded(oldPosition, newPosition, visibleContentRect(), contentsSize(), element); + } - InspectorInstrumentation::didScrollLayer(&frame); if (scrollsOverflow()) - frame.loader().client().didChangeScrollOffset(); + view.frameView().didChangeScrollOffset(); + + view.frameView().viewportContentsChanged(); } -static inline bool frameElementAndViewPermitScroll(HTMLFrameElementBase* frameElementBase, FrameView* frameView) +static inline bool frameElementAndViewPermitScroll(HTMLFrameElementBase* frameElementBase, FrameView& frameView) { // If scrollbars aren't explicitly forbidden, permit scrolling. if (frameElementBase && frameElementBase->scrollingMode() != ScrollbarAlwaysOff) return true; // If scrollbars are forbidden, user initiated scrolls should obviously be ignored. - if (frameView->wasScrolledByUser()) + if (frameView.wasScrolledByUser()) return false; // Forbid autoscrolls when scrollbars are off, but permits other programmatic scrolls, // like navigation to an anchor. - return !frameView->frame().eventHandler().autoscrollInProgress(); + return !frameView.frame().eventHandler().autoscrollInProgress(); +} + +bool RenderLayer::allowsCurrentScroll() const +{ + if (!renderer().hasOverflowClip()) + return false; + + // Don't scroll to reveal an overflow layer that is restricted by the -webkit-line-clamp property. + // FIXME: Is this still needed? It used to be relevant for Safari RSS. + if (renderer().parent() && !renderer().parent()->style().lineClamp().isNone()) + return false; + + RenderBox* box = renderBox(); + ASSERT(box); // Only boxes can have overflowClip set. + + if (renderer().frame().eventHandler().autoscrollInProgress()) { + // The "programmatically" here is misleading; this asks whether the box has scrollable overflow, + // or is a special case like a form control. + return box->canBeProgramaticallyScrolled(); + } + + // Programmatic scrolls can scroll overflow:hidden. + return box->hasHorizontalOverflow() || box->hasVerticalOverflow(); } -void RenderLayer::scrollRectToVisible(const LayoutRect& rect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) +void RenderLayer::scrollRectToVisible(SelectionRevealMode revealMode, const LayoutRect& absoluteRect, bool insideFixed, const ScrollAlignment& alignX, const ScrollAlignment& alignY) { - RenderLayer* parentLayer = 0; - LayoutRect newRect = rect; + LOG_WITH_STREAM(Scrolling, stream << "Layer " << this << " scrollRectToVisible " << absoluteRect); + + RenderLayer* parentLayer = nullptr; + LayoutRect newRect = absoluteRect; // We may end up propagating a scroll event. It is important that we suspend events until // the end of the function since they could delete the layer or the layer's renderer(). FrameView& frameView = renderer().view().frameView(); - bool restrictedByLineClamp = false; - if (renderer().parent()) { + if (renderer().parent()) parentLayer = renderer().parent()->enclosingLayer(); - restrictedByLineClamp = !renderer().parent()->style().lineClamp().isNone(); - } - if (renderer().hasOverflowClip() && !restrictedByLineClamp) { + if (allowsCurrentScroll()) { // Don't scroll to reveal an overflow layer that is restricted by the -webkit-line-clamp property. // This will prevent us from revealing text hidden by the slider in Safari RSS. RenderBox* box = renderBox(); ASSERT(box); - LayoutRect localExposeRect(box->absoluteToLocalQuad(FloatQuad(FloatRect(rect)), UseTransforms).boundingBox()); + LayoutRect localExposeRect(box->absoluteToLocalQuad(FloatQuad(FloatRect(absoluteRect))).boundingBox()); LayoutRect layerBounds(0, 0, box->clientWidth(), box->clientHeight()); - LayoutRect r = getRectToExpose(layerBounds, layerBounds, localExposeRect, alignX, alignY); + LayoutRect revealRect = getRectToExpose(layerBounds, localExposeRect, insideFixed, alignX, alignY); - IntSize clampedScrollOffset = clampScrollOffset(scrollOffset() + toIntSize(roundedIntRect(r).location())); + ScrollOffset clampedScrollOffset = clampScrollOffset(scrollOffset() + toIntSize(roundedIntRect(revealRect).location())); if (clampedScrollOffset != scrollOffset()) { - IntSize oldScrollOffset = scrollOffset(); + ScrollOffset oldScrollOffset = scrollOffset(); scrollToOffset(clampedScrollOffset); IntSize scrollOffsetDifference = scrollOffset() - oldScrollOffset; localExposeRect.move(-scrollOffsetDifference); newRect = LayoutRect(box->localToAbsoluteQuad(FloatQuad(FloatRect(localExposeRect)), UseTransforms).boundingBox()); } - } else if (!parentLayer && renderer().isBox() && renderBox()->canBeProgramaticallyScrolled()) { - Element* ownerElement = renderer().document().ownerElement(); + } else if (!parentLayer && renderer().isRenderView()) { + HTMLFrameOwnerElement* ownerElement = renderer().document().ownerElement(); if (ownerElement && ownerElement->renderer()) { - HTMLFrameElementBase* frameElementBase = 0; + HTMLFrameElementBase* frameElementBase = nullptr; - if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) - frameElementBase = toHTMLFrameElementBase(ownerElement); + if (is<HTMLFrameElementBase>(*ownerElement)) + frameElementBase = downcast<HTMLFrameElementBase>(ownerElement); + + if (frameElementAndViewPermitScroll(frameElementBase, frameView)) { + // If this assertion fires we need to protect the ownerElement from being destroyed. + NoEventDispatchAssertion assertNoEventDispatch; - if (frameElementAndViewPermitScroll(frameElementBase, &frameView)) { LayoutRect viewRect = frameView.visibleContentRect(LegacyIOSDocumentVisibleRect); - LayoutRect exposeRect = getRectToExpose(viewRect, viewRect, rect, alignX, alignY); + LayoutRect exposeRect = getRectToExpose(viewRect, absoluteRect, insideFixed, alignX, alignY); - int xOffset = roundToInt(exposeRect.x()); - int yOffset = roundToInt(exposeRect.y()); + IntPoint scrollOffset(roundedIntPoint(exposeRect.location())); // Adjust offsets if they're outside of the allowable range. - xOffset = std::max(0, std::min(frameView.contentsWidth(), xOffset)); - yOffset = std::max(0, std::min(frameView.contentsHeight(), yOffset)); + scrollOffset = scrollOffset.constrainedBetween(IntPoint(), IntPoint(frameView.contentsSize())); + frameView.setScrollPosition(scrollOffset); - frameView.setScrollPosition(IntPoint(xOffset, yOffset)); if (frameView.safeToPropagateScrollToParent()) { parentLayer = ownerElement->renderer()->enclosingLayer(); - // FIXME: This doesn't correctly convert the rect to - // absolute coordinates in the parent. - newRect.setX(rect.x() - frameView.scrollX() + frameView.x()); - newRect.setY(rect.y() - frameView.scrollY() + frameView.y()); + // Convert the rect into the coordinate space of the parent frame's document. + newRect = frameView.contentsToContainingViewContents(enclosingIntRect(newRect)); + insideFixed = false; // FIXME: ideally need to determine if this <iframe> is inside position:fixed. } else - parentLayer = 0; + parentLayer = nullptr; } } else { + if (revealMode == SelectionRevealMode::RevealUpToMainFrame && frameView.frame().isMainFrame()) + return; + #if !PLATFORM(IOS) LayoutRect viewRect = frameView.visibleContentRect(); - LayoutRect visibleRectRelativeToDocument = viewRect; - IntSize scrollOffsetRelativeToDocument = frameView.scrollOffsetRelativeToDocument(); - visibleRectRelativeToDocument.setLocation(IntPoint(scrollOffsetRelativeToDocument.width(), scrollOffsetRelativeToDocument.height())); #else - // FIXME: is this equivalent to the code above? - LayoutRect viewRect = frameView.actualVisibleContentRect(); - LayoutRect visibleRectRelativeToDocument = viewRect; + LayoutRect viewRect = frameView.unobscuredContentRect(); #endif + // Move the target rect into "scrollView contents" coordinates. + LayoutRect targetRect = absoluteRect; + targetRect.move(0, frameView.headerHeight()); - LayoutRect r = getRectToExpose(viewRect, visibleRectRelativeToDocument, rect, alignX, alignY); - - frameView.setScrollPosition(roundedIntPoint(r.location())); + LayoutRect revealRect = getRectToExpose(viewRect, targetRect, insideFixed, alignX, alignY); + + frameView.setScrollPosition(roundedIntPoint(revealRect.location())); // This is the outermost view of a web page, so after scrolling this view we // scroll its container by calling Page::scrollRectIntoView. // This only has an effect on the Mac platform in applications // that put web views into scrolling containers, such as Mac OS X Mail. // The canAutoscroll function in EventHandler also knows about this. - if (Page* page = frameView.frame().page()) - page->chrome().scrollRectIntoView(pixelSnappedIntRect(rect)); + page().chrome().scrollRectIntoView(snappedIntRect(absoluteRect)); } } - if (renderer().frame().eventHandler().autoscrollInProgress()) - parentLayer = enclosingScrollableLayer(); - if (parentLayer) - parentLayer->scrollRectToVisible(newRect, alignX, alignY); + parentLayer->scrollRectToVisible(revealMode, newRect, insideFixed, alignX, alignY); } void RenderLayer::updateCompositingLayersAfterScroll() { -#if USE(ACCELERATED_COMPOSITING) if (compositor().inCompositingMode()) { // Our stacking container is guaranteed to contain all of our descendants that may need // repositioning, so update compositing layers from there. @@ -2497,13 +2597,42 @@ void RenderLayer::updateCompositingLayersAfterScroll() compositor().updateCompositingLayers(CompositingUpdateOnScroll, compositingAncestor); } } -#endif } -LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const LayoutRect &visibleRectRelativeToDocument, const LayoutRect &exposeRect, const ScrollAlignment& alignX, const ScrollAlignment& alignY) +LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const LayoutRect &exposeRect, bool insideFixed, const ScrollAlignment& alignX, const ScrollAlignment& alignY) const { + FrameView& frameView = renderer().view().frameView(); + if (renderer().isRenderView() && insideFixed) { + // If the element is inside position:fixed and we're not scaled, no amount of scrolling is going to move things around. + if (frameView.frameScaleFactor() == 1) + return visibleRect; + + if (renderer().settings().visualViewportEnabled()) { + // exposeRect is in absolute coords, affected by page scale. Unscale it. + LayoutRect unscaledExposeRect = exposeRect; + unscaledExposeRect.scale(1 / frameView.frameScaleFactor()); + unscaledExposeRect.move(0, -frameView.headerHeight()); + + // These are both in unscaled coordinates. + LayoutRect layoutViewport = frameView.layoutViewportRect(); + LayoutRect visualViewport = frameView.visualViewportRect(); + + // The rect to expose may be partially offscreen, which we can't do anything about with position:fixed. + unscaledExposeRect.intersect(layoutViewport); + // Make sure it's not larger than the visual viewport; if so, we'll just move to the top left. + unscaledExposeRect.setSize(unscaledExposeRect.size().shrunkTo(visualViewport.size())); + + // Compute how much we have to move the visualViewport to reveal the part of the layoutViewport that contains exposeRect. + LayoutRect requiredVisualViewport = getRectToExpose(visualViewport, unscaledExposeRect, false, alignX, alignY); + // Scale it back up. + requiredVisualViewport.scale(frameView.frameScaleFactor()); + requiredVisualViewport.move(0, frameView.headerHeight()); + return requiredVisualViewport; + } + } + // Determine the appropriate X behavior. - ScrollBehavior scrollX; + ScrollAlignment::Behavior scrollX; LayoutRect exposeRectX(exposeRect.x(), visibleRect.y(), exposeRect.width(), visibleRect.height()); LayoutUnit intersectWidth = intersection(visibleRect, exposeRectX).width(); if (intersectWidth == exposeRect.width() || intersectWidth >= MIN_INTERSECT_FOR_REVEAL) @@ -2514,8 +2643,8 @@ LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const Lay else if (intersectWidth == visibleRect.width()) { // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. scrollX = ScrollAlignment::getVisibleBehavior(alignX); - if (scrollX == alignCenter) - scrollX = noScroll; + if (scrollX == ScrollAlignment::Behavior::AlignCenter) + scrollX = ScrollAlignment::Behavior::NoScroll; } else if (intersectWidth > 0) // If the rectangle is partially visible, but not above the minimum threshold, use the specified partial behavior scrollX = ScrollAlignment::getPartialBehavior(alignX); @@ -2523,32 +2652,32 @@ LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const Lay scrollX = ScrollAlignment::getHiddenBehavior(alignX); // If we're trying to align to the closest edge, and the exposeRect is further right // than the visibleRect, and not bigger than the visible area, then align with the right. - if (scrollX == alignToClosestEdge && exposeRect.maxX() > visibleRect.maxX() && exposeRect.width() < visibleRect.width()) - scrollX = alignRight; + if (scrollX == ScrollAlignment::Behavior::AlignToClosestEdge && exposeRect.maxX() > visibleRect.maxX() && exposeRect.width() < visibleRect.width()) + scrollX = ScrollAlignment::Behavior::AlignRight; // Given the X behavior, compute the X coordinate. LayoutUnit x; - if (scrollX == noScroll) + if (scrollX == ScrollAlignment::Behavior::NoScroll) x = visibleRect.x(); - else if (scrollX == alignRight) + else if (scrollX == ScrollAlignment::Behavior::AlignRight) x = exposeRect.maxX() - visibleRect.width(); - else if (scrollX == alignCenter) + else if (scrollX == ScrollAlignment::Behavior::AlignCenter) x = exposeRect.x() + (exposeRect.width() - visibleRect.width()) / 2; else x = exposeRect.x(); // Determine the appropriate Y behavior. - ScrollBehavior scrollY; + ScrollAlignment::Behavior scrollY; LayoutRect exposeRectY(visibleRect.x(), exposeRect.y(), visibleRect.width(), exposeRect.height()); - LayoutUnit intersectHeight = intersection(visibleRectRelativeToDocument, exposeRectY).height(); + LayoutUnit intersectHeight = intersection(visibleRect, exposeRectY).height(); if (intersectHeight == exposeRect.height()) // If the rectangle is fully visible, use the specified visible behavior. scrollY = ScrollAlignment::getVisibleBehavior(alignY); else if (intersectHeight == visibleRect.height()) { // If the rect is bigger than the visible area, don't bother trying to center. Other alignments will work. scrollY = ScrollAlignment::getVisibleBehavior(alignY); - if (scrollY == alignCenter) - scrollY = noScroll; + if (scrollY == ScrollAlignment::Behavior::AlignCenter) + scrollY = ScrollAlignment::Behavior::NoScroll; } else if (intersectHeight > 0) // If the rectangle is partially visible, use the specified partial behavior scrollY = ScrollAlignment::getPartialBehavior(alignY); @@ -2556,16 +2685,16 @@ LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const Lay scrollY = ScrollAlignment::getHiddenBehavior(alignY); // If we're trying to align to the closest edge, and the exposeRect is further down // than the visibleRect, and not bigger than the visible area, then align with the bottom. - if (scrollY == alignToClosestEdge && exposeRect.maxY() > visibleRect.maxY() && exposeRect.height() < visibleRect.height()) - scrollY = alignBottom; + if (scrollY == ScrollAlignment::Behavior::AlignToClosestEdge && exposeRect.maxY() > visibleRect.maxY() && exposeRect.height() < visibleRect.height()) + scrollY = ScrollAlignment::Behavior::AlignBottom; // Given the Y behavior, compute the Y coordinate. LayoutUnit y; - if (scrollY == noScroll) + if (scrollY == ScrollAlignment::Behavior::NoScroll) y = visibleRect.y(); - else if (scrollY == alignBottom) + else if (scrollY == ScrollAlignment::Behavior::AlignBottom) y = exposeRect.maxY() - visibleRect.height(); - else if (scrollY == alignCenter) + else if (scrollY == ScrollAlignment::Behavior::AlignCenter) y = exposeRect.y() + (exposeRect.height() - visibleRect.height()) / 2; else y = exposeRect.y(); @@ -2576,7 +2705,7 @@ LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const Lay void RenderLayer::autoscroll(const IntPoint& position) { IntPoint currentDocumentPosition = renderer().view().frameView().windowToContents(position); - scrollRectToVisible(LayoutRect(currentDocumentPosition, LayoutSize(1, 1)), ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); + scrollRectToVisible(SelectionRevealMode::Reveal, LayoutRect(currentDocumentPosition, LayoutSize(1, 1)), false, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded); } bool RenderLayer::canResize() const @@ -2596,7 +2725,7 @@ void RenderLayer::resize(const PlatformMouseEvent& evt, const LayoutSize& oldOff // FIXME: The only case where renderer->element()->renderer() != renderer is with continuations. Do they matter here? // If they do it would still be better to deal with them explicitly. Element* element = renderer().element(); - RenderBox* renderer = toRenderBox(element->renderer()); + auto* renderer = downcast<RenderBox>(element->renderer()); Document& document = element->document(); if (!document.frame()->eventHandler().mousePressed()) @@ -2613,35 +2742,35 @@ void RenderLayer::resize(const PlatformMouseEvent& evt, const LayoutSize& oldOff element->setMinimumSizeForResizing(minimumSize); LayoutSize adjustedOldOffset = LayoutSize(oldOffset.width() / zoomFactor, oldOffset.height() / zoomFactor); - if (renderer->style().shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) { + if (shouldPlaceBlockDirectionScrollbarOnLeft()) { newOffset.setWidth(-newOffset.width()); adjustedOldOffset.setWidth(-adjustedOldOffset.width()); } LayoutSize difference = (currentSize + newOffset - adjustedOldOffset).expandedTo(minimumSize) - currentSize; - StyledElement* styledElement = toStyledElement(element); + StyledElement* styledElement = downcast<StyledElement>(element); bool isBoxSizingBorder = renderer->style().boxSizing() == BORDER_BOX; EResize resize = renderer->style().resize(); if (resize != RESIZE_VERTICAL && difference.width()) { - if (element->isFormControlElement()) { + if (is<HTMLFormControlElement>(*element)) { // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>). styledElement->setInlineStyleProperty(CSSPropertyMarginLeft, renderer->marginLeft() / zoomFactor, CSSPrimitiveValue::CSS_PX); styledElement->setInlineStyleProperty(CSSPropertyMarginRight, renderer->marginRight() / zoomFactor, CSSPrimitiveValue::CSS_PX); } - LayoutUnit baseWidth = renderer->width() - (isBoxSizingBorder ? LayoutUnit() : renderer->borderAndPaddingWidth()); + LayoutUnit baseWidth = renderer->width() - (isBoxSizingBorder ? LayoutUnit() : renderer->horizontalBorderAndPaddingExtent()); baseWidth = baseWidth / zoomFactor; styledElement->setInlineStyleProperty(CSSPropertyWidth, roundToInt(baseWidth + difference.width()), CSSPrimitiveValue::CSS_PX); } if (resize != RESIZE_HORIZONTAL && difference.height()) { - if (element->isFormControlElement()) { + if (is<HTMLFormControlElement>(*element)) { // Make implicit margins from the theme explicit (see <http://bugs.webkit.org/show_bug.cgi?id=9547>). styledElement->setInlineStyleProperty(CSSPropertyMarginTop, renderer->marginTop() / zoomFactor, CSSPrimitiveValue::CSS_PX); styledElement->setInlineStyleProperty(CSSPropertyMarginBottom, renderer->marginBottom() / zoomFactor, CSSPrimitiveValue::CSS_PX); } - LayoutUnit baseHeight = renderer->height() - (isBoxSizingBorder ? LayoutUnit() : renderer->borderAndPaddingHeight()); + LayoutUnit baseHeight = renderer->height() - (isBoxSizingBorder ? LayoutUnit() : renderer->verticalBorderAndPaddingExtent()); baseHeight = baseHeight / zoomFactor; styledElement->setInlineStyleProperty(CSSPropertyHeight, roundToInt(baseHeight + difference.height()), CSSPrimitiveValue::CSS_PX); } @@ -2657,117 +2786,113 @@ int RenderLayer::scrollSize(ScrollbarOrientation orientation) const return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0; } -void RenderLayer::setScrollOffset(const IntPoint& offset) +void RenderLayer::setScrollOffset(const ScrollOffset& offset) { - scrollTo(offset.x(), offset.y()); + scrollTo(scrollPositionFromOffset(offset)); } -int RenderLayer::scrollPosition(Scrollbar* scrollbar) const +int RenderLayer::scrollOffset(ScrollbarOrientation orientation) const { - if (scrollbar->orientation() == HorizontalScrollbar) - return scrollXOffset(); - if (scrollbar->orientation() == VerticalScrollbar) - return scrollYOffset(); - return 0; -} + if (orientation == HorizontalScrollbar) + return scrollOffset().x(); -IntPoint RenderLayer::scrollPosition() const -{ - return IntPoint(m_scrollOffset); -} + if (orientation == VerticalScrollbar) + return scrollOffset().y(); -IntPoint RenderLayer::minimumScrollPosition() const -{ - return -scrollOrigin(); -} - -IntPoint RenderLayer::maximumScrollPosition() const -{ - // FIXME: m_scrollSize may not be up-to-date if m_scrollDimensionsDirty is true. - return -scrollOrigin() + roundedIntSize(m_scrollSize) - visibleContentRectIncludingScrollbars(ContentsVisibleRect).size(); + return 0; } IntRect RenderLayer::visibleContentRectInternal(VisibleContentRectIncludesScrollbars scrollbarInclusion, VisibleContentRectBehavior) const { - int verticalScrollbarWidth = 0; - int horizontalScrollbarHeight = 0; - if (scrollbarInclusion == IncludeScrollbars) { - verticalScrollbarWidth = (verticalScrollbar() && !verticalScrollbar()->isOverlayScrollbar()) ? verticalScrollbar()->width() : 0; - horizontalScrollbarHeight = (horizontalScrollbar() && !horizontalScrollbar()->isOverlayScrollbar()) ? horizontalScrollbar()->height() : 0; - -#if PLATFORM(IOS) - if (hasAcceleratedTouchScrolling()) { - verticalScrollbarWidth = 0; - horizontalScrollbarHeight = 0; - } -#endif - } + IntSize scrollbarSpace; + if (showsOverflowControls() && scrollbarInclusion == IncludeScrollbars) + scrollbarSpace = scrollbarIntrusion(); - return IntRect(IntPoint(scrollXOffset(), scrollYOffset()), - IntSize(std::max(0, m_layerSize.width() - verticalScrollbarWidth), - std::max(0, m_layerSize.height() - horizontalScrollbarHeight))); + // FIXME: This seems wrong: m_layerSize includes borders. Can we just use the ScrollableArea implementation? + return IntRect(scrollPosition(), IntSize(std::max(0, m_layerSize.width() - scrollbarSpace.width()), std::max(0, m_layerSize.height() - scrollbarSpace.height()))); } IntSize RenderLayer::overhangAmount() const { +#if ENABLE(RUBBER_BANDING) + if (!renderer().settings().rubberBandingForSubScrollableRegionsEnabled()) + return IntSize(); + + IntSize stretch; + + // FIXME: use maximumScrollOffset(), or just move this to ScrollableArea. + ScrollOffset scrollOffset = scrollOffsetFromPosition(scrollPosition()); + if (scrollOffset.y() < 0) + stretch.setHeight(scrollOffset.y()); + else if (scrollableContentsSize().height() && scrollOffset.y() > scrollableContentsSize().height() - visibleHeight()) + stretch.setHeight(scrollOffset.y() - (scrollableContentsSize().height() - visibleHeight())); + + if (scrollOffset.x() < 0) + stretch.setWidth(scrollOffset.x()); + else if (scrollableContentsSize().width() && scrollOffset.x() > scrollableContentsSize().width() - visibleWidth()) + stretch.setWidth(scrollOffset.x() - (scrollableContentsSize().width() - visibleWidth())); + + return stretch; +#else return IntSize(); +#endif } bool RenderLayer::isActive() const { - Page* page = renderer().frame().page(); - return page && page->focusController().isActive(); + return page().focusController().isActive(); } -static int cornerStart(const RenderLayer* layer, int minX, int maxX, int thickness) +static int cornerStart(const RenderLayer& layer, int minX, int maxX, int thickness) { - if (layer->renderer().style().shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) - return minX + layer->renderer().style().borderLeftWidth(); - return maxX - thickness - layer->renderer().style().borderRightWidth(); + if (layer.shouldPlaceBlockDirectionScrollbarOnLeft()) + return minX + layer.renderer().style().borderLeftWidth(); + return maxX - thickness - layer.renderer().style().borderRightWidth(); } -static LayoutRect cornerRect(const RenderLayer* layer, const LayoutRect& bounds) +static LayoutRect cornerRect(const RenderLayer& layer, const LayoutRect& bounds) { int horizontalThickness; int verticalThickness; - if (!layer->verticalScrollbar() && !layer->horizontalScrollbar()) { + if (!layer.verticalScrollbar() && !layer.horizontalScrollbar()) { // FIXME: This isn't right. We need to know the thickness of custom scrollbars // even when they don't exist in order to set the resizer square size properly. - horizontalThickness = ScrollbarTheme::theme()->scrollbarThickness(); + horizontalThickness = ScrollbarTheme::theme().scrollbarThickness(); verticalThickness = horizontalThickness; - } else if (layer->verticalScrollbar() && !layer->horizontalScrollbar()) { - horizontalThickness = layer->verticalScrollbar()->width(); + } else if (layer.verticalScrollbar() && !layer.horizontalScrollbar()) { + horizontalThickness = layer.verticalScrollbar()->width(); verticalThickness = horizontalThickness; - } else if (layer->horizontalScrollbar() && !layer->verticalScrollbar()) { - verticalThickness = layer->horizontalScrollbar()->height(); + } else if (layer.horizontalScrollbar() && !layer.verticalScrollbar()) { + verticalThickness = layer.horizontalScrollbar()->height(); horizontalThickness = verticalThickness; } else { - horizontalThickness = layer->verticalScrollbar()->width(); - verticalThickness = layer->horizontalScrollbar()->height(); + horizontalThickness = layer.verticalScrollbar()->width(); + verticalThickness = layer.horizontalScrollbar()->height(); } return LayoutRect(cornerStart(layer, bounds.x(), bounds.maxX(), horizontalThickness), - bounds.maxY() - verticalThickness - layer->renderer().style().borderBottomWidth(), + bounds.maxY() - verticalThickness - layer.renderer().style().borderBottomWidth(), horizontalThickness, verticalThickness); } IntRect RenderLayer::scrollCornerRect() const { - // We have a scrollbar corner when a scrollbar is visible and not filling the entire length of the box. + // We have a scrollbar corner when a non overlay scrollbar is visible and not filling the entire length of the box. // This happens when: - // (a) A resizer is present and at least one scrollbar is present - // (b) Both scrollbars are present. - bool hasHorizontalBar = horizontalScrollbar(); - bool hasVerticalBar = verticalScrollbar(); + // (a) A resizer is present and at least one non overlay scrollbar is present + // (b) Both non overlay scrollbars are present. + // Overlay scrollbars always fill the entire length of the box so we never have scroll corner in that case. + bool hasHorizontalBar = m_hBar && !m_hBar->isOverlayScrollbar(); + bool hasVerticalBar = m_vBar && !m_vBar->isOverlayScrollbar(); bool hasResizer = renderer().style().resize() != RESIZE_NONE; if ((hasHorizontalBar && hasVerticalBar) || (hasResizer && (hasHorizontalBar || hasVerticalBar))) - return pixelSnappedIntRect(cornerRect(this, renderBox()->borderBoxRect())); + return snappedIntRect(cornerRect(*this, renderBox()->borderBoxRect())); return IntRect(); } -static LayoutRect resizerCornerRect(const RenderLayer* layer, const LayoutRect& bounds) +static LayoutRect resizerCornerRect(const RenderLayer& layer, const LayoutRect& bounds) { - ASSERT(layer->renderer().isBox()); - if (layer->renderer().style().resize() == RESIZE_NONE) + ASSERT(layer.renderer().isBox()); + if (layer.renderer().style().resize() == RESIZE_NONE) return LayoutRect(); return cornerRect(layer, bounds); } @@ -2776,10 +2901,10 @@ LayoutRect RenderLayer::scrollCornerAndResizerRect() const { RenderBox* box = renderBox(); if (!box) - return IntRect(); + return LayoutRect(); LayoutRect scrollCornerAndResizer = scrollCornerRect(); if (scrollCornerAndResizer.isEmpty()) - scrollCornerAndResizer = resizerCornerRect(this, box->borderBoxRect()); + scrollCornerAndResizer = resizerCornerRect(*this, box->borderBoxRect()); return scrollCornerAndResizer; } @@ -2789,40 +2914,73 @@ bool RenderLayer::isScrollCornerVisible() const return !scrollCornerRect().isEmpty(); } -IntRect RenderLayer::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const +IntRect RenderLayer::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntRect& scrollbarRect) const { IntRect rect = scrollbarRect; rect.move(scrollbarOffset(scrollbar)); - return renderer().view().frameView().convertFromRenderer(&renderer(), rect); + return renderer().view().frameView().convertFromRendererToContainingView(&renderer(), rect); } -IntRect RenderLayer::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const +IntRect RenderLayer::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntRect& parentRect) const { - IntRect rect = renderer().view().frameView().convertToRenderer(&renderer(), parentRect); + IntRect rect = renderer().view().frameView().convertFromContainingViewToRenderer(&renderer(), parentRect); rect.move(-scrollbarOffset(scrollbar)); return rect; } -IntPoint RenderLayer::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const +IntPoint RenderLayer::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntPoint& scrollbarPoint) const { IntPoint point = scrollbarPoint; point.move(scrollbarOffset(scrollbar)); - return renderer().view().frameView().convertFromRenderer(&renderer(), point); + return renderer().view().frameView().convertFromRendererToContainingView(&renderer(), point); } -IntPoint RenderLayer::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const +IntPoint RenderLayer::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntPoint& parentPoint) const { - IntPoint point = renderer().view().frameView().convertToRenderer(&renderer(), parentPoint); + IntPoint point = renderer().view().frameView().convertFromContainingViewToRenderer(&renderer(), parentPoint); point.move(-scrollbarOffset(scrollbar)); return point; } +IntSize RenderLayer::visibleSize() const +{ + RenderBox* box = renderBox(); + if (!box) + return IntSize(); + + return IntSize(roundToInt(box->clientWidth()), roundToInt(box->clientHeight())); +} + IntSize RenderLayer::contentsSize() const { return IntSize(scrollWidth(), scrollHeight()); } +IntSize RenderLayer::scrollableContentsSize() const +{ + IntSize contentsSize = this->contentsSize(); + + if (!hasScrollableHorizontalOverflow()) + contentsSize.setWidth(std::min(contentsSize.width(), visibleSize().width())); + + if (!hasScrollableVerticalOverflow()) + contentsSize.setHeight(std::min(contentsSize.height(), visibleSize().height())); + + return contentsSize; +} + +void RenderLayer::availableContentSizeChanged(AvailableSizeChangeReason reason) +{ + ScrollableArea::availableContentSizeChanged(reason); + + if (reason == AvailableSizeChangeReason::ScrollbarsChanged) { + if (is<RenderBlock>(renderer())) + downcast<RenderBlock>(renderer()).setShouldForceRelayoutChildren(true); + renderer().setNeedsLayout(); + } +} + bool RenderLayer::shouldSuspendScrollAnimations() const { return renderer().view().frameView().shouldSuspendScrollAnimations(); @@ -2831,21 +2989,18 @@ bool RenderLayer::shouldSuspendScrollAnimations() const #if PLATFORM(IOS) void RenderLayer::didStartScroll() { - if (Page* page = renderer().frame().page()) - page->chrome().client().didStartOverflowScroll(); + page().chrome().client().didStartOverflowScroll(); } void RenderLayer::didEndScroll() { - if (Page* page = renderer().frame().page()) - page->chrome().client().didEndOverflowScroll(); + page().chrome().client().didEndOverflowScroll(); } void RenderLayer::didUpdateScroll() { // Send this notification when we scroll, since this is how we keep selection updated. - if (Page* page = renderer().frame().page()) - page->chrome().client().didLayout(ChromeClient::Scroll); + page().chrome().client().didLayout(ChromeClient::Scroll); } #endif @@ -2890,7 +3045,7 @@ IntRect RenderLayer::rectForVerticalScrollbar(const IntRect& borderBoxRect) cons LayoutUnit RenderLayer::verticalScrollbarStart(int minX, int maxX) const { const RenderBox* box = renderBox(); - if (renderer().style().shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) + if (shouldPlaceBlockDirectionScrollbarOnLeft()) return minX + box->borderLeft(); return maxX - box->borderRight() - m_vBar->width(); } @@ -2899,35 +3054,31 @@ LayoutUnit RenderLayer::horizontalScrollbarStart(int minX) const { const RenderBox* box = renderBox(); int x = minX + box->borderLeft(); - if (renderer().style().shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) - x += m_vBar ? m_vBar->width() : roundToInt(resizerCornerRect(this, box->borderBoxRect()).width()); + if (shouldPlaceBlockDirectionScrollbarOnLeft()) + x += m_vBar ? m_vBar->width() : roundToInt(resizerCornerRect(*this, box->borderBoxRect()).width()); return x; } -IntSize RenderLayer::scrollbarOffset(const Scrollbar* scrollbar) const +IntSize RenderLayer::scrollbarOffset(const Scrollbar& scrollbar) const { RenderBox* box = renderBox(); - if (scrollbar == m_vBar.get()) + if (&scrollbar == m_vBar.get()) return IntSize(verticalScrollbarStart(0, box->width()), box->borderTop()); - if (scrollbar == m_hBar.get()) - return IntSize(horizontalScrollbarStart(0), box->height() - box->borderBottom() - scrollbar->height()); + if (&scrollbar == m_hBar.get()) + return IntSize(horizontalScrollbarStart(0), box->height() - box->borderBottom() - scrollbar.height()); ASSERT_NOT_REACHED(); return IntSize(); } -void RenderLayer::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& rect) +void RenderLayer::invalidateScrollbarRect(Scrollbar& scrollbar, const IntRect& rect) { -#if PLATFORM(IOS) - // No need to invalidate scrollbars if we're using accelerated scrolling. - if (hasAcceleratedTouchScrolling()) + if (!showsOverflowControls()) return; -#endif -#if USE(ACCELERATED_COMPOSITING) - if (scrollbar == m_vBar.get()) { + if (&scrollbar == m_vBar.get()) { if (GraphicsLayer* layer = layerForVerticalScrollbar()) { layer->setNeedsDisplayInRect(rect); return; @@ -2938,7 +3089,7 @@ void RenderLayer::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& r return; } } -#endif + IntRect scrollRect = rect; RenderBox* box = renderBox(); ASSERT(box); @@ -2946,10 +3097,10 @@ void RenderLayer::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& r if (!box->parent()) return; - if (scrollbar == m_vBar.get()) + if (&scrollbar == m_vBar.get()) scrollRect.move(verticalScrollbarStart(0, box->width()), box->borderTop()); else - scrollRect.move(horizontalScrollbarStart(0), box->height() - box->borderBottom() - scrollbar->height()); + scrollRect.move(horizontalScrollbarStart(0), box->height() - box->borderBottom() - scrollbar.height()); LayoutRect repaintRect = scrollRect; renderBox()->flipForWritingMode(repaintRect); renderer().repaintRectangle(repaintRect); @@ -2957,18 +3108,14 @@ void RenderLayer::invalidateScrollbarRect(Scrollbar* scrollbar, const IntRect& r void RenderLayer::invalidateScrollCornerRect(const IntRect& rect) { -#if PLATFORM(IOS) - // No need to invalidate the scroll corner if we're using accelerated scrolling. - if (hasAcceleratedTouchScrolling()) + if (!showsOverflowControls()) return; -#endif -#if USE(ACCELERATED_COMPOSITING) if (GraphicsLayer* layer = layerForScrollCorner()) { layer->setNeedsDisplayInRect(rect); return; } -#endif + if (m_scrollCorner) m_scrollCorner->repaintRectangle(rect); if (m_resizer) @@ -2979,27 +3126,30 @@ static inline RenderElement* rendererForScrollbar(RenderLayerModelObject& render { if (Element* element = renderer.element()) { if (ShadowRoot* shadowRoot = element->containingShadowRoot()) { - if (shadowRoot->type() == ShadowRoot::UserAgentShadowRoot) - return shadowRoot->hostElement()->renderer(); + if (shadowRoot->mode() == ShadowRootMode::UserAgent) + return shadowRoot->host()->renderer(); } } return &renderer; } -PassRefPtr<Scrollbar> RenderLayer::createScrollbar(ScrollbarOrientation orientation) +Ref<Scrollbar> RenderLayer::createScrollbar(ScrollbarOrientation orientation) { RefPtr<Scrollbar> widget; - RenderElement* actualRenderer = rendererForScrollbar(renderer()); - bool hasCustomScrollbarStyle = actualRenderer->isBox() && actualRenderer->style().hasPseudoStyle(SCROLLBAR); + ASSERT(rendererForScrollbar(renderer())); + auto& actualRenderer = *rendererForScrollbar(renderer()); + bool hasCustomScrollbarStyle = is<RenderBox>(actualRenderer) && downcast<RenderBox>(actualRenderer).style().hasPseudoStyle(SCROLLBAR); if (hasCustomScrollbarStyle) - widget = RenderScrollbar::createCustomScrollbar(this, orientation, actualRenderer->element()); + widget = RenderScrollbar::createCustomScrollbar(*this, orientation, downcast<RenderBox>(actualRenderer).element()); else { - widget = Scrollbar::createNativeScrollbar(this, orientation, RegularScrollbar); + widget = Scrollbar::createNativeScrollbar(*this, orientation, RegularScrollbar); didAddScrollbar(widget.get(), orientation); + if (page().expectsWheelEventTriggers()) + scrollAnimator().setWheelEventTestTrigger(page().testTrigger()); } - renderer().view().frameView().addChild(widget.get()); - return widget.release(); + renderer().view().frameView().addChild(*widget); + return widget.releaseNonNull(); } void RenderLayer::destroyScrollbar(ScrollbarOrientation orientation) @@ -3012,16 +3162,15 @@ void RenderLayer::destroyScrollbar(ScrollbarOrientation orientation) willRemoveScrollbar(scrollbar.get(), orientation); scrollbar->removeFromParent(); - scrollbar->disconnectFromScrollableArea(); - scrollbar = 0; + scrollbar = nullptr; } bool RenderLayer::scrollsOverflow() const { - if (!renderer().isBox()) + if (!is<RenderBox>(renderer())) return false; - return toRenderBox(renderer()).scrollsOverflow(); + return downcast<RenderBox>(renderer()).scrollsOverflow(); } void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar) @@ -3029,10 +3178,18 @@ void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar) if (hasScrollbar == hasHorizontalScrollbar()) return; - if (hasScrollbar) + if (hasScrollbar) { m_hBar = createScrollbar(HorizontalScrollbar); - else +#if ENABLE(RUBBER_BANDING) + ScrollElasticity elasticity = scrollsOverflow() && renderer().settings().rubberBandingForSubScrollableRegionsEnabled() ? ScrollElasticityAutomatic : ScrollElasticityNone; + ScrollableArea::setHorizontalScrollElasticity(elasticity); +#endif + } else { destroyScrollbar(HorizontalScrollbar); +#if ENABLE(RUBBER_BANDING) + ScrollableArea::setHorizontalScrollElasticity(ScrollElasticityNone); +#endif + } // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style. if (m_hBar) @@ -3052,10 +3209,18 @@ void RenderLayer::setHasVerticalScrollbar(bool hasScrollbar) if (hasScrollbar == hasVerticalScrollbar()) return; - if (hasScrollbar) + if (hasScrollbar) { m_vBar = createScrollbar(VerticalScrollbar); - else +#if ENABLE(RUBBER_BANDING) + ScrollElasticity elasticity = scrollsOverflow() && renderer().settings().rubberBandingForSubScrollableRegionsEnabled() ? ScrollElasticityAutomatic : ScrollElasticityNone; + ScrollableArea::setVerticalScrollElasticity(elasticity); +#endif + } else { destroyScrollbar(VerticalScrollbar); +#if ENABLE(RUBBER_BANDING) + ScrollableArea::setVerticalScrollElasticity(ScrollElasticityNone); +#endif + } // Destroying or creating one bar can cause our scrollbar corner to come and go. We need to update the opposite scrollbar's style. if (m_hBar) @@ -3077,31 +3242,73 @@ ScrollableArea* RenderLayer::enclosingScrollableArea() const // FIXME: We should return the frame view here (or possibly an ancestor frame view, // if the frame view isn't scrollable. - return 0; + return nullptr; } -int RenderLayer::verticalScrollbarWidth(OverlayScrollbarSizeRelevancy relevancy) const +bool RenderLayer::isScrollableOrRubberbandable() { - if (!m_vBar || (m_vBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_vBar->shouldParticipateInHitTesting()))) - return 0; + return renderer().isScrollableOrRubberbandableBox(); +} -#if PLATFORM(IOS) - if (hasAcceleratedTouchScrolling()) - return 0; +bool RenderLayer::hasScrollableOrRubberbandableAncestor() +{ + for (RenderLayer* nextLayer = parentLayerCrossFrame(*this); nextLayer; nextLayer = parentLayerCrossFrame(*nextLayer)) { + if (nextLayer->isScrollableOrRubberbandable()) + return true; + } + + return false; +} + +#if ENABLE(CSS_SCROLL_SNAP) +void RenderLayer::updateSnapOffsets() +{ + // FIXME: Extend support beyond HTMLElements. + if (!is<HTMLElement>(enclosingElement()) || !enclosingElement()->renderBox()) + return; + + RenderBox* box = enclosingElement()->renderBox(); + updateSnapOffsetsForScrollableArea(*this, *downcast<HTMLElement>(enclosingElement()), *box, box->style()); +} + +bool RenderLayer::isScrollSnapInProgress() const +{ + if (!scrollsOverflow()) + return false; + + if (ScrollAnimator* scrollAnimator = existingScrollAnimator()) + return scrollAnimator->isScrollSnapInProgress(); + + return false; +} #endif - return m_vBar->width(); +bool RenderLayer::usesMockScrollAnimator() const +{ + return Settings::usesMockScrollAnimator(); } -int RenderLayer::horizontalScrollbarHeight(OverlayScrollbarSizeRelevancy relevancy) const +void RenderLayer::logMockScrollAnimatorMessage(const String& message) const +{ + renderer().document().addConsoleMessage(MessageSource::Other, MessageLevel::Debug, "RenderLayer: " + message); +} + +int RenderLayer::verticalScrollbarWidth(OverlayScrollbarSizeRelevancy relevancy) const { - if (!m_hBar || (m_hBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_hBar->shouldParticipateInHitTesting()))) + if (!m_vBar + || !showsOverflowControls() + || (m_vBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_vBar->shouldParticipateInHitTesting()))) return 0; -#if PLATFORM(IOS) - if (hasAcceleratedTouchScrolling()) + return m_vBar->width(); +} + +int RenderLayer::horizontalScrollbarHeight(OverlayScrollbarSizeRelevancy relevancy) const +{ + if (!m_hBar + || !showsOverflowControls() + || (m_hBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_hBar->shouldParticipateInHitTesting()))) return 0; -#endif return m_hBar->height(); } @@ -3111,7 +3318,7 @@ IntSize RenderLayer::offsetFromResizeCorner(const IntPoint& absolutePoint) const // Currently the resize corner is either the bottom right corner or the bottom left corner. // FIXME: This assumes the location is 0, 0. Is this guaranteed to always be the case? IntSize elementSize = size(); - if (renderer().style().shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) + if (shouldPlaceBlockDirectionScrollbarOnLeft()) elementSize.setWidth(0); IntPoint resizerPoint = IntPoint(elementSize); IntPoint localPoint = roundedIntPoint(absoluteToContents(absolutePoint)); @@ -3132,7 +3339,7 @@ void RenderLayer::positionOverflowControls(const IntSize& offsetFromRoot) if (!box) return; - const IntRect borderBox = box->pixelSnappedBorderBoxRect(); + const IntRect borderBox = snappedIntRect(box->borderBoxRect()); const IntRect& scrollCorner = scrollCornerRect(); IntRect absBounds(borderBox.location() + offsetFromRoot, borderBox.size()); if (m_vBar) { @@ -3150,12 +3357,10 @@ void RenderLayer::positionOverflowControls(const IntSize& offsetFromRoot) if (m_scrollCorner) m_scrollCorner->setFrameRect(scrollCorner); if (m_resizer) - m_resizer->setFrameRect(resizerCornerRect(this, borderBox)); + m_resizer->setFrameRect(resizerCornerRect(*this, borderBox)); -#if USE(ACCELERATED_COMPOSITING) if (isComposited()) backing()->positionOverflowControlsLayers(); -#endif } int RenderLayer::scrollWidth() const @@ -3163,7 +3368,8 @@ int RenderLayer::scrollWidth() const ASSERT(renderBox()); if (m_scrollDimensionsDirty) const_cast<RenderLayer*>(this)->computeScrollDimensions(); - return snapSizeToPixel(m_scrollSize.width(), renderBox()->clientLeft() + renderBox()->x()); + // FIXME: This should use snappedIntSize() instead with absolute coordinates. + return m_scrollSize.width(); } int RenderLayer::scrollHeight() const @@ -3171,7 +3377,8 @@ int RenderLayer::scrollHeight() const ASSERT(renderBox()); if (m_scrollDimensionsDirty) const_cast<RenderLayer*>(this)->computeScrollDimensions(); - return snapSizeToPixel(m_scrollSize.height(), renderBox()->clientTop() + renderBox()->y()); + // FIXME: This should use snappedIntSize() instead with absolute coordinates. + return m_scrollSize.height(); } LayoutUnit RenderLayer::overflowTop() const @@ -3213,11 +3420,13 @@ void RenderLayer::computeScrollDimensions() m_scrollDimensionsDirty = false; - m_scrollSize.setWidth(overflowRight() - overflowLeft()); - m_scrollSize.setHeight(overflowBottom() - overflowTop()); + m_scrollSize.setWidth(roundToInt(overflowRight() - overflowLeft())); + m_scrollSize.setHeight(roundToInt(overflowBottom() - overflowTop())); - int scrollableLeftOverflow = overflowLeft() - box->borderLeft(); - int scrollableTopOverflow = overflowTop() - box->borderTop(); + int scrollableLeftOverflow = roundToInt(overflowLeft() - box->borderLeft()); + if (shouldPlaceBlockDirectionScrollbarOnLeft()) + scrollableLeftOverflow -= verticalScrollbarWidth(); + int scrollableTopOverflow = roundToInt(overflowTop() - box->borderTop()); setScrollOrigin(IntPoint(-scrollableLeftOverflow, -scrollableTopOverflow)); } @@ -3235,14 +3444,28 @@ bool RenderLayer::hasHorizontalOverflow() const { ASSERT(!m_scrollDimensionsDirty); - return scrollWidth() > renderBox()->pixelSnappedClientWidth(); + return scrollWidth() > roundToInt(renderBox()->clientWidth()); } bool RenderLayer::hasVerticalOverflow() const { ASSERT(!m_scrollDimensionsDirty); - return scrollHeight() > renderBox()->pixelSnappedClientHeight(); + return scrollHeight() > roundToInt(renderBox()->clientHeight()); +} + +static bool styleRequiresScrollbar(const RenderStyle& style, ScrollbarOrientation axis) +{ + EOverflow overflow = axis == ScrollbarOrientation::HorizontalScrollbar ? style.overflowX() : style.overflowY(); + bool overflowScrollActsLikeAuto = overflow == OSCROLL && !style.hasPseudoStyle(SCROLLBAR) && ScrollbarTheme::theme().usesOverlayScrollbars(); + return overflow == OSCROLL && !overflowScrollActsLikeAuto; +} + +static bool styleDefinesAutomaticScrollbar(const RenderStyle& style, ScrollbarOrientation axis) +{ + EOverflow overflow = axis == ScrollbarOrientation::HorizontalScrollbar ? style.overflowX() : style.overflowY(); + bool overflowScrollActsLikeAuto = overflow == OSCROLL && !style.hasPseudoStyle(SCROLLBAR) && ScrollbarTheme::theme().usesOverlayScrollbars(); + return overflow == OAUTO || overflow == OOVERLAY || overflowScrollActsLikeAuto; } void RenderLayer::updateScrollbarsAfterLayout() @@ -3257,20 +3480,20 @@ void RenderLayer::updateScrollbarsAfterLayout() bool hasHorizontalOverflow = this->hasHorizontalOverflow(); bool hasVerticalOverflow = this->hasVerticalOverflow(); - // overflow:scroll should just enable/disable. - if (renderer().style().overflowX() == OSCROLL) + // If overflow requires a scrollbar, then we just need to enable or disable. + if (m_hBar && styleRequiresScrollbar(renderer().style(), HorizontalScrollbar)) m_hBar->setEnabled(hasHorizontalOverflow); - if (renderer().style().overflowY() == OSCROLL) + if (m_vBar && styleRequiresScrollbar(renderer().style(), VerticalScrollbar)) m_vBar->setEnabled(hasVerticalOverflow); - // overflow:auto may need to lay out again if scrollbars got added/removed. - bool autoHorizontalScrollBarChanged = box->hasAutoHorizontalScrollbar() && (hasHorizontalScrollbar() != hasHorizontalOverflow); - bool autoVerticalScrollBarChanged = box->hasAutoVerticalScrollbar() && (hasVerticalScrollbar() != hasVerticalOverflow); + // Scrollbars with auto behavior may need to lay out again if scrollbars got added or removed. + bool autoHorizontalScrollBarChanged = box->hasHorizontalScrollbarWithAutoBehavior() && (hasHorizontalScrollbar() != hasHorizontalOverflow); + bool autoVerticalScrollBarChanged = box->hasVerticalScrollbarWithAutoBehavior() && (hasVerticalScrollbar() != hasVerticalOverflow); if (autoHorizontalScrollBarChanged || autoVerticalScrollBarChanged) { - if (box->hasAutoHorizontalScrollbar()) + if (box->hasHorizontalScrollbarWithAutoBehavior()) setHasHorizontalScrollbar(hasHorizontalOverflow); - if (box->hasAutoVerticalScrollbar()) + if (box->hasVerticalScrollbarWithAutoBehavior()) setHasVerticalScrollbar(hasVerticalOverflow); updateSelfPaintingLayer(); @@ -3288,8 +3511,8 @@ void RenderLayer::updateScrollbarsAfterLayout() // Our proprietary overflow: overlay value doesn't trigger a layout. m_inOverflowRelayout = true; renderer().setNeedsLayout(MarkOnlyThis); - if (renderer().isRenderBlock()) { - RenderBlock& block = toRenderBlock(renderer()); + if (is<RenderBlock>(renderer())) { + RenderBlock& block = downcast<RenderBlock>(renderer()); block.scrollbarsChanged(autoHorizontalScrollBarChanged, autoVerticalScrollBarChanged); block.layoutBlock(true); } else @@ -3301,14 +3524,14 @@ void RenderLayer::updateScrollbarsAfterLayout() // Set up the range (and page step/line step). if (m_hBar) { - int clientWidth = box->pixelSnappedClientWidth(); - int pageStep = std::max(std::max<int>(clientWidth * Scrollbar::minFractionToStepWhenPaging(), clientWidth - Scrollbar::maxOverlapBetweenPages()), 1); + int clientWidth = roundToInt(box->clientWidth()); + int pageStep = Scrollbar::pageStep(clientWidth); m_hBar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep); m_hBar->setProportion(clientWidth, m_scrollSize.width()); } if (m_vBar) { - int clientHeight = box->pixelSnappedClientHeight(); - int pageStep = std::max(std::max<int>(clientHeight * Scrollbar::minFractionToStepWhenPaging(), clientHeight - Scrollbar::maxOverlapBetweenPages()), 1); + int clientHeight = roundToInt(box->clientHeight()); + int pageStep = Scrollbar::pageStep(clientHeight); m_vBar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep); m_vBar->setProportion(clientHeight, m_scrollSize.height()); } @@ -3323,14 +3546,20 @@ void RenderLayer::updateScrollInfoAfterLayout() return; m_scrollDimensionsDirty = true; - IntSize originalScrollOffset = scrollOffset(); + ScrollOffset originalScrollOffset = scrollOffset(); computeScrollDimensions(); - if (box->style().overflowX() != OMARQUEE) { +#if ENABLE(CSS_SCROLL_SNAP) + // FIXME: Ensure that offsets are also updated in case of programmatic style changes. + // https://bugs.webkit.org/show_bug.cgi?id=135964 + updateSnapOffsets(); +#endif + + if (!box->isHTMLMarquee() && !isRubberBandInProgress()) { // Layout may cause us to be at an invalid scroll position. In this case we need // to pull our scroll offsets back to the max (or push them up to the min). - IntSize clampedScrollOffset = clampScrollOffset(scrollOffset()); + ScrollOffset clampedScrollOffset = clampScrollOffset(scrollOffset()); #if PLATFORM(IOS) // FIXME: This looks wrong. The caret adjust mode should only be enabled on editing related entry points. // This code was added to fix an issue where the text insertion point would always be drawn on the right edge @@ -3350,16 +3579,16 @@ void RenderLayer::updateScrollInfoAfterLayout() if (originalScrollOffset != scrollOffset()) scrollToOffsetWithoutAnimation(IntPoint(scrollOffset())); -#if USE(ACCELERATED_COMPOSITING) // Composited scrolling may need to be enabled or disabled if the amount of overflow changed. if (compositor().updateLayerCompositingState(*this)) compositor().setCompositingLayersNeedRebuild(); -#endif + + updateScrollSnapState(); } bool RenderLayer::overflowControlsIntersectRect(const IntRect& localRect) const { - const IntRect borderBox = renderBox()->pixelSnappedBorderBoxRect(); + const IntRect borderBox = snappedIntRect(renderBox()->borderBoxRect()); if (rectForHorizontalScrollbar(borderBox).intersects(localRect)) return true; @@ -3370,23 +3599,31 @@ bool RenderLayer::overflowControlsIntersectRect(const IntRect& localRect) const if (scrollCornerRect().intersects(localRect)) return true; - if (resizerCornerRect(this, borderBox).intersects(localRect)) + if (resizerCornerRect(*this, borderBox).intersects(localRect)) return true; return false; } -void RenderLayer::paintOverflowControls(GraphicsContext* context, const IntPoint& paintOffset, const IntRect& damageRect, bool paintingOverlayControls) +bool RenderLayer::showsOverflowControls() const +{ +#if PLATFORM(IOS) + // Don't render (custom) scrollbars if we have accelerated scrolling. + if (hasAcceleratedTouchScrolling()) + return false; +#endif + + return true; +} + +void RenderLayer::paintOverflowControls(GraphicsContext& context, const IntPoint& paintOffset, const IntRect& damageRect, bool paintingOverlayControls) { // Don't do anything if we have no overflow. if (!renderer().hasOverflowClip()) return; -#if PLATFORM(IOS) - // Don't render (custom) scrollbars if we have accelerated scrolling. - if (hasAcceleratedTouchScrolling()) + if (!showsOverflowControls()) return; -#endif // Overlay scrollbars paint in a second pass through the layer tree so that they will paint // on top of everything else. If this is the normal painting pass, paintingOverlayControls @@ -3396,20 +3633,16 @@ void RenderLayer::paintOverflowControls(GraphicsContext* context, const IntPoint // second pass doesn't need to re-enter the RenderTree to get it right. if (hasOverlayScrollbars() && !paintingOverlayControls) { m_cachedOverlayScrollbarOffset = paintOffset; -#if USE(ACCELERATED_COMPOSITING) + // It's not necessary to do the second pass if the scrollbars paint into layers. if ((m_hBar && layerForHorizontalScrollbar()) || (m_vBar && layerForVerticalScrollbar())) return; -#endif IntRect localDamgeRect = damageRect; localDamgeRect.moveBy(-paintOffset); if (!overflowControlsIntersectRect(localDamgeRect)) return; - RenderLayer* paintingRoot = 0; -#if USE(ACCELERATED_COMPOSITING) - paintingRoot = enclosingCompositingLayer(); -#endif + RenderLayer* paintingRoot = enclosingCompositingLayer(); if (!paintingRoot) paintingRoot = renderer().view().layer(); @@ -3431,23 +3664,13 @@ void RenderLayer::paintOverflowControls(GraphicsContext* context, const IntPoint positionOverflowControls(toIntSize(adjustedPaintOffset)); // Now that we're sure the scrollbars are in the right place, paint them. - if (m_hBar -#if USE(ACCELERATED_COMPOSITING) - && !layerForHorizontalScrollbar() -#endif - ) + if (m_hBar && !layerForHorizontalScrollbar()) m_hBar->paint(context, damageRect); - if (m_vBar -#if USE(ACCELERATED_COMPOSITING) - && !layerForVerticalScrollbar() -#endif - ) + if (m_vBar && !layerForVerticalScrollbar()) m_vBar->paint(context, damageRect); -#if USE(ACCELERATED_COMPOSITING) if (layerForScrollCorner()) return; -#endif // We fill our scroll corner with white if we have a scrollbar that doesn't run all the way up to the // edge of the box. @@ -3457,17 +3680,14 @@ void RenderLayer::paintOverflowControls(GraphicsContext* context, const IntPoint paintResizer(context, adjustedPaintOffset, damageRect); } -void RenderLayer::paintScrollCorner(GraphicsContext* context, const IntPoint& paintOffset, const IntRect& damageRect) +void RenderLayer::paintScrollCorner(GraphicsContext& context, const IntPoint& paintOffset, const IntRect& damageRect) { - RenderBox* box = renderBox(); - ASSERT(box); - IntRect absRect = scrollCornerRect(); absRect.moveBy(paintOffset); if (!absRect.intersects(damageRect)) return; - if (context->updatingControlTints()) { + if (context.updatingControlTints()) { updateScrollCornerStyle(); return; } @@ -3480,39 +3700,41 @@ void RenderLayer::paintScrollCorner(GraphicsContext* context, const IntPoint& pa // We don't want to paint white if we have overlay scrollbars, since we need // to see what is behind it. if (!hasOverlayScrollbars()) - context->fillRect(absRect, Color::white, box->style().colorSpace()); + context.fillRect(absRect, Color::white); } -void RenderLayer::drawPlatformResizerImage(GraphicsContext* context, const LayoutRect& resizerCornerRect) +void RenderLayer::drawPlatformResizerImage(GraphicsContext& context, const LayoutRect& resizerCornerRect) { - float deviceScaleFactor = WebCore::deviceScaleFactor(&renderer().frame()); - RefPtr<Image> resizeCornerImage; - IntSize cornerResizerSize; - if (deviceScaleFactor >= 2) { - DEFINE_STATIC_LOCAL(Image*, resizeCornerImageHiRes, (Image::loadPlatformResource("textAreaResizeCorner@2x").leakRef())); + FloatSize cornerResizerSize; + if (renderer().document().deviceScaleFactor() >= 2) { + static NeverDestroyed<Image*> resizeCornerImageHiRes(Image::loadPlatformResource("textAreaResizeCorner@2x").leakRef()); resizeCornerImage = resizeCornerImageHiRes; cornerResizerSize = resizeCornerImage->size(); cornerResizerSize.scale(0.5f); } else { - DEFINE_STATIC_LOCAL(Image*, resizeCornerImageLoRes, (Image::loadPlatformResource("textAreaResizeCorner").leakRef())); + static NeverDestroyed<Image*> resizeCornerImageLoRes(Image::loadPlatformResource("textAreaResizeCorner").leakRef()); resizeCornerImage = resizeCornerImageLoRes; cornerResizerSize = resizeCornerImage->size(); } - if (renderer().style().shouldPlaceBlockDirectionScrollbarOnLogicalLeft()) { - context->save(); - context->translate(resizerCornerRect.x() + cornerResizerSize.width(), resizerCornerRect.y() + resizerCornerRect.height() - cornerResizerSize.height()); - context->scale(FloatSize(-1.0, 1.0)); - context->drawImage(resizeCornerImage.get(), renderer().style().colorSpace(), IntRect(IntPoint(), cornerResizerSize)); - context->restore(); + if (shouldPlaceBlockDirectionScrollbarOnLeft()) { + context.save(); + context.translate(resizerCornerRect.x() + cornerResizerSize.width(), resizerCornerRect.y() + resizerCornerRect.height() - cornerResizerSize.height()); + context.scale(FloatSize(-1.0, 1.0)); + if (resizeCornerImage) + context.drawImage(*resizeCornerImage, FloatRect(FloatPoint(), cornerResizerSize)); + context.restore(); return; } - LayoutRect imageRect(resizerCornerRect.maxXMaxYCorner() - cornerResizerSize, cornerResizerSize); - context->drawImage(resizeCornerImage.get(), renderer().style().colorSpace(), pixelSnappedIntRect(imageRect)); + + if (!resizeCornerImage) + return; + FloatRect imageRect = snapRectToDevicePixels(LayoutRect(resizerCornerRect.maxXMaxYCorner() - cornerResizerSize, cornerResizerSize), renderer().document().deviceScaleFactor()); + context.drawImage(*resizeCornerImage, imageRect); } -void RenderLayer::paintResizer(GraphicsContext* context, const LayoutPoint& paintOffset, const LayoutRect& damageRect) +void RenderLayer::paintResizer(GraphicsContext& context, const LayoutPoint& paintOffset, const LayoutRect& damageRect) { if (renderer().style().resize() == RESIZE_NONE) return; @@ -3520,12 +3742,12 @@ void RenderLayer::paintResizer(GraphicsContext* context, const LayoutPoint& pain RenderBox* box = renderBox(); ASSERT(box); - LayoutRect absRect = resizerCornerRect(this, box->borderBoxRect()); + LayoutRect absRect = resizerCornerRect(*this, box->borderBoxRect()); absRect.moveBy(paintOffset); if (!absRect.intersects(damageRect)) return; - if (context->updatingControlTints()) { + if (context.updatingControlTints()) { updateResizerStyle(); return; } @@ -3540,14 +3762,14 @@ void RenderLayer::paintResizer(GraphicsContext* context, const LayoutPoint& pain // Draw a frame around the resizer (1px grey line) if there are any scrollbars present. // Clipping will exclude the right and bottom edges of this frame. if (!hasOverlayScrollbars() && (m_vBar || m_hBar)) { - GraphicsContextStateSaver stateSaver(*context); - context->clip(absRect); + GraphicsContextStateSaver stateSaver(context); + context.clip(absRect); LayoutRect largerCorner = absRect; largerCorner.setSize(LayoutSize(largerCorner.width() + LayoutUnit::fromPixel(1), largerCorner.height() + LayoutUnit::fromPixel(1))); - context->setStrokeColor(Color(makeRGB(217, 217, 217)), ColorSpaceDeviceRGB); - context->setStrokeThickness(1.0f); - context->setFillColor(Color::transparent, ColorSpaceDeviceRGB); - context->drawRect(pixelSnappedIntRect(largerCorner)); + context.setStrokeColor(Color(makeRGB(217, 217, 217))); + context.setStrokeThickness(1.0f); + context.setFillColor(Color::transparent); + context.drawRect(snappedIntRect(largerCorner)); } } @@ -3561,8 +3783,8 @@ bool RenderLayer::isPointInResizeControl(const IntPoint& absolutePoint) const IntPoint localPoint = roundedIntPoint(absoluteToContents(absolutePoint)); - IntRect localBounds(0, 0, box->pixelSnappedWidth(), box->pixelSnappedHeight()); - return resizerCornerRect(this, localBounds).contains(localPoint); + IntRect localBounds(IntPoint(), snappedIntRect(box->frameRect()).size()); + return resizerCornerRect(*this, localBounds).contains(localPoint); } bool RenderLayer::hitTestOverflowControls(HitTestResult& result, const IntPoint& localPoint) @@ -3575,7 +3797,7 @@ bool RenderLayer::hitTestOverflowControls(HitTestResult& result, const IntPoint& IntRect resizeControlRect; if (renderer().style().resize() != RESIZE_NONE) { - resizeControlRect = pixelSnappedIntRect(resizerCornerRect(this, box->borderBoxRect())); + resizeControlRect = snappedIntRect(resizerCornerRect(*this, box->borderBoxRect())); if (resizeControlRect.contains(localPoint)) return true; } @@ -3615,19 +3837,18 @@ bool RenderLayer::scroll(ScrollDirection direction, ScrollGranularity granularit return ScrollableArea::scroll(direction, granularity, multiplier); } -void RenderLayer::paint(GraphicsContext* context, const LayoutRect& damageRect, PaintBehavior paintBehavior, RenderObject* subtreePaintRoot, RenderRegion* region, PaintLayerFlags paintFlags) +void RenderLayer::paint(GraphicsContext& context, const LayoutRect& damageRect, const LayoutSize& subpixelOffset, PaintBehavior paintBehavior, RenderObject* subtreePaintRoot, PaintLayerFlags paintFlags, SecurityOriginPaintPolicy paintPolicy) { OverlapTestRequestMap overlapTestRequests; - LayerPaintingInfo paintingInfo(this, enclosingIntRect(damageRect), paintBehavior, LayoutSize(), subtreePaintRoot, region, &overlapTestRequests); + LayerPaintingInfo paintingInfo(this, enclosingIntRect(damageRect), paintBehavior, subpixelOffset, subtreePaintRoot, &overlapTestRequests, paintPolicy == SecurityOriginPaintPolicy::AccessibleOriginOnly); paintLayer(context, paintingInfo, paintFlags); - OverlapTestRequestMap::iterator end = overlapTestRequests.end(); - for (OverlapTestRequestMap::iterator it = overlapTestRequests.begin(); it != end; ++it) - it->key->setOverlapTestResult(false); + for (auto& widget : overlapTestRequests.keys()) + widget->setOverlapTestResult(false); } -void RenderLayer::paintOverlayScrollbars(GraphicsContext* context, const LayoutRect& damageRect, PaintBehavior paintBehavior, RenderObject* subtreePaintRoot) +void RenderLayer::paintOverlayScrollbars(GraphicsContext& context, const LayoutRect& damageRect, PaintBehavior paintBehavior, RenderObject* subtreePaintRoot) { if (!m_containsDirtyOverlayScrollbars) return; @@ -3642,9 +3863,7 @@ static bool inContainingBlockChain(RenderLayer* startLayer, RenderLayer* endLaye { if (startLayer == endLayer) return true; - - RenderView* view = &startLayer->renderer().view(); - for (RenderBlock* currentBlock = startLayer->renderer().containingBlock(); currentBlock && currentBlock != view; currentBlock = currentBlock->containingBlock()) { + for (const auto* currentBlock = startLayer->renderer().containingBlock(); currentBlock && !is<RenderView>(*currentBlock); currentBlock = currentBlock->containingBlock()) { if (currentBlock->layer() == endLayer) return true; } @@ -3652,96 +3871,93 @@ static bool inContainingBlockChain(RenderLayer* startLayer, RenderLayer* endLaye return false; } -void RenderLayer::clipToRect(RenderLayer* rootLayer, GraphicsContext* context, const LayoutRect& paintDirtyRect, const ClipRect& clipRect, - BorderRadiusClippingRule rule) +void RenderLayer::clipToRect(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, const ClipRect& clipRect, BorderRadiusClippingRule rule) { - if (clipRect.rect() != paintDirtyRect) { - context->save(); - context->clip(pixelSnappedIntRect(clipRect.rect())); - } + float deviceScaleFactor = renderer().document().deviceScaleFactor(); + bool needsClipping = !clipRect.isInfinite() && clipRect.rect() != paintingInfo.paintDirtyRect; + if (needsClipping || clipRect.affectedByRadius()) + context.save(); - if (!clipRect.hasRadius()) - return; + if (needsClipping) { + LayoutRect adjustedClipRect = clipRect.rect(); + adjustedClipRect.move(paintingInfo.subpixelOffset); + context.clip(snapRectToDevicePixels(adjustedClipRect, deviceScaleFactor)); + } - // If the clip rect has been tainted by a border radius, then we have to walk up our layer chain applying the clips from - // any layers with overflow. The condition for being able to apply these clips is that the overflow object be in our - // containing block chain so we check that also. - for (RenderLayer* layer = rule == IncludeSelfForBorderRadius ? this : parent(); layer; layer = layer->parent()) { - if (layer->renderer().hasOverflowClip() && layer->renderer().style().hasBorderRadius() && inContainingBlockChain(this, layer)) { - LayoutPoint delta; - layer->convertToLayerCoords(rootLayer, delta); - context->clipRoundedRect(layer->renderer().style().getRoundedInnerBorderFor(LayoutRect(delta, layer->size()))); + if (clipRect.affectedByRadius()) { + // If the clip rect has been tainted by a border radius, then we have to walk up our layer chain applying the clips from + // any layers with overflow. The condition for being able to apply these clips is that the overflow object be in our + // containing block chain so we check that also. + for (RenderLayer* layer = rule == IncludeSelfForBorderRadius ? this : parent(); layer; layer = layer->parent()) { + if (layer->renderer().hasOverflowClip() && layer->renderer().style().hasBorderRadius() && inContainingBlockChain(this, layer)) { + LayoutRect adjustedClipRect = LayoutRect(toLayoutPoint(layer->offsetFromAncestor(paintingInfo.rootLayer, AdjustForColumns)), layer->size()); + adjustedClipRect.move(paintingInfo.subpixelOffset); + FloatRoundedRect roundedRect = layer->renderer().style().getRoundedInnerBorderFor(adjustedClipRect).pixelSnappedRoundedRectForPainting(deviceScaleFactor); + if (roundedRect.intersectionIsRectangular(paintingInfo.paintDirtyRect)) + context.clip(snapRectToDevicePixels(intersection(paintingInfo.paintDirtyRect, adjustedClipRect), deviceScaleFactor)); + else + context.clipRoundedRect(roundedRect); + } + + if (layer == paintingInfo.rootLayer) + break; } - - if (layer == rootLayer) - break; } } -void RenderLayer::restoreClip(GraphicsContext* context, const LayoutRect& paintDirtyRect, const ClipRect& clipRect) +void RenderLayer::restoreClip(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, const ClipRect& clipRect) { - if (clipRect.rect() == paintDirtyRect) - return; - context->restore(); + if ((!clipRect.isInfinite() && clipRect.rect() != paintingInfo.paintDirtyRect) || clipRect.affectedByRadius()) + context.restore(); } static void performOverlapTests(OverlapTestRequestMap& overlapTestRequests, const RenderLayer* rootLayer, const RenderLayer* layer) { Vector<OverlapTestRequestClient*> overlappedRequestClients; - OverlapTestRequestMap::iterator end = overlapTestRequests.end(); - LayoutRect boundingBox = layer->boundingBox(rootLayer); - for (OverlapTestRequestMap::iterator it = overlapTestRequests.begin(); it != end; ++it) { - if (!boundingBox.intersects(it->value)) + LayoutRect boundingBox = layer->boundingBox(rootLayer, layer->offsetFromAncestor(rootLayer)); + for (auto& request : overlapTestRequests) { + if (!boundingBox.intersects(request.value)) continue; - it->key->setOverlapTestResult(true); - overlappedRequestClients.append(it->key); + request.key->setOverlapTestResult(true); + overlappedRequestClients.append(request.key); } - for (size_t i = 0; i < overlappedRequestClients.size(); ++i) - overlapTestRequests.remove(overlappedRequestClients[i]); + for (auto* client : overlappedRequestClients) + overlapTestRequests.remove(client); } -#if USE(ACCELERATED_COMPOSITING) - static inline bool shouldDoSoftwarePaint(const RenderLayer* layer, bool paintingReflection) { return paintingReflection && !layer->has3DTransform(); } - -#endif static inline bool shouldSuppressPaintingLayer(RenderLayer* layer) { // Avoid painting descendants of the root layer when stylesheets haven't loaded. This eliminates FOUC. // It's ok not to draw, because later on, when all the stylesheets do load, updateStyleSelector on the Document // will do a full repaint(). - if (layer->renderer().document().didLayoutWithPendingStylesheets() && !layer->isRootLayer() && !layer->renderer().isRoot()) + if (layer->renderer().document().didLayoutWithPendingStylesheets() && !layer->isRootLayer() && !layer->renderer().isDocumentElementRenderer()) return true; // Avoid painting all layers if the document is in a state where visual updates aren't allowed. - // A full repaint will occur in Document::implicitClose() if painting is suppressed here. + // A full repaint will occur in Document::setVisualUpdatesAllowed(bool) if painting is suppressed here. if (!layer->renderer().document().visualUpdatesAllowed()) return true; return false; } -#if USE(ACCELERATED_COMPOSITING) - static inline bool paintForFixedRootBackground(const RenderLayer* layer, RenderLayer::PaintLayerFlags paintFlags) { - return layer->renderer().isRoot() && (paintFlags & RenderLayer::PaintLayerPaintingRootBackgroundOnly); + return layer->renderer().isDocumentElementRenderer() && (paintFlags & RenderLayer::PaintLayerPaintingRootBackgroundOnly); } -#endif - -void RenderLayer::paintLayer(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) +void RenderLayer::paintLayer(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { -#if USE(ACCELERATED_COMPOSITING) if (isComposited()) { // The updatingControlTints() painting pass goes through compositing layers, // but we need to ensure that we don't cache clip rects computed with the wrong root in this case. - if (context->updatingControlTints() || (paintingInfo.paintBehavior & PaintBehaviorFlattenCompositingLayers)) + if (context.updatingControlTints() || (paintingInfo.paintBehavior & PaintBehaviorFlattenCompositingLayers)) paintFlags |= PaintLayerTemporaryClipRects; else if (!backing()->paintsIntoWindow() && !backing()->paintsIntoCompositedAncestor() @@ -3756,7 +3972,6 @@ void RenderLayer::paintLayer(GraphicsContext* context, const LayerPaintingInfo& ASSERT(renderer().style().position() == FixedPosition); return; } -#endif // Non self-painting leaf layers don't need to be painted as their renderer() should properly paint itself. if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant()) @@ -3771,20 +3986,11 @@ void RenderLayer::paintLayer(GraphicsContext* context, const LayerPaintingInfo& // Don't paint the layer if the renderer doesn't belong to this region. // This is true as long as we clamp the range of a box to its containing block range. - - // FIXME: Hack to disable region information for in flow threads. Implement this logic in a different way. - LayerPaintingInfo& info = const_cast<LayerPaintingInfo&>(paintingInfo); - RenderRegion* region = info.region; - if (region) { - if (enclosingPaginationLayer()) - info.region = 0; - else { - RenderFlowThread* flowThread = region->flowThread(); - ASSERT(flowThread); - - if (!flowThread->objectShouldPaintInFlowRegion(&renderer(), region)) - return; - } + RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment(); + if (namedFlowFragment) { + ASSERT(namedFlowFragment->isValid()); + if (!namedFlowFragment->flowThread()->objectShouldFragmentInFlowRegion(&renderer(), namedFlowFragment)) + return; } if (paintsWithTransparency(paintingInfo.paintBehavior)) @@ -3794,53 +4000,48 @@ void RenderLayer::paintLayer(GraphicsContext* context, const LayerPaintingInfo& if (paintsWithTransform(paintingInfo.paintBehavior) && !(paintFlags & PaintLayerAppliedTransform)) { TransformationMatrix layerTransform = renderableTransform(paintingInfo.paintBehavior); // If the transform can't be inverted, then don't paint anything. - if (!layerTransform.isInvertible()) { - info.region = region; + if (!layerTransform.isInvertible()) return; - } // If we have a transparency layer enclosing us and we are the root of a transform, then we need to establish the transparency // layer from the parent now, assuming there is a parent if (paintFlags & PaintLayerHaveTransparency) { if (parent()) - parent()->beginTransparencyLayers(context, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, paintingInfo.paintBehavior); + parent()->beginTransparencyLayers(context, paintingInfo, paintingInfo.paintDirtyRect); else - beginTransparencyLayers(context, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, paintingInfo.paintBehavior); + beginTransparencyLayers(context, paintingInfo, paintingInfo.paintDirtyRect); } - if (enclosingPaginationLayer()) { + if (enclosingPaginationLayer(ExcludeCompositedPaginatedLayers)) { paintTransformedLayerIntoFragments(context, paintingInfo, paintFlags); - info.region = region; return; } // Make sure the parent's clip rects have been calculated. ClipRect clipRect = paintingInfo.paintDirtyRect; if (parent()) { - ClipRectsContext clipRectsContext(paintingInfo.rootLayer, paintingInfo.region, (paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, + ClipRectsContext clipRectsContext(paintingInfo.rootLayer, (paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize, (paintFlags & PaintLayerPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip); clipRect = backgroundClipRect(clipRectsContext); clipRect.intersect(paintingInfo.paintDirtyRect); // Push the parent coordinate space's clip. - parent()->clipToRect(paintingInfo.rootLayer, context, paintingInfo.paintDirtyRect, clipRect); + parent()->clipToRect(context, paintingInfo, clipRect); } paintLayerByApplyingTransform(context, paintingInfo, paintFlags); // Restore the clip. if (parent()) - parent()->restoreClip(context, paintingInfo.paintDirtyRect, clipRect); + parent()->restoreClip(context, paintingInfo, clipRect); - info.region = region; return; } paintLayerContentsAndReflection(context, paintingInfo, paintFlags); - info.region = region; } -void RenderLayer::paintLayerContentsAndReflection(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) +void RenderLayer::paintLayerContentsAndReflection(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant()); @@ -3858,160 +4059,202 @@ void RenderLayer::paintLayerContentsAndReflection(GraphicsContext* context, cons paintLayerContents(context, paintingInfo, localPaintFlags); } -bool RenderLayer::setupFontSubpixelQuantization(GraphicsContext* context, bool& didQuantizeFonts) +bool RenderLayer::setupFontSubpixelQuantization(GraphicsContext& context, bool& didQuantizeFonts) { - if (context->paintingDisabled()) + if (context.paintingDisabled()) return false; bool scrollingOnMainThread = true; #if ENABLE(ASYNC_SCROLLING) - if (Page* page = renderer().frame().page()) { - if (ScrollingCoordinator* scrollingCoordinator = page->scrollingCoordinator()) - scrollingOnMainThread = scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(); - } + if (ScrollingCoordinator* scrollingCoordinator = page().scrollingCoordinator()) + scrollingOnMainThread = scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(renderer().view().frameView()); #endif // FIXME: We shouldn't have to disable subpixel quantization for overflow clips or subframes once we scroll those // things on the scrolling thread. bool contentsScrollByPainting = (renderer().hasOverflowClip() && !usesCompositedScrolling()) || (renderer().frame().ownerElement()); - if (scrollingOnMainThread || contentsScrollByPainting) { - didQuantizeFonts = context->shouldSubpixelQuantizeFonts(); - context->setShouldSubpixelQuantizeFonts(false); + bool isZooming = !page().chrome().client().hasStablePageScaleFactor(); + if (scrollingOnMainThread || contentsScrollByPainting || isZooming) { + didQuantizeFonts = context.shouldSubpixelQuantizeFonts(); + context.setShouldSubpixelQuantizeFonts(false); return true; } return false; } -bool RenderLayer::setupClipPath(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, const LayoutPoint& offsetFromRoot, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed) +template <class ReferenceBoxClipPathOperation> +static inline LayoutRect computeReferenceBox(const RenderObject& renderer, const ReferenceBoxClipPathOperation& clippingPath, const LayoutSize& offsetFromRoot, const LayoutRect& rootRelativeBounds) +{ + // FIXME: Support different reference boxes for inline content. + // https://bugs.webkit.org/show_bug.cgi?id=129047 + if (!renderer.isBox()) + return rootRelativeBounds; + + LayoutRect referenceBox; + const auto& box = downcast<RenderBox>(renderer); + switch (clippingPath.referenceBox()) { + case ContentBox: + referenceBox = box.contentBoxRect(); + referenceBox.move(offsetFromRoot); + break; + case PaddingBox: + referenceBox = box.paddingBoxRect(); + referenceBox.move(offsetFromRoot); + break; + // FIXME: Support margin-box. Use bounding client rect for now. + // https://bugs.webkit.org/show_bug.cgi?id=127984 + case MarginBox: + // fill, stroke, view-box compute to border-box for HTML elements. + case Fill: + case Stroke: + case ViewBox: + case BorderBox: + case BoxMissing: + referenceBox = box.borderBoxRect(); + referenceBox.move(offsetFromRoot); + break; + } + + return referenceBox; +} + +Path RenderLayer::computeClipPath(const LayoutSize& offsetFromRoot, LayoutRect& rootRelativeBounds, WindRule& windRule) const { - if (!renderer().hasClipPath() || context->paintingDisabled()) - return false; + const RenderStyle& style = renderer().style(); + float deviceSaleFactor = renderer().document().deviceScaleFactor(); - RenderStyle& style = renderer().style(); - ASSERT(style.clipPath()); - if (style.clipPath()->type() == ClipPathOperation::Shape) { - ShapeClipPathOperation& clippingPath = toShapeClipPathOperation(*(style.clipPath())); + if (is<ShapeClipPathOperation>(*style.clipPath())) { + auto& clipPath = downcast<ShapeClipPathOperation>(*style.clipPath()); + FloatRect referenceBox = snapRectToDevicePixels(computeReferenceBox(renderer(), clipPath, offsetFromRoot, rootRelativeBounds), deviceSaleFactor); - if (!rootRelativeBoundsComputed) { - rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, &offsetFromRoot, 0); - rootRelativeBoundsComputed = true; - } + windRule = clipPath.windRule(); + return clipPath.pathForReferenceRect(referenceBox); + } + + if (is<BoxClipPathOperation>(*style.clipPath()) && is<RenderBox>(renderer())) { - LayoutRect referenceBox; - if (renderer().isBox()) { - RenderBox& box = toRenderBox(renderer()); - switch (clippingPath.referenceBox()) { - case ContentBox: - referenceBox = box.contentBoxRect(); - referenceBox.moveBy(rootRelativeBounds.location()); - break; - case PaddingBox: - referenceBox = box.paddingBoxRect(); - referenceBox.moveBy(rootRelativeBounds.location()); - break; - case BorderBox: - referenceBox = box.borderBoxRect(); - referenceBox.moveBy(rootRelativeBounds.location()); - break; - case MarginBox: - // FIXME: Support margin-box. Use bounding client rect for now. - case BoundingBox: - case BoxMissing: - // FIXME: If no reference box was specified the spec demands to use - // the border-box. However, the current prefixed version of clip-path uses - // bounding-box. Keep bounding-box for now. - referenceBox = rootRelativeBounds; - } - } else - // FIXME: Support different reference boxes for inline content. - referenceBox = rootRelativeBounds; + auto& clipPath = downcast<BoxClipPathOperation>(*style.clipPath()); + + FloatRoundedRect shapeRect = computeRoundedRectForBoxShape(clipPath.referenceBox(), downcast<RenderBox>(renderer())).pixelSnappedRoundedRectForPainting(deviceSaleFactor); + shapeRect.move(offsetFromRoot); + + windRule = RULE_NONZERO; + return clipPath.pathForReferenceRect(shapeRect); + } + + return Path(); +} - context->save(); - context->clipPath(clippingPath.pathForReferenceRect(referenceBox), clippingPath.windRule()); +bool RenderLayer::setupClipPath(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, const LayoutSize& offsetFromRoot, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed) +{ + if (!renderer().hasClipPath() || context.paintingDisabled()) + return false; + + if (!rootRelativeBoundsComputed) { + rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, offsetFromRoot, 0); + rootRelativeBoundsComputed = true; + } + + auto& style = renderer().style(); + ASSERT(style.clipPath()); + if (is<ShapeClipPathOperation>(*style.clipPath()) || (is<BoxClipPathOperation>(*style.clipPath()) && is<RenderBox>(renderer()))) { + WindRule windRule; + LayoutSize paintingOffsetFromRoot = LayoutSize(snapSizeToDevicePixel(offsetFromRoot + paintingInfo.subpixelOffset, LayoutPoint(), renderer().document().deviceScaleFactor())); + Path path = computeClipPath(paintingOffsetFromRoot, rootRelativeBounds, windRule); + context.save(); + context.clipPath(path, windRule); return true; } -#if ENABLE(SVG) if (style.clipPath()->type() == ClipPathOperation::Reference) { ReferenceClipPathOperation* referenceClipPathOperation = static_cast<ReferenceClipPathOperation*>(style.clipPath()); Element* element = renderer().document().getElementById(referenceClipPathOperation->fragment()); if (element && element->hasTagName(SVGNames::clipPathTag) && element->renderer()) { - if (!rootRelativeBoundsComputed) { - rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, &offsetFromRoot, 0); - rootRelativeBoundsComputed = true; - } - - // FIXME: This should use a safer cast such as toRenderSVGResourceContainer(). - // FIXME: Should this do a context->save() and return true so we restore the context? - static_cast<RenderSVGResourceClipper*>(element->renderer())->applyClippingToContext(renderer(), rootRelativeBounds, paintingInfo.paintDirtyRect, context); + context.save(); + downcast<RenderSVGResourceClipper>(*element->renderer()).applyClippingToContext(renderer(), rootRelativeBounds, paintingInfo.paintDirtyRect, context); + return true; } } -#endif return false; } -#if ENABLE(CSS_FILTERS) - -PassOwnPtr<FilterEffectRendererHelper> RenderLayer::setupFilters(GraphicsContext* context, LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, const LayoutPoint& offsetFromRoot, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed) +std::pair<RenderLayer::FilterInfo*, std::unique_ptr<FilterEffectRendererHelper>> RenderLayer::filterPainter(GraphicsContext& context, PaintLayerFlags paintFlags) const { - if (context->paintingDisabled()) - return nullptr; + if (context.paintingDisabled()) + return { }; if (paintFlags & PaintLayerPaintingOverlayScrollbars) - return nullptr; + return { }; - FilterInfo* filterInfo = FilterInfo::getIfExists(*this); - bool hasPaintedFilter = filterInfo && filterInfo->renderer() && paintsWithFilters(); - if (!hasPaintedFilter) - return nullptr; + if (!paintsWithFilters()) + return { }; - OwnPtr<FilterEffectRendererHelper> filterPainter = adoptPtr(new FilterEffectRendererHelper(hasPaintedFilter)); - if (!filterPainter->haveFilterEffect()) + auto* info = FilterInfo::getIfExists(*this); + if (!info || !info->renderer()) + return { }; + + auto helper = std::make_unique<FilterEffectRendererHelper>(true, context); + if (!helper->haveFilterEffect()) + return { }; + + return { info, WTFMove(helper) }; +} + +bool RenderLayer::hasFilterThatIsPainting(GraphicsContext& context, PaintLayerFlags paintFlags) const +{ + return !!filterPainter(context, paintFlags).first; +} + +std::unique_ptr<FilterEffectRendererHelper> RenderLayer::setupFilters(GraphicsContext& context, LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, const LayoutSize& offsetFromRoot, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed) +{ + auto painter = filterPainter(context, paintFlags); + if (!painter.first) return nullptr; - - LayoutRect filterRepaintRect = filterInfo->dirtySourceRect(); - filterRepaintRect.move(offsetFromRoot.x(), offsetFromRoot.y()); + + auto& filterInfo = *painter.first; + auto& filterPainter = *painter.second; + + LayoutRect filterRepaintRect = filterInfo.dirtySourceRect(); + filterRepaintRect.move(offsetFromRoot); if (!rootRelativeBoundsComputed) { - rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, &offsetFromRoot, 0); + rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, offsetFromRoot, 0); rootRelativeBoundsComputed = true; } - if (filterPainter->prepareFilterEffect(this, rootRelativeBounds, paintingInfo.paintDirtyRect, filterRepaintRect)) { - // Now we know for sure, that the source image will be updated, so we can revert our tracking repaint rect back to zero. - filterInfo->resetDirtySourceRect(); + if (!filterPainter.prepareFilterEffect(*this, enclosingIntRect(rootRelativeBounds), enclosingIntRect(paintingInfo.paintDirtyRect), enclosingIntRect(filterRepaintRect))) + return nullptr; - if (!filterPainter->beginFilterEffect()) - return nullptr; + // Now we know for sure that the source image will be updated, so we can revert our tracking repaint rect back to zero. + filterInfo.resetDirtySourceRect(); - // Check that we didn't fail to allocate the graphics context for the offscreen buffer. - ASSERT(filterPainter->hasStartedFilterEffect()); + if (!filterPainter.beginFilterEffect()) + return nullptr; - paintingInfo.paintDirtyRect = filterPainter->repaintRect(); - // If the filter needs the full source image, we need to avoid using the clip rectangles. - // Otherwise, if for example this layer has overflow:hidden, a drop shadow will not compute correctly. - // Note that we will still apply the clipping on the final rendering of the filter. - paintingInfo.clipToDirtyRect = !filterInfo->renderer()->hasFilterThatMovesPixels(); - return filterPainter.release(); - } - return nullptr; + paintingInfo.paintDirtyRect = filterPainter.repaintRect(); + + // If the filter needs the full source image, we need to avoid using the clip rectangles. + // Otherwise, if for example this layer has overflow:hidden, a drop shadow will not compute correctly. + // Note that we will still apply the clipping on the final rendering of the filter. + paintingInfo.clipToDirtyRect = !filterInfo.renderer()->hasFilterThatMovesPixels(); + + paintingInfo.requireSecurityOriginAccessForWidgets = filterInfo.renderer()->hasFilterThatShouldBeRestrictedBySecurityOrigin(); + + return WTFMove(painter.second); } -GraphicsContext* RenderLayer::applyFilters(FilterEffectRendererHelper* filterPainter, GraphicsContext* originalContext, LayerPaintingInfo& paintingInfo, LayerFragments& layerFragments) +void RenderLayer::applyFilters(FilterEffectRendererHelper* filterPainter, GraphicsContext& originalContext, const LayerPaintingInfo& paintingInfo, const LayerFragments& layerFragments) { ASSERT(filterPainter->hasStartedFilterEffect()); - // Apply the correct clipping (ie. overflow: hidden). - // FIXME: It is incorrect to just clip to the damageRect here once multiple fragments are involved. + + // FIXME: Handle more than one fragment. ClipRect backgroundRect = layerFragments.isEmpty() ? ClipRect() : layerFragments[0].backgroundRect; - clipToRect(paintingInfo.rootLayer, originalContext, paintingInfo.paintDirtyRect, backgroundRect); + clipToRect(originalContext, paintingInfo, backgroundRect); filterPainter->applyFilterEffect(originalContext); - restoreClip(originalContext, paintingInfo.paintDirtyRect, backgroundRect); - return originalContext; + restoreClip(originalContext, paintingInfo, backgroundRect); } -#endif - // Helper for the sorting of layers by z-index. static inline bool compareZIndex(RenderLayer* first, RenderLayer* second) { @@ -4022,7 +4265,7 @@ static inline bool compareZIndex(RenderLayer* first, RenderLayer* second) // These layers should not be painted in a similar way as the other elements collected in // named flows - regions -> named flows - since we do not want them to be clipped to the // regions viewport. -void RenderLayer::paintFixedLayersInNamedFlows(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) +void RenderLayer::paintFixedLayersInNamedFlows(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { if (!isRootLayer()) return; @@ -4039,13 +4282,11 @@ void RenderLayer::paintFixedLayersInNamedFlows(GraphicsContext* context, const L renderer().view().flowThreadController().collectFixedPositionedLayers(fixedLayers); // Paint the layers - for (size_t i = 0; i < fixedLayers.size(); ++i) { - RenderLayer* fixedLayer = fixedLayers.at(i); + for (auto* fixedLayer : fixedLayers) fixedLayer->paintLayer(context, paintingInfo, paintFlags); - } } -void RenderLayer::paintLayerContents(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) +void RenderLayer::paintLayerContents(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant()); @@ -4068,7 +4309,7 @@ void RenderLayer::paintLayerContents(GraphicsContext* context, const LayerPainti || (!isPaintingScrollingContent && isPaintingCompositedForeground)); bool shouldPaintContent = m_hasVisibleContent && isSelfPaintingLayer && !isPaintingOverlayScrollbars; - if (localPaintFlags & PaintLayerPaintingRootBackgroundOnly && !renderer().isRenderView() && !renderer().isRoot()) + if (localPaintFlags & PaintLayerPaintingRootBackgroundOnly && !renderer().isRenderView() && !renderer().isDocumentElementRenderer()) return; // Ensure our lists are up-to-date. @@ -4080,9 +4321,7 @@ void RenderLayer::paintLayerContents(GraphicsContext* context, const LayerPainti && renderer().fixedPositionedWithNamedFlowContainingBlock()) return; - LayoutPoint offsetFromRoot; - convertToLayerCoords(paintingInfo.rootLayer, offsetFromRoot); - + LayoutSize offsetFromRoot = offsetFromAncestor(paintingInfo.rootLayer); LayoutRect rootRelativeBounds; bool rootRelativeBoundsComputed = false; @@ -4092,149 +4331,177 @@ void RenderLayer::paintLayerContents(GraphicsContext* context, const LayerPainti bool needToAdjustSubpixelQuantization = setupFontSubpixelQuantization(context, didQuantizeFonts); // Apply clip-path to context. - bool hasClipPath = setupClipPath(context, paintingInfo, offsetFromRoot, rootRelativeBounds, rootRelativeBoundsComputed); + LayoutSize columnAwareOffsetFromRoot = offsetFromRoot; + if (renderer().flowThreadContainingBlock() && (renderer().hasClipPath() || hasFilterThatIsPainting(context, paintFlags))) + columnAwareOffsetFromRoot = toLayoutSize(convertToLayerCoords(paintingInfo.rootLayer, LayoutPoint(), AdjustForColumns)); - LayerPaintingInfo localPaintingInfo(paintingInfo); + bool hasClipPath = false; + if (shouldApplyClipPath(paintingInfo.paintBehavior, localPaintFlags)) + hasClipPath = setupClipPath(context, paintingInfo, columnAwareOffsetFromRoot, rootRelativeBounds, rootRelativeBoundsComputed); - GraphicsContext* transparencyLayerContext = context; -#if ENABLE(CSS_FILTERS) - OwnPtr<FilterEffectRendererHelper> filterPainter = setupFilters(context, localPaintingInfo, paintFlags, offsetFromRoot, rootRelativeBounds, rootRelativeBoundsComputed); - if (filterPainter) { - context = filterPainter->filterContext(); - if (context != transparencyLayerContext && haveTransparency) { - // If we have a filter and transparency, we have to eagerly start a transparency layer here, rather than risk a child layer lazily starts one with the wrong context. - beginTransparencyLayers(transparencyLayerContext, localPaintingInfo.rootLayer, paintingInfo.paintDirtyRect, localPaintingInfo.paintBehavior); - } - } -#endif - - // If this layer's renderer is a child of the subtreePaintRoot, we render unconditionally, which - // is done by passing a nil subtreePaintRoot down to our renderer (as if no subtreePaintRoot was ever set). - // Otherwise, our renderer tree may or may not contain the subtreePaintRoot root, so we pass that root along - // so it will be tested against as we descend through the renderers. - RenderObject* subtreePaintRootForRenderer = 0; - if (localPaintingInfo.subtreePaintRoot && !renderer().isDescendantOf(localPaintingInfo.subtreePaintRoot)) - subtreePaintRootForRenderer = localPaintingInfo.subtreePaintRoot; - - if (localPaintingInfo.overlapTestRequests && isSelfPaintingLayer) - performOverlapTests(*localPaintingInfo.overlapTestRequests, localPaintingInfo.rootLayer, this); + bool selectionAndBackgroundsOnly = paintingInfo.paintBehavior & PaintBehaviorSelectionAndBackgroundsOnly; + bool selectionOnly = paintingInfo.paintBehavior & PaintBehaviorSelectionOnly; + LayerFragments layerFragments; + RenderObject* subtreePaintRootForRenderer = nullptr; - bool forceBlackText = localPaintingInfo.paintBehavior & PaintBehaviorForceBlackText; - bool selectionOnly = localPaintingInfo.paintBehavior & PaintBehaviorSelectionOnly; - - PaintBehavior paintBehavior = PaintBehaviorNormal; - if (localPaintFlags & PaintLayerPaintingSkipRootBackground) - paintBehavior |= PaintBehaviorSkipRootBackground; - else if (localPaintFlags & PaintLayerPaintingRootBackgroundOnly) - paintBehavior |= PaintBehaviorRootBackgroundOnly; + { // Scope for filter-related state changes. + LayerPaintingInfo localPaintingInfo(paintingInfo); + std::unique_ptr<FilterEffectRendererHelper> filterPainter = setupFilters(context, localPaintingInfo, paintFlags, columnAwareOffsetFromRoot, rootRelativeBounds, rootRelativeBoundsComputed); - LayerFragments layerFragments; - LayoutRect paintDirtyRect = localPaintingInfo.paintDirtyRect; - if (shouldPaintContent || shouldPaintOutline || isPaintingOverlayScrollbars) { - // Collect the fragments. This will compute the clip rectangles and paint offsets for each layer fragment, as well as whether or not the content of each - // fragment should paint. If the parent's filter dictates full repaint to ensure proper filter effect, - // use the overflow clip as dirty rect, instead of no clipping. It maintains proper clipping for overflow::scroll. - if (!paintingInfo.clipToDirtyRect && renderer().hasOverflowClip()) { - // We can turn clipping back by requesting full repaint for the overflow area. - localPaintingInfo.clipToDirtyRect = true; - paintDirtyRect = selfClipRect(); + GraphicsContext* filterContext = filterPainter ? filterPainter->filterContext() : nullptr; + if (filterContext && haveTransparency) { + // If we have a filter and transparency, we have to eagerly start a transparency layer here, rather than risk a child layer lazily starts one with the wrong context. + beginTransparencyLayers(context, localPaintingInfo, paintingInfo.paintDirtyRect); + } + GraphicsContext& currentContext = filterContext ? *filterContext : context; + + // If this layer's renderer is a child of the subtreePaintRoot, we render unconditionally, which + // is done by passing a nil subtreePaintRoot down to our renderer (as if no subtreePaintRoot was ever set). + // Otherwise, our renderer tree may or may not contain the subtreePaintRoot root, so we pass that root along + // so it will be tested against as we descend through the renderers. + if (localPaintingInfo.subtreePaintRoot && !renderer().isDescendantOf(localPaintingInfo.subtreePaintRoot)) + subtreePaintRootForRenderer = localPaintingInfo.subtreePaintRoot; + + if (localPaintingInfo.overlapTestRequests && isSelfPaintingLayer) + performOverlapTests(*localPaintingInfo.overlapTestRequests, localPaintingInfo.rootLayer, this); + + PaintBehavior paintBehavior = PaintBehaviorNormal; + if (localPaintFlags & PaintLayerPaintingSkipRootBackground) + paintBehavior |= PaintBehaviorSkipRootBackground; + else if (localPaintFlags & PaintLayerPaintingRootBackgroundOnly) + paintBehavior |= PaintBehaviorRootBackgroundOnly; + + LayoutRect paintDirtyRect = localPaintingInfo.paintDirtyRect; + if (shouldPaintContent || shouldPaintOutline || isPaintingOverlayScrollbars) { + // Collect the fragments. This will compute the clip rectangles and paint offsets for each layer fragment, as well as whether or not the content of each + // fragment should paint. If the parent's filter dictates full repaint to ensure proper filter effect, + // use the overflow clip as dirty rect, instead of no clipping. It maintains proper clipping for overflow::scroll. + if (!localPaintingInfo.clipToDirtyRect && renderer().hasOverflowClip()) { + // We can turn clipping back by requesting full repaint for the overflow area. + localPaintingInfo.clipToDirtyRect = true; + paintDirtyRect = clipRectRelativeToAncestor(localPaintingInfo.rootLayer, offsetFromRoot, LayoutRect::infiniteRect()); + } + collectFragments(layerFragments, localPaintingInfo.rootLayer, paintDirtyRect, ExcludeCompositedPaginatedLayers, + (localPaintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize, + (isPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip, offsetFromRoot); + updatePaintingInfoForFragments(layerFragments, localPaintingInfo, localPaintFlags, shouldPaintContent, offsetFromRoot); + } + + if (isPaintingCompositedBackground) { + // Paint only the backgrounds for all of the fragments of the layer. + if (shouldPaintContent && !selectionOnly) { + paintBackgroundForFragments(layerFragments, currentContext, context, paintingInfo.paintDirtyRect, haveTransparency, + localPaintingInfo, paintBehavior, subtreePaintRootForRenderer); + } } - collectFragments(layerFragments, localPaintingInfo.rootLayer, localPaintingInfo.region, paintDirtyRect, - (localPaintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize, - (isPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip, &offsetFromRoot); - updatePaintingInfoForFragments(layerFragments, localPaintingInfo, localPaintFlags, shouldPaintContent, &offsetFromRoot); - } - - if (isPaintingCompositedBackground) { - // Paint only the backgrounds for all of the fragments of the layer. - if (shouldPaintContent && !selectionOnly) - paintBackgroundForFragments(layerFragments, context, transparencyLayerContext, paintingInfo.paintDirtyRect, haveTransparency, - localPaintingInfo, paintBehavior, subtreePaintRootForRenderer); - } - - // Now walk the sorted list of children with negative z-indices. - if ((isPaintingScrollingContent && isPaintingOverflowContents) || (!isPaintingScrollingContent && isPaintingCompositedBackground)) - paintList(negZOrderList(), context, localPaintingInfo, localPaintFlags); - - if (isPaintingCompositedForeground) { - if (shouldPaintContent) - paintForegroundForFragments(layerFragments, context, transparencyLayerContext, paintingInfo.paintDirtyRect, haveTransparency, - localPaintingInfo, paintBehavior, subtreePaintRootForRenderer, selectionOnly, forceBlackText); - } - if (shouldPaintOutline) - paintOutlineForFragments(layerFragments, context, localPaintingInfo, paintBehavior, subtreePaintRootForRenderer); + // Now walk the sorted list of children with negative z-indices. + if ((isPaintingScrollingContent && isPaintingOverflowContents) || (!isPaintingScrollingContent && isPaintingCompositedBackground)) + paintList(negZOrderList(), currentContext, localPaintingInfo, localPaintFlags); + + if (isPaintingCompositedForeground) { + if (shouldPaintContent) { + paintForegroundForFragments(layerFragments, currentContext, context, paintingInfo.paintDirtyRect, haveTransparency, + localPaintingInfo, paintBehavior, subtreePaintRootForRenderer, selectionOnly || selectionAndBackgroundsOnly); + } + } - if (isPaintingCompositedForeground) { - // Paint any child layers that have overflow. - paintList(m_normalFlowList.get(), context, localPaintingInfo, localPaintFlags); - - // Now walk the sorted list of children with positive z-indices. - paintList(posZOrderList(), context, localPaintingInfo, localPaintFlags); + if (shouldPaintOutline) + paintOutlineForFragments(layerFragments, currentContext, localPaintingInfo, paintBehavior, subtreePaintRootForRenderer); - // Paint the fixed elements from flow threads. - paintFixedLayersInNamedFlows(context, localPaintingInfo, localPaintFlags); + if (isPaintingCompositedForeground) { + // Paint any child layers that have overflow. + paintList(m_normalFlowList.get(), currentContext, localPaintingInfo, localPaintFlags); - // If this is a region, paint its contents via the flow thread's layer. - paintFlowThreadIfRegion(context, localPaintingInfo, localPaintFlags, offsetFromRoot, paintDirtyRect, isPaintingOverflowContents); - } + // Now walk the sorted list of children with positive z-indices. + paintList(posZOrderList(), currentContext, localPaintingInfo, localPaintFlags); - if (isPaintingOverlayScrollbars) - paintOverflowControlsForFragments(layerFragments, context, localPaintingInfo); + // Paint the fixed elements from flow threads. + paintFixedLayersInNamedFlows(currentContext, localPaintingInfo, localPaintFlags); + + // If this is a region, paint its contents via the flow thread's layer. + if (shouldPaintContent) + paintFlowThreadIfRegionForFragments(layerFragments, currentContext, localPaintingInfo, localPaintFlags); + } -#if ENABLE(CSS_FILTERS) - if (filterPainter) { - context = applyFilters(filterPainter.get(), transparencyLayerContext, localPaintingInfo, layerFragments); - filterPainter.clear(); + if (isPaintingOverlayScrollbars && hasScrollbars()) + paintOverflowControlsForFragments(layerFragments, currentContext, localPaintingInfo); + + if (filterContext) { + // When we called collectFragments() last time, paintDirtyRect was reset to represent the filter bounds. + // Now we need to compute the backgroundRect uncontaminated by filters, in order to clip the filtered result. + // Note that we also use paintingInfo here, not localPaintingInfo which filters also contaminated. + LayerFragments layerFragments; + collectFragments(layerFragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, ExcludeCompositedPaginatedLayers, + (localPaintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize, + (isPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip, offsetFromRoot); + updatePaintingInfoForFragments(layerFragments, paintingInfo, localPaintFlags, shouldPaintContent, offsetFromRoot); + + applyFilters(filterPainter.get(), context, paintingInfo, layerFragments); + filterPainter = nullptr; + } } -#endif - // Make sure that we now use the original transparency context. - ASSERT(transparencyLayerContext == context); + if (shouldPaintContent && !(selectionOnly || selectionAndBackgroundsOnly)) { + if (shouldPaintMask(paintingInfo.paintBehavior, localPaintFlags)) { + // Paint the mask for the fragments. + paintMaskForFragments(layerFragments, context, paintingInfo, subtreePaintRootForRenderer); + } - if ((localPaintFlags & PaintLayerPaintingCompositingMaskPhase) && shouldPaintContent && renderer().hasMask() && !selectionOnly) { - // Paint the mask for the fragments. - paintMaskForFragments(layerFragments, context, localPaintingInfo, subtreePaintRootForRenderer); + if (!(paintFlags & PaintLayerPaintingCompositingMaskPhase) && (paintFlags & PaintLayerPaintingCompositingClipPathPhase)) { + // Re-use paintChildClippingMaskForFragments to paint black for the compositing clipping mask. + paintChildClippingMaskForFragments(layerFragments, context, paintingInfo, subtreePaintRootForRenderer); + } + + if ((localPaintFlags & PaintLayerPaintingChildClippingMaskPhase)) { + // Paint the border radius mask for the fragments. + paintChildClippingMaskForFragments(layerFragments, context, paintingInfo, subtreePaintRootForRenderer); + } } // End our transparency layer if (haveTransparency && m_usedTransparency && !m_paintingInsideReflection) { - context->endTransparencyLayer(); - context->restore(); + context.endTransparencyLayer(); + context.restore(); m_usedTransparency = false; } // Re-set this to whatever it was before we painted the layer. if (needToAdjustSubpixelQuantization) - context->setShouldSubpixelQuantizeFonts(didQuantizeFonts); + context.setShouldSubpixelQuantizeFonts(didQuantizeFonts); if (hasClipPath) - context->restore(); + context.restore(); } -void RenderLayer::paintLayerByApplyingTransform(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, const LayoutPoint& translationOffset) +void RenderLayer::paintLayerByApplyingTransform(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, const LayoutSize& translationOffset) { // 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; - convertToLayerCoords(paintingInfo.rootLayer, delta); - delta.moveBy(translationOffset); + float deviceScaleFactor = renderer().document().deviceScaleFactor(); + LayoutSize offsetFromParent = offsetFromAncestor(paintingInfo.rootLayer); + offsetFromParent += translationOffset; TransformationMatrix transform(renderableTransform(paintingInfo.paintBehavior)); - IntPoint roundedDelta = roundedIntPoint(delta); - transform.translateRight(roundedDelta.x(), roundedDelta.y()); - LayoutSize adjustedSubPixelAccumulation = paintingInfo.subPixelAccumulation + (delta - roundedDelta); - + // Add the subpixel accumulation to the current layer's offset so that we can always snap the translateRight value to where the renderer() is supposed to be painting. + LayoutSize offsetForThisLayer = offsetFromParent + paintingInfo.subpixelOffset; + FloatSize devicePixelSnappedOffsetForThisLayer = toFloatSize(roundPointToDevicePixels(toLayoutPoint(offsetForThisLayer), deviceScaleFactor)); + // We handle accumulated subpixels through nested layers here. Since the context gets translated to device pixels, + // all we need to do is add the delta to the accumulated pixels coming from ancestor layers. + // Translate the graphics context to the snapping position to avoid off-device-pixel positing. + transform.translateRight(devicePixelSnappedOffsetForThisLayer.width(), devicePixelSnappedOffsetForThisLayer.height()); // Apply the transform. - GraphicsContextStateSaver stateSaver(*context); - context->concatCTM(transform.toAffineTransform()); + AffineTransform oldTransfrom = context.getCTM(); + context.concatCTM(transform.toAffineTransform()); // Now do a paint with the root layer shifted to be us. - LayerPaintingInfo transformedPaintingInfo(this, enclosingIntRect(transform.inverse().mapRect(paintingInfo.paintDirtyRect)), paintingInfo.paintBehavior, - adjustedSubPixelAccumulation, paintingInfo.subtreePaintRoot, paintingInfo.region, paintingInfo.overlapTestRequests); + LayoutSize adjustedSubpixelOffset = offsetForThisLayer - LayoutSize(devicePixelSnappedOffsetForThisLayer); + LayerPaintingInfo transformedPaintingInfo(this, LayoutRect(encloseRectToDevicePixels(transform.inverse().value_or(AffineTransform()).mapRect(paintingInfo.paintDirtyRect), deviceScaleFactor)), + paintingInfo.paintBehavior, adjustedSubpixelOffset, paintingInfo.subtreePaintRoot, paintingInfo.overlapTestRequests); paintLayerContentsAndReflection(context, transformedPaintingInfo, paintFlags); + context.setCTM(oldTransfrom); } -void RenderLayer::paintList(Vector<RenderLayer*>* list, GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) +void RenderLayer::paintList(Vector<RenderLayer*>* list, GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { if (!list) return; @@ -4246,58 +4513,133 @@ void RenderLayer::paintList(Vector<RenderLayer*>* list, GraphicsContext* context LayerListMutationDetector mutationChecker(this); #endif - for (size_t i = 0; i < list->size(); ++i) { - RenderLayer* childLayer = list->at(i); + for (auto* childLayer : *list) { if (childLayer->isFlowThreadCollectingGraphicsLayersUnderRegions()) continue; + childLayer->paintLayer(context, paintingInfo, paintFlags); + } +} - if (!childLayer->isPaginated()) - childLayer->paintLayer(context, paintingInfo, paintFlags); - else - paintPaginatedChildLayer(childLayer, context, paintingInfo, paintFlags); +RenderLayer* RenderLayer::enclosingPaginationLayerInSubtree(const RenderLayer* rootLayer, PaginationInclusionMode mode) const +{ + // If we don't have an enclosing layer, or if the root layer is the same as the enclosing layer, + // then just return the enclosing pagination layer (it will be 0 in the former case and the rootLayer in the latter case). + RenderLayer* paginationLayer = enclosingPaginationLayer(mode); + if (!paginationLayer || rootLayer == paginationLayer) + return paginationLayer; + + // Walk up the layer tree and see which layer we hit first. If it's the root, then the enclosing pagination + // layer isn't in our subtree and we return nullptr. If we hit the enclosing pagination layer first, then + // we can return it. + for (const RenderLayer* layer = this; layer; layer = layer->parent()) { + if (layer == rootLayer) + return nullptr; + if (layer == paginationLayer) + return paginationLayer; } + + // This should never be reached, since an enclosing layer should always either be the rootLayer or be + // our enclosing pagination layer. + ASSERT_NOT_REACHED(); + return nullptr; } -void RenderLayer::collectFragments(LayerFragments& fragments, const RenderLayer* rootLayer, RenderRegion* region, const LayoutRect& dirtyRect, - ClipRectsType clipRectsType, OverlayScrollbarSizeRelevancy inOverlayScrollbarSizeRelevancy, ShouldRespectOverflowClip respectOverflowClip, const LayoutPoint* offsetFromRoot, - const LayoutRect* layerBoundingBox) +void RenderLayer::collectFragments(LayerFragments& fragments, const RenderLayer* rootLayer, const LayoutRect& dirtyRect, PaginationInclusionMode inclusionMode, + ClipRectsType clipRectsType, OverlayScrollbarSizeRelevancy inOverlayScrollbarSizeRelevancy, ShouldRespectOverflowClip respectOverflowClip, const LayoutSize& offsetFromRoot, + const LayoutRect* layerBoundingBox, ShouldApplyRootOffsetToFragments applyRootOffsetToFragments) { - if (!enclosingPaginationLayer() || hasTransform()) { + RenderLayer* paginationLayer = enclosingPaginationLayerInSubtree(rootLayer, inclusionMode); + if (!paginationLayer || hasTransform()) { // For unpaginated layers, there is only one fragment. LayerFragment fragment; - ClipRectsContext clipRectsContext(rootLayer, region, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip); - calculateRects(clipRectsContext, dirtyRect, fragment.layerBounds, fragment.backgroundRect, fragment.foregroundRect, fragment.outlineRect, offsetFromRoot); + ClipRectsContext clipRectsContext(rootLayer, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip); + calculateRects(clipRectsContext, dirtyRect, fragment.layerBounds, fragment.backgroundRect, fragment.foregroundRect, offsetFromRoot); fragments.append(fragment); return; } // Compute our offset within the enclosing pagination layer. - LayoutPoint offsetWithinPaginatedLayer; - convertToLayerCoords(enclosingPaginationLayer(), offsetWithinPaginatedLayer); + LayoutSize offsetWithinPaginatedLayer = offsetFromAncestor(paginationLayer); // Calculate clip rects relative to the enclosingPaginationLayer. The purpose of this call is to determine our bounds clipped to intermediate // layers between us and the pagination context. It's important to minimize the number of fragments we need to create and this helps with that. - ClipRectsContext paginationClipRectsContext(enclosingPaginationLayer(), region, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip); + ClipRectsContext paginationClipRectsContext(paginationLayer, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip); LayoutRect layerBoundsInFlowThread; ClipRect backgroundRectInFlowThread; ClipRect foregroundRectInFlowThread; - ClipRect outlineRectInFlowThread; calculateRects(paginationClipRectsContext, LayoutRect::infiniteRect(), layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread, - outlineRectInFlowThread, &offsetWithinPaginatedLayer); + offsetWithinPaginatedLayer); // Take our bounding box within the flow thread and clip it. - LayoutRect layerBoundingBoxInFlowThread = layerBoundingBox ? *layerBoundingBox : boundingBox(enclosingPaginationLayer(), 0, &offsetWithinPaginatedLayer); + LayoutRect layerBoundingBoxInFlowThread = layerBoundingBox ? *layerBoundingBox : boundingBox(paginationLayer, offsetWithinPaginatedLayer); layerBoundingBoxInFlowThread.intersect(backgroundRectInFlowThread.rect()); + + auto& enclosingFlowThread = downcast<RenderFlowThread>(paginationLayer->renderer()); + RenderLayer* parentPaginationLayer = paginationLayer->parent()->enclosingPaginationLayerInSubtree(rootLayer, inclusionMode); + LayerFragments ancestorFragments; + if (parentPaginationLayer) { + // Compute a bounding box accounting for fragments. + LayoutRect layerFragmentBoundingBoxInParentPaginationLayer = enclosingFlowThread.fragmentsBoundingBox(layerBoundingBoxInFlowThread); + + // Convert to be in the ancestor pagination context's coordinate space. + LayoutSize offsetWithinParentPaginatedLayer = paginationLayer->offsetFromAncestor(parentPaginationLayer); + layerFragmentBoundingBoxInParentPaginationLayer.move(offsetWithinParentPaginatedLayer); + + // Now collect ancestor fragments. + parentPaginationLayer->collectFragments(ancestorFragments, rootLayer, dirtyRect, inclusionMode, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip, + offsetFromAncestor(rootLayer), &layerFragmentBoundingBoxInParentPaginationLayer, ApplyRootOffsetToFragments); + + if (ancestorFragments.isEmpty()) + return; + + for (auto& ancestorFragment : ancestorFragments) { + // Shift the dirty rect into flow thread coordinates. + LayoutRect dirtyRectInFlowThread(dirtyRect); + dirtyRectInFlowThread.move(-offsetWithinParentPaginatedLayer - ancestorFragment.paginationOffset); + + size_t oldSize = fragments.size(); + + // Tell the flow thread to collect the fragments. We pass enough information to create a minimal number of fragments based off the pages/columns + // that intersect the actual dirtyRect as well as the pages/columns that intersect our layer's bounding box. + enclosingFlowThread.collectLayerFragments(fragments, layerBoundingBoxInFlowThread, dirtyRectInFlowThread); + + size_t newSize = fragments.size(); + + if (oldSize == newSize) + continue; + for (size_t i = oldSize; i < newSize; ++i) { + LayerFragment& fragment = fragments.at(i); + + // Set our four rects with all clipping applied that was internal to the flow thread. + fragment.setRects(layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread, &layerBoundingBoxInFlowThread); + + // Shift to the root-relative physical position used when painting the flow thread in this fragment. + fragment.moveBy(toLayoutPoint(ancestorFragment.paginationOffset + fragment.paginationOffset + offsetWithinParentPaginatedLayer)); + + // Intersect the fragment with our ancestor's background clip so that e.g., columns in an overflow:hidden block are + // properly clipped by the overflow. + fragment.intersect(ancestorFragment.paginationClip); + + // Now intersect with our pagination clip. This will typically mean we're just intersecting the dirty rect with the column + // clip, so the column clip ends up being all we apply. + fragment.intersect(fragment.paginationClip); + + if (applyRootOffsetToFragments == ApplyRootOffsetToFragments) + fragment.paginationOffset = fragment.paginationOffset + offsetWithinParentPaginatedLayer; + } + } + + return; + } + // Shift the dirty rect into flow thread coordinates. - LayoutPoint offsetOfPaginationLayerFromRoot; - enclosingPaginationLayer()->convertToLayerCoords(rootLayer, offsetOfPaginationLayerFromRoot); + LayoutSize offsetOfPaginationLayerFromRoot = enclosingPaginationLayer(inclusionMode)->offsetFromAncestor(rootLayer); LayoutRect dirtyRectInFlowThread(dirtyRect); - dirtyRectInFlowThread.moveBy(-offsetOfPaginationLayerFromRoot); + dirtyRectInFlowThread.move(-offsetOfPaginationLayerFromRoot); // Tell the flow thread to collect the fragments. We pass enough information to create a minimal number of fragments based off the pages/columns // that intersect the actual dirtyRect as well as the pages/columns that intersect our layer's bounding box. - RenderFlowThread& enclosingFlowThread = toRenderFlowThread(enclosingPaginationLayer()->renderer()); enclosingFlowThread.collectLayerFragments(fragments, layerBoundingBoxInFlowThread, dirtyRectInFlowThread); if (fragments.isEmpty()) @@ -4305,130 +4647,132 @@ void RenderLayer::collectFragments(LayerFragments& fragments, const RenderLayer* // Get the parent clip rects of the pagination layer, since we need to intersect with that when painting column contents. ClipRect ancestorClipRect = dirtyRect; - if (enclosingPaginationLayer()->parent()) { - ClipRectsContext clipRectsContext(rootLayer, region, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip); - ancestorClipRect = enclosingPaginationLayer()->backgroundClipRect(clipRectsContext); + if (paginationLayer->parent()) { + ClipRectsContext clipRectsContext(rootLayer, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip); + ancestorClipRect = paginationLayer->backgroundClipRect(clipRectsContext); ancestorClipRect.intersect(dirtyRect); } - for (size_t i = 0; i < fragments.size(); ++i) { - LayerFragment& fragment = fragments.at(i); - + for (auto& fragment : fragments) { // Set our four rects with all clipping applied that was internal to the flow thread. - fragment.setRects(layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread, outlineRectInFlowThread); + fragment.setRects(layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread, &layerBoundingBoxInFlowThread); // Shift to the root-relative physical position used when painting the flow thread in this fragment. - fragment.moveBy(fragment.paginationOffset + offsetOfPaginationLayerFromRoot); + fragment.moveBy(toLayoutPoint(fragment.paginationOffset + offsetOfPaginationLayerFromRoot)); // Intersect the fragment with our ancestor's background clip so that e.g., columns in an overflow:hidden block are // properly clipped by the overflow. - fragment.intersect(ancestorClipRect.rect()); - + fragment.intersect(ancestorClipRect); + // Now intersect with our pagination clip. This will typically mean we're just intersecting the dirty rect with the column // clip, so the column clip ends up being all we apply. fragment.intersect(fragment.paginationClip); + + if (applyRootOffsetToFragments == ApplyRootOffsetToFragments) + fragment.paginationOffset = fragment.paginationOffset + offsetOfPaginationLayerFromRoot; } } void RenderLayer::updatePaintingInfoForFragments(LayerFragments& fragments, const LayerPaintingInfo& localPaintingInfo, PaintLayerFlags localPaintFlags, - bool shouldPaintContent, const LayoutPoint* offsetFromRoot) + bool shouldPaintContent, const LayoutSize& offsetFromRoot) { - ASSERT(offsetFromRoot); - for (size_t i = 0; i < fragments.size(); ++i) { - LayerFragment& fragment = fragments.at(i); + for (auto& fragment : fragments) { fragment.shouldPaintContent = shouldPaintContent; if (this != localPaintingInfo.rootLayer || !(localPaintFlags & PaintLayerPaintingOverflowContents)) { - LayoutPoint newOffsetFromRoot = *offsetFromRoot + fragment.paginationOffset; - fragment.shouldPaintContent &= intersectsDamageRect(fragment.layerBounds, fragment.backgroundRect.rect(), localPaintingInfo.rootLayer, &newOffsetFromRoot, localPaintingInfo.region); + LayoutSize newOffsetFromRoot = offsetFromRoot + fragment.paginationOffset; + fragment.shouldPaintContent &= intersectsDamageRect(fragment.layerBounds, fragment.backgroundRect.rect(), localPaintingInfo.rootLayer, newOffsetFromRoot, fragment.hasBoundingBox ? &fragment.boundingBox : 0); } } } -void RenderLayer::paintTransformedLayerIntoFragments(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) +void RenderLayer::paintTransformedLayerIntoFragments(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { LayerFragments enclosingPaginationFragments; - LayoutPoint offsetOfPaginationLayerFromRoot; - LayoutRect transformedExtent = transparencyClipBox(this, enclosingPaginationLayer(), PaintingTransparencyClipBox, RootOfTransparencyClipBox, paintingInfo.paintBehavior); - enclosingPaginationLayer()->collectFragments(enclosingPaginationFragments, paintingInfo.rootLayer, paintingInfo.region, paintingInfo.paintDirtyRect, + LayoutSize offsetOfPaginationLayerFromRoot; + RenderLayer* paginatedLayer = enclosingPaginationLayer(ExcludeCompositedPaginatedLayers); + LayoutRect transformedExtent = transparencyClipBox(*this, paginatedLayer, PaintingTransparencyClipBox, RootOfTransparencyClipBox, paintingInfo.paintBehavior); + paginatedLayer->collectFragments(enclosingPaginationFragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, ExcludeCompositedPaginatedLayers, (paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize, - (paintFlags & PaintLayerPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip, &offsetOfPaginationLayerFromRoot, &transformedExtent); + (paintFlags & PaintLayerPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip, offsetOfPaginationLayerFromRoot, &transformedExtent); - for (size_t i = 0; i < enclosingPaginationFragments.size(); ++i) { - const LayerFragment& fragment = enclosingPaginationFragments.at(i); - + for (const auto& fragment : enclosingPaginationFragments) { // Apply the page/column clip for this fragment, as well as any clips established by layers in between us and // the enclosing pagination layer. LayoutRect clipRect = fragment.backgroundRect.rect(); // Now compute the clips within a given fragment - if (parent() != enclosingPaginationLayer()) { - enclosingPaginationLayer()->convertToLayerCoords(paintingInfo.rootLayer, offsetOfPaginationLayerFromRoot); + if (parent() != paginatedLayer) { + offsetOfPaginationLayerFromRoot = toLayoutSize(paginatedLayer->convertToLayerCoords(paintingInfo.rootLayer, toLayoutPoint(offsetOfPaginationLayerFromRoot))); - ClipRectsContext clipRectsContext(enclosingPaginationLayer(), paintingInfo.region, (paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, + ClipRectsContext clipRectsContext(paginatedLayer, (paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize, (paintFlags & PaintLayerPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip); LayoutRect parentClipRect = backgroundClipRect(clipRectsContext).rect(); - parentClipRect.moveBy(fragment.paginationOffset + offsetOfPaginationLayerFromRoot); + parentClipRect.move(fragment.paginationOffset + offsetOfPaginationLayerFromRoot); clipRect.intersect(parentClipRect); } - parent()->clipToRect(paintingInfo.rootLayer, context, paintingInfo.paintDirtyRect, clipRect); + parent()->clipToRect(context, paintingInfo, clipRect); paintLayerByApplyingTransform(context, paintingInfo, paintFlags, fragment.paginationOffset); - parent()->restoreClip(context, paintingInfo.paintDirtyRect, clipRect); + parent()->restoreClip(context, paintingInfo, clipRect); } } -void RenderLayer::paintBackgroundForFragments(const LayerFragments& layerFragments, GraphicsContext* context, GraphicsContext* transparencyLayerContext, +void RenderLayer::paintBackgroundForFragments(const LayerFragments& layerFragments, GraphicsContext& context, GraphicsContext& contextForTransparencyLayer, const LayoutRect& transparencyPaintDirtyRect, bool haveTransparency, const LayerPaintingInfo& localPaintingInfo, PaintBehavior paintBehavior, RenderObject* subtreePaintRootForRenderer) { - for (size_t i = 0; i < layerFragments.size(); ++i) { - const LayerFragment& fragment = layerFragments.at(i); + for (const auto& fragment : layerFragments) { if (!fragment.shouldPaintContent) continue; // Begin transparency layers lazily now that we know we have to paint something. if (haveTransparency) - beginTransparencyLayers(transparencyLayerContext, localPaintingInfo.rootLayer, transparencyPaintDirtyRect, localPaintingInfo.paintBehavior); + beginTransparencyLayers(contextForTransparencyLayer, localPaintingInfo, transparencyPaintDirtyRect); if (localPaintingInfo.clipToDirtyRect) { // Paint our background first, before painting any child layers. // Establish the clip used to paint our background. - clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect, DoNotIncludeSelfForBorderRadius); // Background painting will handle clipping to self. + clipToRect(context, localPaintingInfo, fragment.backgroundRect, DoNotIncludeSelfForBorderRadius); // Background painting will handle clipping to self. } // Paint the background. // FIXME: Eventually we will collect the region from the fragment itself instead of just from the paint info. - PaintInfo paintInfo(context, pixelSnappedIntRect(fragment.backgroundRect.rect()), PaintPhaseBlockBackground, paintBehavior, subtreePaintRootForRenderer, localPaintingInfo.region, 0, 0, &localPaintingInfo.rootLayer->renderer()); - renderer().paint(paintInfo, toPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation)); + PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhaseBlockBackground, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer()); + renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset)); if (localPaintingInfo.clipToDirtyRect) - restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect); + restoreClip(context, localPaintingInfo, fragment.backgroundRect); } } -void RenderLayer::paintForegroundForFragments(const LayerFragments& layerFragments, GraphicsContext* context, GraphicsContext* transparencyLayerContext, +void RenderLayer::paintForegroundForFragments(const LayerFragments& layerFragments, GraphicsContext& context, GraphicsContext& contextForTransparencyLayer, const LayoutRect& transparencyPaintDirtyRect, bool haveTransparency, const LayerPaintingInfo& localPaintingInfo, PaintBehavior paintBehavior, - RenderObject* subtreePaintRootForRenderer, bool selectionOnly, bool forceBlackText) + RenderObject* subtreePaintRootForRenderer, bool selectionOnly) { // Begin transparency if we have something to paint. if (haveTransparency) { - for (size_t i = 0; i < layerFragments.size(); ++i) { - const LayerFragment& fragment = layerFragments.at(i); + for (const auto& fragment : layerFragments) { if (fragment.shouldPaintContent && !fragment.foregroundRect.isEmpty()) { - beginTransparencyLayers(transparencyLayerContext, localPaintingInfo.rootLayer, transparencyPaintDirtyRect, localPaintingInfo.paintBehavior); + beginTransparencyLayers(contextForTransparencyLayer, localPaintingInfo, transparencyPaintDirtyRect); break; } } } - - PaintBehavior localPaintBehavior = forceBlackText ? (PaintBehavior)PaintBehaviorForceBlackText : paintBehavior; + + PaintBehavior localPaintBehavior; + if (localPaintingInfo.paintBehavior & PaintBehaviorForceBlackText) + localPaintBehavior = PaintBehaviorForceBlackText; + else if (localPaintingInfo.paintBehavior & PaintBehaviorForceWhiteText) + localPaintBehavior = PaintBehaviorForceWhiteText; + else + localPaintBehavior = paintBehavior; // Optimize clipping for the single fragment case. bool shouldClip = localPaintingInfo.clipToDirtyRect && layerFragments.size() == 1 && layerFragments[0].shouldPaintContent && !layerFragments[0].foregroundRect.isEmpty(); ClipRect clippedRect; if (shouldClip) { clippedRect = layerFragments[0].foregroundRect; - clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, clippedRect); + clipToRect(context, localPaintingInfo, clippedRect); } // We have to loop through every fragment multiple times, since we have to repaint in each specific phase in order for @@ -4439,195 +4783,98 @@ void RenderLayer::paintForegroundForFragments(const LayerFragments& layerFragmen if (!selectionOnly) { paintForegroundForFragmentsWithPhase(PaintPhaseFloat, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer); paintForegroundForFragmentsWithPhase(PaintPhaseForeground, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer); - - // Switch the clipping rectangle to the outline version. - if (shouldClip && clippedRect != layerFragments[0].outlineRect) { - restoreClip(context, localPaintingInfo.paintDirtyRect, clippedRect); - - if (!layerFragments[0].outlineRect.isEmpty()) { - clippedRect = layerFragments[0].outlineRect; - clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, clippedRect); - } else - shouldClip = false; - } - paintForegroundForFragmentsWithPhase(PaintPhaseChildOutlines, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer); } if (shouldClip) - restoreClip(context, localPaintingInfo.paintDirtyRect, clippedRect); + restoreClip(context, localPaintingInfo, clippedRect); } -void RenderLayer::paintForegroundForFragmentsWithPhase(PaintPhase phase, const LayerFragments& layerFragments, GraphicsContext* context, +void RenderLayer::paintForegroundForFragmentsWithPhase(PaintPhase phase, const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo, PaintBehavior paintBehavior, RenderObject* subtreePaintRootForRenderer) { bool shouldClip = localPaintingInfo.clipToDirtyRect && layerFragments.size() > 1; - for (size_t i = 0; i < layerFragments.size(); ++i) { - const LayerFragment& fragment = layerFragments.at(i); + for (const auto& fragment : layerFragments) { if (!fragment.shouldPaintContent || fragment.foregroundRect.isEmpty()) continue; if (shouldClip) - clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, fragment.foregroundRect); + clipToRect(context, localPaintingInfo, fragment.foregroundRect); - PaintInfo paintInfo(context, pixelSnappedIntRect(fragment.foregroundRect.rect()), phase, paintBehavior, subtreePaintRootForRenderer, localPaintingInfo.region, 0, 0, &localPaintingInfo.rootLayer->renderer()); + PaintInfo paintInfo(context, fragment.foregroundRect.rect(), phase, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer(), localPaintingInfo.requireSecurityOriginAccessForWidgets); if (phase == PaintPhaseForeground) paintInfo.overlapTestRequests = localPaintingInfo.overlapTestRequests; - renderer().paint(paintInfo, toPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation)); + renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset)); if (shouldClip) - restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.foregroundRect); + restoreClip(context, localPaintingInfo, fragment.foregroundRect); } } -void RenderLayer::paintOutlineForFragments(const LayerFragments& layerFragments, GraphicsContext* context, const LayerPaintingInfo& localPaintingInfo, +void RenderLayer::paintOutlineForFragments(const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo, PaintBehavior paintBehavior, RenderObject* subtreePaintRootForRenderer) { - for (size_t i = 0; i < layerFragments.size(); ++i) { - const LayerFragment& fragment = layerFragments.at(i); - if (fragment.outlineRect.isEmpty()) + for (const auto& fragment : layerFragments) { + if (fragment.backgroundRect.isEmpty()) continue; // Paint our own outline - PaintInfo paintInfo(context, pixelSnappedIntRect(fragment.outlineRect.rect()), PaintPhaseSelfOutline, paintBehavior, subtreePaintRootForRenderer, localPaintingInfo.region, 0, 0, &localPaintingInfo.rootLayer->renderer()); - clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, fragment.outlineRect, DoNotIncludeSelfForBorderRadius); - renderer().paint(paintInfo, toPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation)); - restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.outlineRect); + PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhaseSelfOutline, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer()); + clipToRect(context, localPaintingInfo, fragment.backgroundRect, DoNotIncludeSelfForBorderRadius); + renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset)); + restoreClip(context, localPaintingInfo, fragment.backgroundRect); } } -void RenderLayer::paintMaskForFragments(const LayerFragments& layerFragments, GraphicsContext* context, const LayerPaintingInfo& localPaintingInfo, +void RenderLayer::paintMaskForFragments(const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo, RenderObject* subtreePaintRootForRenderer) { - for (size_t i = 0; i < layerFragments.size(); ++i) { - const LayerFragment& fragment = layerFragments.at(i); + for (const auto& fragment : layerFragments) { if (!fragment.shouldPaintContent) continue; if (localPaintingInfo.clipToDirtyRect) - clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect, DoNotIncludeSelfForBorderRadius); // Mask painting will handle clipping to self. + clipToRect(context, localPaintingInfo, fragment.backgroundRect, DoNotIncludeSelfForBorderRadius); // Mask painting will handle clipping to self. // Paint the mask. // FIXME: Eventually we will collect the region from the fragment itself instead of just from the paint info. - PaintInfo paintInfo(context, pixelSnappedIntRect(fragment.backgroundRect.rect()), PaintPhaseMask, PaintBehaviorNormal, subtreePaintRootForRenderer, localPaintingInfo.region, 0, 0, &localPaintingInfo.rootLayer->renderer()); - renderer().paint(paintInfo, toPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation)); + PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhaseMask, PaintBehaviorNormal, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer()); + renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset)); if (localPaintingInfo.clipToDirtyRect) - restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect); + restoreClip(context, localPaintingInfo, fragment.backgroundRect); } } -void RenderLayer::paintOverflowControlsForFragments(const LayerFragments& layerFragments, GraphicsContext* context, const LayerPaintingInfo& localPaintingInfo) +void RenderLayer::paintChildClippingMaskForFragments(const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo, + RenderObject* subtreePaintRootForRenderer) { - for (size_t i = 0; i < layerFragments.size(); ++i) { - const LayerFragment& fragment = layerFragments.at(i); - clipToRect(localPaintingInfo.rootLayer, context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect); - paintOverflowControls(context, roundedIntPoint(toPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subPixelAccumulation)), - pixelSnappedIntRect(fragment.backgroundRect.rect()), true); - restoreClip(context, localPaintingInfo.paintDirtyRect, fragment.backgroundRect); - } -} + for (const auto& fragment : layerFragments) { + if (!fragment.shouldPaintContent) + continue; -void RenderLayer::paintPaginatedChildLayer(RenderLayer* childLayer, GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) -{ - // We need to do multiple passes, breaking up our child layer into strips. - Vector<RenderLayer*> columnLayers; - RenderLayer* ancestorLayer = isNormalFlowOnly() ? parent() : stackingContainer(); - for (RenderLayer* curr = childLayer->parent(); curr; curr = curr->parent()) { - if (curr->renderer().hasColumns() && checkContainingBlockChainForPagination(&childLayer->renderer(), curr->renderBox())) - columnLayers.append(curr); - if (curr == ancestorLayer) - break; - } + if (localPaintingInfo.clipToDirtyRect) + clipToRect(context, localPaintingInfo, fragment.foregroundRect, IncludeSelfForBorderRadius); // Child clipping mask painting will handle clipping to self. - // It is possible for paintLayer() to be called after the child layer ceases to be paginated but before - // updateLayerPositions() is called and resets the isPaginated() flag, see <rdar://problem/10098679>. - // If this is the case, just bail out, since the upcoming call to updateLayerPositions() will repaint the layer. - if (!columnLayers.size()) - return; + // Paint the clipped mask. + PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhaseClippingMask, PaintBehaviorNormal, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer()); + renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset)); - paintChildLayerIntoColumns(childLayer, context, paintingInfo, paintFlags, columnLayers, columnLayers.size() - 1); + if (localPaintingInfo.clipToDirtyRect) + restoreClip(context, localPaintingInfo, fragment.foregroundRect); + } } -void RenderLayer::paintChildLayerIntoColumns(RenderLayer* childLayer, GraphicsContext* context, const LayerPaintingInfo& paintingInfo, - PaintLayerFlags paintFlags, const Vector<RenderLayer*>& columnLayers, size_t colIndex) +void RenderLayer::paintOverflowControlsForFragments(const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo) { - RenderBlock& columnBlock = toRenderBlock(columnLayers[colIndex]->renderer()); - - ASSERT(columnBlock.hasColumns()); - if (!columnBlock.hasColumns()) - return; - - LayoutPoint layerOffset; - // FIXME: It looks suspicious to call convertToLayerCoords here - // as canUseConvertToLayerCoords is true for this layer. - columnBlock.layer()->convertToLayerCoords(paintingInfo.rootLayer, layerOffset); - - bool isHorizontal = columnBlock.style().isHorizontalWritingMode(); - - ColumnInfo* colInfo = columnBlock.columnInfo(); - unsigned colCount = columnBlock.columnCount(colInfo); - LayoutUnit currLogicalTopOffset = columnBlock.initialBlockOffsetForPainting(); - LayoutUnit blockDelta = columnBlock.blockDeltaForPaintingNextColumn(); - for (unsigned i = 0; i < colCount; i++) { - // For each rect, we clip to the rect, and then we adjust our coords. - LayoutRect colRect = columnBlock.columnRectAt(colInfo, i); - columnBlock.flipForWritingMode(colRect); - - LayoutUnit logicalLeftOffset = (isHorizontal ? colRect.x() : colRect.y()) - columnBlock.logicalLeftOffsetForContent(); - LayoutSize offset = isHorizontal ? LayoutSize(logicalLeftOffset, currLogicalTopOffset) : LayoutSize(currLogicalTopOffset, logicalLeftOffset); - colRect.moveBy(layerOffset); - - LayoutRect localDirtyRect(paintingInfo.paintDirtyRect); - localDirtyRect.intersect(colRect); - if (!localDirtyRect.isEmpty()) { - GraphicsContextStateSaver stateSaver(*context); - - // Each strip pushes a clip, since column boxes are specified as being - // like overflow:hidden. - context->clip(pixelSnappedIntRect(colRect)); - - if (!colIndex) { - // Apply a translation transform to change where the layer paints. - TransformationMatrix oldTransform; - bool oldHasTransform = childLayer->transform(); - if (oldHasTransform) - oldTransform = *childLayer->transform(); - TransformationMatrix newTransform(oldTransform); - newTransform.translateRight(roundToInt(offset.width()), roundToInt(offset.height())); - - childLayer->m_transform = adoptPtr(new TransformationMatrix(newTransform)); - - LayerPaintingInfo localPaintingInfo(paintingInfo); - localPaintingInfo.paintDirtyRect = localDirtyRect; - childLayer->paintLayer(context, localPaintingInfo, paintFlags); - - if (oldHasTransform) - childLayer->m_transform = adoptPtr(new TransformationMatrix(oldTransform)); - else - childLayer->m_transform.clear(); - } else { - // Adjust the transform such that the renderer's upper left corner will paint at (0,0) in user space. - // This involves subtracting out the position of the layer in our current coordinate space. - LayoutPoint childOffset; - columnLayers[colIndex - 1]->convertToLayerCoords(paintingInfo.rootLayer, childOffset); - TransformationMatrix transform; - transform.translateRight(roundToInt(childOffset.x() + offset.width()), roundToInt(childOffset.y() + offset.height())); - - // Apply the transform. - context->concatCTM(transform.toAffineTransform()); - - // Now do a paint with the root layer shifted to be the next multicol block. - LayerPaintingInfo columnPaintingInfo(paintingInfo); - columnPaintingInfo.rootLayer = columnLayers[colIndex - 1]; - columnPaintingInfo.paintDirtyRect = transform.inverse().mapRect(localDirtyRect); - paintChildLayerIntoColumns(childLayer, context, columnPaintingInfo, paintFlags, columnLayers, colIndex - 1); - } - } - - // Move to the next position. - currLogicalTopOffset += blockDelta; + for (const auto& fragment : layerFragments) { + if (fragment.backgroundRect.isEmpty()) + continue; + clipToRect(context, localPaintingInfo, fragment.backgroundRect); + paintOverflowControls(context, roundedIntPoint(toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset)), + snappedIntRect(fragment.backgroundRect.rect()), true); + restoreClip(context, localPaintingInfo, fragment.backgroundRect); } } @@ -4639,20 +4886,21 @@ bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result) bool RenderLayer::hitTest(const HitTestRequest& request, const HitTestLocation& hitTestLocation, HitTestResult& result) { ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant()); + ASSERT(!renderer().view().needsLayout()); + + updateLayerListsIfNeeded(); - renderer().document().updateLayout(); - - LayoutRect hitTestArea = isOutOfFlowRenderFlowThread() ? toRenderFlowThread(&renderer())->visualOverflowRect() : renderer().view().documentRect(); + LayoutRect hitTestArea = isOutOfFlowRenderFlowThread() ? downcast<RenderFlowThread>(renderer()).visualOverflowRect() : renderer().view().documentRect(); if (!request.ignoreClipping()) hitTestArea.intersect(renderer().view().frameView().visibleContentRect(LegacyIOSDocumentVisibleRect)); - RenderLayer* insideLayer = hitTestLayer(this, 0, request, result, hitTestArea, hitTestLocation, false); + RenderLayer* insideLayer = hitTestLayer(this, nullptr, request, result, hitTestArea, hitTestLocation, false); if (!insideLayer) { // We didn't hit any layer. If we are the root layer and the mouse is -- or just was -- down, // return ourselves. We do this so mouse events continue getting delivered after a drag has // exited the WebView, and so hit testing over a scrollbar hits the content document. if (!request.isChildFrameHitTest() && (request.active() || request.release()) && isRootLayer()) { - renderer().updateHitTestResult(result, toRenderView(renderer()).flipForWritingMode(hitTestLocation.point())); + renderer().updateHitTestResult(result, downcast<RenderView>(renderer()).flipForWritingMode(hitTestLocation.point())); insideLayer = this; } } @@ -4673,8 +4921,7 @@ Element* RenderLayer::enclosingElement() const if (Element* e = r->element()) return e; } - ASSERT_NOT_REACHED(); - return 0; + return nullptr; } RenderLayer* RenderLayer::enclosingFlowThreadAncestor() const @@ -4683,7 +4930,7 @@ RenderLayer* RenderLayer::enclosingFlowThreadAncestor() const for (; curr && !curr->isRenderFlowThread(); curr = curr->parent()) { if (curr->isStackingContainer() && curr->isComposited()) { // We only adjust the position of the first level of layers. - return 0; + return nullptr; } } return curr; @@ -4691,7 +4938,7 @@ RenderLayer* RenderLayer::enclosingFlowThreadAncestor() const bool RenderLayer::isFlowThreadCollectingGraphicsLayersUnderRegions() const { - return renderer().isRenderFlowThread() && toRenderFlowThread(renderer()).collectsGraphicsLayersUnderRegions(); + return is<RenderFlowThread>(renderer()) && downcast<RenderFlowThread>(renderer()).collectsGraphicsLayersUnderRegions(); } // Compute the z-offset of the point in the transformState. @@ -4711,35 +4958,35 @@ static double computeZOffset(const HitTestingTransformState& transformState) return backmappedPoint.z(); } -PassRefPtr<HitTestingTransformState> RenderLayer::createLocalTransformState(RenderLayer* rootLayer, RenderLayer* containerLayer, +Ref<HitTestingTransformState> RenderLayer::createLocalTransformState(RenderLayer* rootLayer, RenderLayer* containerLayer, const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* containerTransformState, - const LayoutPoint& translationOffset) const + const LayoutSize& translationOffset) const { RefPtr<HitTestingTransformState> transformState; - LayoutPoint offset; + LayoutSize offset; if (containerTransformState) { // If we're already computing transform state, then it's relative to the container (which we know is non-null). transformState = HitTestingTransformState::create(*containerTransformState); - convertToLayerCoords(containerLayer, offset); + offset = offsetFromAncestor(containerLayer); } else { // If this is the first time we need to make transform state, then base it off of hitTestLocation, // which is relative to rootLayer. transformState = HitTestingTransformState::create(hitTestLocation.transformedPoint(), hitTestLocation.transformedRect(), FloatQuad(hitTestRect)); - convertToLayerCoords(rootLayer, offset); + offset = offsetFromAncestor(rootLayer); } - offset.moveBy(translationOffset); + offset += translationOffset; - RenderObject* containerRenderer = containerLayer ? &containerLayer->renderer() : 0; + RenderObject* containerRenderer = containerLayer ? &containerLayer->renderer() : nullptr; if (renderer().shouldUseTransformFromContainer(containerRenderer)) { TransformationMatrix containerTransform; - renderer().getTransformFromContainer(containerRenderer, toLayoutSize(offset), containerTransform); + renderer().getTransformFromContainer(containerRenderer, offset, containerTransform); transformState->applyTransform(containerTransform, HitTestingTransformState::AccumulateTransform); } else { - transformState->translate(offset.x(), offset.y(), HitTestingTransformState::AccumulateTransform); + transformState->translate(offset.width(), offset.height(), HitTestingTransformState::AccumulateTransform); } - return transformState; + return transformState.releaseNonNull(); } @@ -4747,6 +4994,11 @@ static bool isHitCandidate(const RenderLayer* hitLayer, bool canDepthSort, doubl { if (!hitLayer) return false; + + // RenderNamedFlowFragments are not hit candidates. The hit test algorithm will pick the parent + // layer, the one of the region. + if (hitLayer->renderer().isRenderNamedFlowFragment()) + return false; // The hit layer is depth-sorting with other layers, so just say that it was hit. if (canDepthSort) @@ -4776,22 +5028,22 @@ RenderLayer* RenderLayer::hitTestFixedLayersInNamedFlows(RenderLayer* /*rootLaye bool depthSortDescendants) { if (!isRootLayer()) - return 0; + return nullptr; // Get the named flows for the view if (!renderer().view().hasRenderNamedFlowThreads()) - return 0; + return nullptr; Vector<RenderLayer*> fixedLayers; renderer().view().flowThreadController().collectFixedPositionedLayers(fixedLayers); // Hit test the layers - RenderLayer* resultLayer = 0; + RenderLayer* resultLayer = nullptr; for (int i = fixedLayers.size() - 1; i >= 0; --i) { RenderLayer* fixedLayer = fixedLayers.at(i); HitTestResult tempResult(result.hitTestLocation()); - RenderLayer* hitLayer = fixedLayer->hitTestLayer(fixedLayer->renderer().flowThreadContainingBlock()->layer(), 0, request, tempResult, + RenderLayer* hitLayer = fixedLayer->hitTestLayer(fixedLayer->renderer().flowThreadContainingBlock()->layer(), nullptr, request, tempResult, hitTestRect, hitTestLocation, false, transformState, zOffsetForDescendants); // If it a rect-based test, we can safely append the temporary result since it might had hit @@ -4824,37 +5076,38 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont const HitTestingTransformState* transformState, double* zOffset) { if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant()) - return 0; + return nullptr; + + RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment(); // Prevent hitting the fixed layers inside the flow thread when hitting through regions. - if (renderer().fixedPositionedWithNamedFlowContainingBlock() && hitTestLocation.region()) - return 0; + if (renderer().fixedPositionedWithNamedFlowContainingBlock() && namedFlowFragment) + return nullptr; // Don't hit-test the layer if the renderer doesn't belong to this region. // This is true as long as we clamp the range of a box to its containing block range. // FIXME: Fix hit testing with in-flow threads included in out-of-flow threads. - if (hitTestLocation.region()) { - RenderFlowThread* flowThread = hitTestLocation.region()->flowThread(); - ASSERT(flowThread); - - if (!flowThread->objectShouldPaintInFlowRegion(&renderer(), hitTestLocation.region())) - return 0; + if (namedFlowFragment) { + ASSERT(namedFlowFragment->isValid()); + RenderFlowThread* flowThread = namedFlowFragment->flowThread(); + if (!flowThread->objectShouldFragmentInFlowRegion(&renderer(), namedFlowFragment)) + return nullptr; } // The natural thing would be to keep HitTestingTransformState on the stack, but it's big, so we heap-allocate. // Apply a transform if we have one. if (transform() && !appliedTransform) { - if (enclosingPaginationLayer()) + if (enclosingPaginationLayer(IncludeCompositedPaginatedLayers)) return hitTestTransformedLayerInFragments(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation, transformState, zOffset); // Make sure the parent's clip rects have been calculated. if (parent()) { - ClipRectsContext clipRectsContext(rootLayer, hitTestLocation.region(), RootRelativeClipRects, IncludeOverlayScrollbarSize); + ClipRectsContext clipRectsContext(rootLayer, RootRelativeClipRects, IncludeOverlayScrollbarSize); ClipRect clipRect = backgroundClipRect(clipRectsContext); - // Go ahead and test the enclosing clip now. + // Test the enclosing clip now. if (!clipRect.intersects(hitTestLocation)) - return 0; + return nullptr; } return hitTestLayerByApplyingTransform(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation, transformState, zOffset); @@ -4869,17 +5122,17 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont // We computed the correct state in the caller (above code), so just reference it. ASSERT(transformState); localTransformState = const_cast<HitTestingTransformState*>(transformState); - } else if (transformState || m_has3DTransformedDescendant || preserves3D()) { + } else if (transformState || has3DTransformedDescendant() || preserves3D()) { // We need transform state for the first time, or to offset the container state, so create it here. localTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestLocation, transformState); } // Check for hit test on backface if backface-visibility is 'hidden' if (localTransformState && renderer().style().backfaceVisibility() == BackfaceVisibilityHidden) { - TransformationMatrix invertedMatrix = localTransformState->m_accumulatedTransform.inverse(); + std::optional<TransformationMatrix> invertedMatrix = localTransformState->m_accumulatedTransform.inverse(); // If the z-vector of the matrix is negative, the back is facing towards the viewer. - if (invertedMatrix.m33() < 0) - return 0; + if (invertedMatrix && invertedMatrix.value().m33() < 0) + return nullptr; } RefPtr<HitTestingTransformState> unflattenedTransformState = localTransformState; @@ -4893,8 +5146,8 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont // The following are used for keeping track of the z-depth of the hit point of 3d-transformed // descendants. double localZOffset = -std::numeric_limits<double>::infinity(); - double* zOffsetForDescendantsPtr = 0; - double* zOffsetForContentsPtr = 0; + double* zOffsetForDescendantsPtr = nullptr; + double* zOffsetForContentsPtr = nullptr; bool depthSortDescendants = false; if (preserves3D()) { @@ -4903,13 +5156,16 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset; zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset; } else if (zOffset) { - zOffsetForDescendantsPtr = 0; + zOffsetForDescendantsPtr = nullptr; // Container needs us to give back a z offset for the hit layer. zOffsetForContentsPtr = zOffset; } // This variable tracks which layer the mouse ends up being inside. - RenderLayer* candidateLayer = 0; + RenderLayer* candidateLayer = nullptr; +#if !ASSERT_DISABLED + LayerListMutationDetector mutationChecker(this); +#endif // Check the fixed positioned layers within flow threads that are positioned by the view. RenderLayer* hitLayer = hitTestFixedLayersInNamedFlows(rootLayer, request, result, hitTestRect, hitTestLocation, @@ -4938,22 +5194,24 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont candidateLayer = hitLayer; } - hitLayer = hitTestFlowThreadIfRegion(rootLayer, request, result, hitTestRect, hitTestLocation, localTransformState.get(), zOffsetForDescendantsPtr); - if (hitLayer) { - if (!depthSortDescendants) - return hitLayer; - candidateLayer = hitLayer; - } - // Collect the fragments. This will compute the clip rectangles for each layer fragment. LayerFragments layerFragments; - collectFragments(layerFragments, rootLayer, hitTestLocation.region(), hitTestRect, RootRelativeClipRects, IncludeOverlayScrollbarSize); + collectFragments(layerFragments, rootLayer, hitTestRect, IncludeCompositedPaginatedLayers, RootRelativeClipRects, IncludeOverlayScrollbarSize, RespectOverflowClip, + offsetFromAncestor(rootLayer)); if (canResize() && hitTestResizerInFragments(layerFragments, hitTestLocation)) { renderer().updateHitTestResult(result, hitTestLocation.point()); return this; } + hitLayer = hitTestFlowThreadIfRegionForFragments(layerFragments, rootLayer, request, result, hitTestRect, hitTestLocation, + localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants); + if (hitLayer) { + if (!depthSortDescendants) + return hitLayer; + candidateLayer = hitLayer; + } + // Next we want to see if the mouse pos is inside the child RenderObjects of the layer. Check // every fragment in reverse order. if (isSelfPaintingLayer()) { @@ -5002,7 +5260,7 @@ RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* cont result.append(tempResult); } - return 0; + return nullptr; } bool RenderLayer::hitTestContentsForFragments(const LayerFragments& layerFragments, const HitTestRequest& request, HitTestResult& result, @@ -5031,7 +5289,7 @@ bool RenderLayer::hitTestResizerInFragments(const LayerFragments& layerFragments for (int i = layerFragments.size() - 1; i >= 0; --i) { const LayerFragment& fragment = layerFragments.at(i); - if (fragment.backgroundRect.intersects(hitTestLocation) && resizerCornerRect(this, pixelSnappedIntRect(fragment.layerBounds)).contains(hitTestLocation.roundedPoint())) + if (fragment.backgroundRect.intersects(hitTestLocation) && resizerCornerRect(*this, snappedIntRect(fragment.layerBounds)).contains(hitTestLocation.roundedPoint())) return true; } @@ -5042,10 +5300,11 @@ RenderLayer* RenderLayer::hitTestTransformedLayerInFragments(RenderLayer* rootLa const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset) { LayerFragments enclosingPaginationFragments; - LayoutPoint offsetOfPaginationLayerFromRoot; - LayoutRect transformedExtent = transparencyClipBox(this, enclosingPaginationLayer(), HitTestingTransparencyClipBox, RootOfTransparencyClipBox); - enclosingPaginationLayer()->collectFragments(enclosingPaginationFragments, rootLayer, hitTestLocation.region(), hitTestRect, - RootRelativeClipRects, IncludeOverlayScrollbarSize, RespectOverflowClip, &offsetOfPaginationLayerFromRoot, &transformedExtent); + LayoutSize offsetOfPaginationLayerFromRoot; + RenderLayer* paginatedLayer = enclosingPaginationLayer(IncludeCompositedPaginatedLayers); + LayoutRect transformedExtent = transparencyClipBox(*this, paginatedLayer, HitTestingTransparencyClipBox, RootOfTransparencyClipBox); + paginatedLayer->collectFragments(enclosingPaginationFragments, rootLayer, hitTestRect, IncludeCompositedPaginatedLayers, + RootRelativeClipRects, IncludeOverlayScrollbarSize, RespectOverflowClip, offsetOfPaginationLayerFromRoot, &transformedExtent); for (int i = enclosingPaginationFragments.size() - 1; i >= 0; --i) { const LayerFragment& fragment = enclosingPaginationFragments.at(i); @@ -5055,12 +5314,12 @@ RenderLayer* RenderLayer::hitTestTransformedLayerInFragments(RenderLayer* rootLa LayoutRect clipRect = fragment.backgroundRect.rect(); // Now compute the clips within a given fragment - if (parent() != enclosingPaginationLayer()) { - enclosingPaginationLayer()->convertToLayerCoords(rootLayer, offsetOfPaginationLayerFromRoot); + if (parent() != paginatedLayer) { + offsetOfPaginationLayerFromRoot = toLayoutSize(paginatedLayer->convertToLayerCoords(rootLayer, toLayoutPoint(offsetOfPaginationLayerFromRoot))); - ClipRectsContext clipRectsContext(enclosingPaginationLayer(), hitTestLocation.region(), RootRelativeClipRects, IncludeOverlayScrollbarSize); + ClipRectsContext clipRectsContext(paginatedLayer, RootRelativeClipRects, IncludeOverlayScrollbarSize); LayoutRect parentClipRect = backgroundClipRect(clipRectsContext).rect(); - parentClipRect.moveBy(fragment.paginationOffset + offsetOfPaginationLayerFromRoot); + parentClipRect.move(fragment.paginationOffset + offsetOfPaginationLayerFromRoot); clipRect.intersect(parentClipRect); } @@ -5073,19 +5332,19 @@ RenderLayer* RenderLayer::hitTestTransformedLayerInFragments(RenderLayer* rootLa return hitLayer; } - return 0; + return nullptr; } RenderLayer* RenderLayer::hitTestLayerByApplyingTransform(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result, const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset, - const LayoutPoint& translationOffset) + const LayoutSize& translationOffset) { // Create a transform state to accumulate this transform. - RefPtr<HitTestingTransformState> newTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestLocation, transformState, translationOffset); + Ref<HitTestingTransformState> newTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestLocation, transformState, translationOffset); // If the transform can't be inverted, then don't hit test this layer at all. if (!newTransformState->m_accumulatedTransform.isInvertible()) - return 0; + return nullptr; // Compute the point and the hit test rect in the coords of this layer by using the values // from the transformState, which store the point and quad in the coords of the last flattened @@ -5103,7 +5362,7 @@ RenderLayer* RenderLayer::hitTestLayerByApplyingTransform(RenderLayer* rootLayer newHitTestLocation = HitTestLocation(localPoint); // Now do a hit test with the root layer shifted to be us. - return hitTestLayer(this, containerLayer, request, result, localHitTestRect, newHitTestLocation, true, newTransformState.get(), zOffset); + return hitTestLayer(this, containerLayer, request, result, localHitTestRect, newHitTestLocation, true, newTransformState.ptr(), zOffset); } bool RenderLayer::hitTestContents(const HitTestRequest& request, HitTestResult& result, const LayoutRect& layerBounds, const HitTestLocation& hitTestLocation, HitTestFilter hitTestFilter) const @@ -5122,6 +5381,13 @@ bool RenderLayer::hitTestContents(const HitTestRequest& request, HitTestResult& // the content in the layer has an element. So just walk up // the tree. if (!result.innerNode() || !result.innerNonSharedNode()) { + if (isOutOfFlowRenderFlowThread()) { + // The flowthread doesn't have an enclosing element, so when hitting the layer of the + // flowthread (e.g. the descent area of the RootInlineBox for the image flowed alone + // inside the flow thread) we're letting the hit testing continue so it will hit the region. + return false; + } + Element* e = enclosingElement(); if (!result.innerNode()) result.setInnerNode(e); @@ -5141,22 +5407,19 @@ RenderLayer* RenderLayer::hitTestList(Vector<RenderLayer*>* list, RenderLayer* r bool depthSortDescendants) { if (!list) - return 0; + return nullptr; if (!hasSelfPaintingLayerDescendant()) - return 0; + return nullptr; - RenderLayer* resultLayer = 0; - for (int i = list->size() - 1; i >= 0; --i) { - RenderLayer* childLayer = list->at(i); + RenderLayer* resultLayer = nullptr; + for (size_t i = list->size(); i > 0; --i) { + RenderLayer* childLayer = list->at(i - 1); if (childLayer->isFlowThreadCollectingGraphicsLayersUnderRegions()) continue; - RenderLayer* hitLayer = 0; + RenderLayer* hitLayer = nullptr; HitTestResult tempResult(result.hitTestLocation()); - if (childLayer->isPaginated()) - hitLayer = hitTestPaginatedChildLayer(childLayer, rootLayer, request, tempResult, hitTestRect, hitTestLocation, transformState, zOffsetForDescendants); - else - hitLayer = childLayer->hitTestLayer(rootLayer, this, request, tempResult, hitTestRect, hitTestLocation, false, transformState, zOffsetForDescendants); + hitLayer = childLayer->hitTestLayer(rootLayer, this, request, tempResult, hitTestRect, hitTestLocation, false, transformState, zOffsetForDescendants); // If it a rect-based test, we can safely append the temporary result since it might had hit // nodes but not necesserily had hitLayer set. @@ -5175,158 +5438,67 @@ RenderLayer* RenderLayer::hitTestList(Vector<RenderLayer*>* list, RenderLayer* r return resultLayer; } -RenderLayer* RenderLayer::hitTestPaginatedChildLayer(RenderLayer* childLayer, RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, - const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset) -{ - Vector<RenderLayer*> columnLayers; - RenderLayer* ancestorLayer = isNormalFlowOnly() ? parent() : stackingContainer(); - for (RenderLayer* curr = childLayer->parent(); curr; curr = curr->parent()) { - if (curr->renderer().hasColumns() && checkContainingBlockChainForPagination(&childLayer->renderer(), curr->renderBox())) - columnLayers.append(curr); - if (curr == ancestorLayer) - break; - } - - ASSERT(columnLayers.size()); - return hitTestChildLayerColumns(childLayer, rootLayer, request, result, hitTestRect, hitTestLocation, transformState, zOffset, - columnLayers, columnLayers.size() - 1); -} - -RenderLayer* RenderLayer::hitTestChildLayerColumns(RenderLayer* childLayer, RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, - const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset, - const Vector<RenderLayer*>& columnLayers, size_t columnIndex) -{ - RenderBlock& columnBlock = toRenderBlock(columnLayers[columnIndex]->renderer()); - - ASSERT(columnBlock.hasColumns()); - if (!columnBlock.hasColumns()) - return 0; - - LayoutPoint layerOffset; - columnBlock.layer()->convertToLayerCoords(rootLayer, layerOffset); - - ColumnInfo* colInfo = columnBlock.columnInfo(); - int colCount = columnBlock.columnCount(colInfo); - - // We have to go backwards from the last column to the first. - bool isHorizontal = columnBlock.style().isHorizontalWritingMode(); - LayoutUnit logicalLeft = columnBlock.logicalLeftOffsetForContent(); - LayoutUnit currLogicalTopOffset = columnBlock.initialBlockOffsetForPainting(); - LayoutUnit blockDelta = columnBlock.blockDeltaForPaintingNextColumn(); - currLogicalTopOffset += colCount * blockDelta; - for (int i = colCount - 1; i >= 0; i--) { - // For each rect, we clip to the rect, and then we adjust our coords. - LayoutRect colRect = columnBlock.columnRectAt(colInfo, i); - columnBlock.flipForWritingMode(colRect); - LayoutUnit currLogicalLeftOffset = (isHorizontal ? colRect.x() : colRect.y()) - logicalLeft; - currLogicalTopOffset -= blockDelta; - - LayoutSize offset = isHorizontal ? LayoutSize(currLogicalLeftOffset, currLogicalTopOffset) : LayoutSize(currLogicalTopOffset, currLogicalLeftOffset); - - colRect.moveBy(layerOffset); - - LayoutRect localClipRect(hitTestRect); - localClipRect.intersect(colRect); - - if (!localClipRect.isEmpty() && hitTestLocation.intersects(localClipRect)) { - RenderLayer* hitLayer = 0; - if (!columnIndex) { - // Apply a translation transform to change where the layer paints. - TransformationMatrix oldTransform; - bool oldHasTransform = childLayer->transform(); - if (oldHasTransform) - oldTransform = *childLayer->transform(); - TransformationMatrix newTransform(oldTransform); - newTransform.translateRight(offset.width(), offset.height()); - - childLayer->m_transform = adoptPtr(new TransformationMatrix(newTransform)); - hitLayer = childLayer->hitTestLayer(rootLayer, columnLayers[0], request, result, localClipRect, hitTestLocation, false, transformState, zOffset); - if (oldHasTransform) - childLayer->m_transform = adoptPtr(new TransformationMatrix(oldTransform)); - else - childLayer->m_transform.clear(); - } else { - // Adjust the transform such that the renderer's upper left corner will be at (0,0) in user space. - // This involves subtracting out the position of the layer in our current coordinate space. - RenderLayer* nextLayer = columnLayers[columnIndex - 1]; - RefPtr<HitTestingTransformState> newTransformState = nextLayer->createLocalTransformState(rootLayer, nextLayer, localClipRect, hitTestLocation, transformState); - newTransformState->translate(offset.width(), offset.height(), HitTestingTransformState::AccumulateTransform); - FloatPoint localPoint = newTransformState->mappedPoint(); - FloatQuad localPointQuad = newTransformState->mappedQuad(); - LayoutRect localHitTestRect = newTransformState->mappedArea().enclosingBoundingBox(); - HitTestLocation newHitTestLocation; - if (hitTestLocation.isRectBasedTest()) - newHitTestLocation = HitTestLocation(localPoint, localPointQuad); - else - newHitTestLocation = HitTestLocation(localPoint); - newTransformState->flatten(); - - hitLayer = hitTestChildLayerColumns(childLayer, columnLayers[columnIndex - 1], request, result, localHitTestRect, newHitTestLocation, - newTransformState.get(), zOffset, columnLayers, columnIndex - 1); - } - - if (hitLayer) - return hitLayer; - } - } - - return 0; -} - -void RenderLayer::updateClipRects(const ClipRectsContext& clipRectsContext) +Ref<ClipRects> RenderLayer::updateClipRects(const ClipRectsContext& clipRectsContext) { ClipRectsType clipRectsType = clipRectsContext.clipRectsType; ASSERT(clipRectsType < NumCachedClipRectsTypes); - if (m_clipRectsCache && m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip)) { - ASSERT(clipRectsContext.rootLayer == m_clipRectsCache->m_clipRectsRoot[clipRectsType]); - ASSERT(m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] == clipRectsContext.overlayScrollbarSizeRelevancy); + if (m_clipRectsCache) { + if (auto* clipRects = m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip)) { + ASSERT(clipRectsContext.rootLayer == m_clipRectsCache->m_clipRectsRoot[clipRectsType]); + ASSERT(m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] == clipRectsContext.overlayScrollbarSizeRelevancy); #ifdef CHECK_CACHED_CLIP_RECTS - // This code is useful to check cached clip rects, but is too expensive to leave enabled in debug builds by default. - ClipRectsContext tempContext(clipRectsContext); - tempContext.clipRectsType = TemporaryClipRects; - ClipRects clipRects; - calculateClipRects(tempContext, clipRects); - ASSERT(clipRects == *m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip).get()); + // This code is useful to check cached clip rects, but is too expensive to leave enabled in debug builds by default. + ClipRectsContext tempContext(clipRectsContext); + tempContext.clipRectsType = TemporaryClipRects; + Ref<ClipRects> tempClipRects = ClipRects::create(); + calculateClipRects(tempContext, tempClipRects); + ASSERT(tempClipRects.get() == *clipRects); #endif - return; // We have the correct cached value. + return *clipRects; // We have the correct cached value. + } } - // For transformed layers, the root layer was shifted to be us, so there is no need to - // examine the parent. We want to cache clip rects with us as the root. - RenderLayer* parentLayer = clipRectsContext.rootLayer != this ? parent() : 0; - if (parentLayer) - parentLayer->updateClipRects(clipRectsContext); - - ClipRects clipRects; - calculateClipRects(clipRectsContext, clipRects); - if (!m_clipRectsCache) - m_clipRectsCache = adoptPtr(new ClipRectsCache); - - if (parentLayer && parentLayer->clipRects(clipRectsContext) && clipRects == *parentLayer->clipRects(clipRectsContext)) - m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, parentLayer->clipRects(clipRectsContext)); - else - m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, ClipRects::create(clipRects)); - + m_clipRectsCache = std::make_unique<ClipRectsCache>(); #ifndef NDEBUG m_clipRectsCache->m_clipRectsRoot[clipRectsType] = clipRectsContext.rootLayer; m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] = clipRectsContext.overlayScrollbarSizeRelevancy; #endif + + RefPtr<ClipRects> parentClipRects; + // For transformed layers, the root layer was shifted to be us, so there is no need to + // examine the parent. We want to cache clip rects with us as the root. + if (auto* parentLayer = (clipRectsContext.rootLayer != this ? parent() : nullptr)) + parentClipRects = parentLayer->updateClipRects(clipRectsContext); + + auto clipRects = ClipRects::create(); + calculateClipRects(clipRectsContext, clipRects); + + if (parentClipRects && *parentClipRects == clipRects) { + m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, parentClipRects.copyRef()); + return parentClipRects.releaseNonNull(); + } + m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, clipRects.copyRef()); + return clipRects; } -void RenderLayer::mapLayerClipRectsToFragmentationLayer(RenderRegion* region, ClipRects& clipRects) const +bool RenderLayer::mapLayerClipRectsToFragmentationLayer(ClipRects& clipRects) const { - ASSERT(region && region->parent() && region->parent()->isRenderNamedFlowFragmentContainer()); + RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment(); + if (!namedFlowFragment) + return false; - ClipRectsContext targetClipRectsContext(region->regionContainerLayer(), 0, TemporaryClipRects); - region->regionContainerLayer()->calculateClipRects(targetClipRectsContext, clipRects); + ASSERT(namedFlowFragment->parent() && namedFlowFragment->parent()->isRenderNamedFlowFragmentContainer()); - LayoutRect flowThreadPortionRect = region->flowThreadPortionRect(); + ClipRectsContext targetClipRectsContext(&namedFlowFragment->fragmentContainerLayer(), TemporaryClipRects); + namedFlowFragment->fragmentContainerLayer().calculateClipRects(targetClipRectsContext, clipRects); + + LayoutRect flowThreadPortionRect = namedFlowFragment->flowThreadPortionRect(); LayoutPoint portionLocation = flowThreadPortionRect.location(); - LayoutRect regionContentBox = region->contentBoxRect(); - LayoutSize moveOffset = portionLocation - regionContentBox.location(); + LayoutRect regionContentBox = namedFlowFragment->fragmentContainer().contentBoxRect(); + LayoutSize moveOffset = portionLocation - regionContentBox.location() + toLayoutSize(namedFlowFragment->fragmentContainer().scrollPosition()); ClipRect newOverflowClipRect = clipRects.overflowClipRect(); newOverflowClipRect.move(moveOffset); @@ -5339,27 +5511,35 @@ void RenderLayer::mapLayerClipRectsToFragmentationLayer(RenderRegion* region, Cl ClipRect newPosClipRect = clipRects.posClipRect(); newPosClipRect.move(moveOffset); clipRects.setPosClipRect(newPosClipRect); + + return true; +} + +ClipRects* RenderLayer::clipRects(const ClipRectsContext& context) const +{ + ASSERT(context.clipRectsType < NumCachedClipRectsTypes); + if (!m_clipRectsCache) + return nullptr; + return m_clipRectsCache->getClipRects(context.clipRectsType, context.respectOverflowClip); } void RenderLayer::calculateClipRects(const ClipRectsContext& clipRectsContext, ClipRects& clipRects) const { if (!parent()) { // The root layer's clip rect is always infinite. - clipRects.reset(LayoutRect::infiniteRect()); + clipRects.reset(); return; } ClipRectsType clipRectsType = clipRectsContext.clipRectsType; bool useCached = clipRectsType != TemporaryClipRects; - if (renderer().isRenderFlowThread() && clipRectsContext.region) { - mapLayerClipRectsToFragmentationLayer(clipRectsContext.region, clipRects); + if (renderer().isRenderNamedFlowThread() && mapLayerClipRectsToFragmentationLayer(clipRects)) return; - } // For transformed layers, the root layer was shifted to be us, so there is no need to // examine the parent. We want to cache clip rects with us as the root. - RenderLayer* parentLayer = clipRectsContext.rootLayer != this ? parent() : 0; + RenderLayer* parentLayer = clipRectsContext.rootLayer != this ? parent() : nullptr; // Ensure that our parent's clip has been calculated so that we can examine the values. if (parentLayer) { @@ -5371,7 +5551,7 @@ void RenderLayer::calculateClipRects(const ClipRectsContext& clipRectsContext, C parentLayer->calculateClipRects(parentContext, clipRects); } } else - clipRects.reset(LayoutRect::infiniteRect()); + clipRects.reset(); // A fixed object is essentially the root of its containing block hierarchy, so when // we encounter such an object, we reset our clip rects to the fixedClipRect. @@ -5395,21 +5575,19 @@ void RenderLayer::calculateClipRects(const ClipRectsContext& clipRectsContext, C // This offset cannot use convertToLayerCoords, because sometimes our rootLayer may be across // some transformed layer boundary, for example, in the RenderLayerCompositor overlapMap, where // clipRects are needed in view space. - LayoutPoint offset; - offset = roundedLayoutPoint(renderer().localToContainerPoint(FloatPoint(), &clipRectsContext.rootLayer->renderer())); + LayoutPoint offset(renderer().localToContainerPoint(FloatPoint(), &clipRectsContext.rootLayer->renderer())); if (clipRects.fixed() && &clipRectsContext.rootLayer->renderer() == &renderer().view()) - offset -= renderer().view().frameView().scrollOffsetForFixedPosition(); + offset -= toLayoutSize(renderer().view().frameView().scrollPositionForFixedPosition()); if (renderer().hasOverflowClip()) { - ClipRect newOverflowClip = toRenderBox(renderer()).overflowClipRectForChildLayers(offset, clipRectsContext.region, clipRectsContext.overlayScrollbarSizeRelevancy); - if (renderer().style().hasBorderRadius()) - newOverflowClip.setHasRadius(true); + ClipRect newOverflowClip = downcast<RenderBox>(renderer()).overflowClipRectForChildLayers(offset, currentRenderNamedFlowFragment(), clipRectsContext.overlayScrollbarSizeRelevancy); + newOverflowClip.setAffectedByRadius(renderer().style().hasBorderRadius()); clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect())); if (renderer().isPositioned()) clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect())); } if (renderer().hasClip()) { - LayoutRect newPosClip = toRenderBox(renderer()).clipRect(offset, clipRectsContext.region); + LayoutRect newPosClip = downcast<RenderBox>(renderer()).clipRect(offset, currentRenderNamedFlowFragment()); clipRects.setPosClipRect(intersection(newPosClip, clipRects.posClipRect())); clipRects.setOverflowClipRect(intersection(newPosClip, clipRects.overflowClipRect())); clipRects.setFixedClipRect(intersection(newPosClip, clipRects.fixedClipRect())); @@ -5417,21 +5595,22 @@ void RenderLayer::calculateClipRects(const ClipRectsContext& clipRectsContext, C } } -void RenderLayer::parentClipRects(const ClipRectsContext& clipRectsContext, ClipRects& clipRects) const +Ref<ClipRects> RenderLayer::parentClipRects(const ClipRectsContext& clipRectsContext) const { ASSERT(parent()); - if (renderer().isRenderFlowThread() && clipRectsContext.region) { - mapLayerClipRectsToFragmentationLayer(clipRectsContext.region, clipRects); - return; + if (renderer().isRenderNamedFlowThread()) { + auto parentClipRects = ClipRects::create(); + if (mapLayerClipRectsToFragmentationLayer(parentClipRects)) + return parentClipRects; } if (clipRectsContext.clipRectsType == TemporaryClipRects) { - parent()->calculateClipRects(clipRectsContext, clipRects); - return; + auto parentClipRects = ClipRects::create(); + parent()->calculateClipRects(clipRectsContext, parentClipRects); + return parentClipRects; } - parent()->updateClipRects(clipRectsContext); - clipRects = *parent()->clipRects(clipRectsContext); + return parent()->updateClipRects(clipRectsContext); } static inline ClipRect backgroundClipRectForPosition(const ClipRects& parentRects, EPosition position) @@ -5448,30 +5627,29 @@ static inline ClipRect backgroundClipRectForPosition(const ClipRects& parentRect ClipRect RenderLayer::backgroundClipRect(const ClipRectsContext& clipRectsContext) const { ASSERT(parent()); + auto computeParentRects = [this, &clipRectsContext] () { + // If we cross into a different pagination context, then we can't rely on the cache. + // Just switch over to using TemporaryClipRects. + if (clipRectsContext.clipRectsType != TemporaryClipRects + && parent()->enclosingPaginationLayer(IncludeCompositedPaginatedLayers) != enclosingPaginationLayer(IncludeCompositedPaginatedLayers)) { + ClipRectsContext tempContext(clipRectsContext); + tempContext.clipRectsType = TemporaryClipRects; + return parentClipRects(tempContext); + } + return parentClipRects(clipRectsContext); + }; - ClipRects parentRects; - - // If we cross into a different pagination context, then we can't rely on the cache. - // Just switch over to using TemporaryClipRects. - if (clipRectsContext.clipRectsType != TemporaryClipRects && parent()->enclosingPaginationLayer() != enclosingPaginationLayer()) { - ClipRectsContext tempContext(clipRectsContext); - tempContext.clipRectsType = TemporaryClipRects; - parentClipRects(tempContext, parentRects); - } else - parentClipRects(clipRectsContext, parentRects); - + auto parentRects = computeParentRects(); ClipRect backgroundClipRect = backgroundClipRectForPosition(parentRects, renderer().style().position()); RenderView& view = renderer().view(); - // Note: infinite clipRects should not be scrolled here, otherwise they will accidentally no longer be considered infinite. - if (parentRects.fixed() && &clipRectsContext.rootLayer->renderer() == &view && backgroundClipRect != LayoutRect::infiniteRect()) - backgroundClipRect.move(view.frameView().scrollOffsetForFixedPosition()); - + if (parentRects->fixed() && &clipRectsContext.rootLayer->renderer() == &view && !backgroundClipRect.isInfinite()) + backgroundClipRect.moveBy(view.frameView().scrollPositionForFixedPosition()); return backgroundClipRect; } void RenderLayer::calculateRects(const ClipRectsContext& clipRectsContext, const LayoutRect& paintDirtyRect, LayoutRect& layerBounds, - ClipRect& backgroundRect, ClipRect& foregroundRect, ClipRect& outlineRect, const LayoutPoint* offsetFromRoot) const + ClipRect& backgroundRect, ClipRect& foregroundRect, const LayoutSize& offsetFromRoot) const { if (clipRectsContext.rootLayer != this && parent()) { backgroundRect = backgroundClipRect(clipRectsContext); @@ -5479,28 +5657,24 @@ void RenderLayer::calculateRects(const ClipRectsContext& clipRectsContext, const } else backgroundRect = paintDirtyRect; - LayoutPoint offset; - if (offsetFromRoot) - offset = *offsetFromRoot; - else - convertToLayerCoords(clipRectsContext.rootLayer, offset); - + LayoutSize offsetFromRootLocal = offsetFromRoot; + RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment(); // If the view is scrolled, the flow thread is not scrolled with it and we should // take the scroll offset into account. - if (clipRectsContext.rootLayer->isOutOfFlowRenderFlowThread() && !clipRectsContext.region) { - FloatPoint absPos = renderer().view().localToAbsolute(FloatPoint(), IsFixed); - offset += LayoutSize(absPos.x(), absPos.y()); + if (clipRectsContext.rootLayer->isOutOfFlowRenderFlowThread() && !namedFlowFragment) { + LayoutPoint absPos = LayoutPoint(renderer().view().localToAbsolute(FloatPoint(), IsFixed)); + offsetFromRootLocal += toLayoutSize(absPos); } - layerBounds = LayoutRect(offset, size()); + layerBounds = LayoutRect(toLayoutPoint(offsetFromRootLocal), size()); foregroundRect = backgroundRect; - outlineRect = backgroundRect; - RenderFlowThread* flowThread = clipRectsContext.region ? clipRectsContext.region->flowThread() : 0; + RenderFlowThread* flowThread = namedFlowFragment ? namedFlowFragment->flowThread() : nullptr; if (isSelfPaintingLayer() && flowThread && !renderer().isInFlowRenderFlowThread()) { - const RenderBoxModelObject& boxModelObject = toRenderBoxModelObject(renderer()); - LayoutRect layerBoundsWithVisualOverflow = clipRectsContext.region->visualOverflowRectForBox(&boxModelObject); + ASSERT(namedFlowFragment->isValid()); + const RenderBoxModelObject& boxModelObject = downcast<RenderBoxModelObject>(renderer()); + LayoutRect layerBoundsWithVisualOverflow = namedFlowFragment->visualOverflowRectForBox(boxModelObject); // Layers are in physical coordinates so the origin must be moved to the physical top-left of the flowthread. if (&boxModelObject == flowThread && flowThread->style().isFlippedBlocksWritingMode()) { @@ -5509,59 +5683,63 @@ void RenderLayer::calculateRects(const ClipRectsContext& clipRectsContext, const else layerBoundsWithVisualOverflow.moveBy(LayoutPoint(flowThread->width(), 0)); } else { - RenderBlock* rendererContainingBlock = boxModelObject.enclosingBox()->isRenderBlock() ? toRenderBlock(boxModelObject.enclosingBox()) : 0; + RenderBlock* rendererContainingBlock = is<RenderBlock>(boxModelObject.enclosingBox()) ? &downcast<RenderBlock>(boxModelObject.enclosingBox()) : nullptr; if (rendererContainingBlock) rendererContainingBlock->flipForWritingMode(layerBoundsWithVisualOverflow); } - layerBoundsWithVisualOverflow.moveBy(offset); + layerBoundsWithVisualOverflow.move(offsetFromRootLocal); backgroundRect.intersect(layerBoundsWithVisualOverflow); foregroundRect = backgroundRect; - outlineRect = backgroundRect; - - // If the region does not clip its overflow, inflate the outline rect. - if (!(clipRectsContext.region->parent()->hasOverflowClip() && (clipRectsContext.region->regionContainerLayer() != clipRectsContext.rootLayer || clipRectsContext.respectOverflowClip == RespectOverflowClip))) - outlineRect.inflate(renderer().maximalOutlineSize(PaintPhaseOutline)); } // Update the clip rects that will be passed to child layers. if (renderer().hasClipOrOverflowClip()) { // This layer establishes a clip of some kind. if (renderer().hasOverflowClip() && (this != clipRectsContext.rootLayer || clipRectsContext.respectOverflowClip == RespectOverflowClip)) { - foregroundRect.intersect(toRenderBox(renderer()).overflowClipRect(offset, clipRectsContext.region, clipRectsContext.overlayScrollbarSizeRelevancy)); + foregroundRect.intersect(downcast<RenderBox>(renderer()).overflowClipRect(toLayoutPoint(offsetFromRootLocal), namedFlowFragment, clipRectsContext.overlayScrollbarSizeRelevancy)); if (renderer().style().hasBorderRadius()) - foregroundRect.setHasRadius(true); + foregroundRect.setAffectedByRadius(true); } if (renderer().hasClip()) { - // Clip applies to *us* as well, so go ahead and update the damageRect. - LayoutRect newPosClip = toRenderBox(renderer()).clipRect(offset, clipRectsContext.region); + // Clip applies to *us* as well, so update the damageRect. + LayoutRect newPosClip = downcast<RenderBox>(renderer()).clipRect(toLayoutPoint(offsetFromRootLocal), namedFlowFragment); backgroundRect.intersect(newPosClip); foregroundRect.intersect(newPosClip); - outlineRect.intersect(newPosClip); } - // If we establish a clip at all, then go ahead and make sure our background - // rect is intersected with our layer's bounds including our visual overflow, + // If we establish a clip at all, then make sure our background rect is intersected with our layer's bounds including our visual overflow, // since any visual overflow like box-shadow or border-outset is not clipped by overflow:auto/hidden. if (renderBox()->hasVisualOverflow()) { // FIXME: Does not do the right thing with CSS regions yet, since we don't yet factor in the // individual region boxes as overflow. - LayoutRect layerBoundsWithVisualOverflow = clipRectsContext.region ? clipRectsContext.region->visualOverflowRectForBox(renderBox()) : renderBox()->visualOverflowRect(); + LayoutRect layerBoundsWithVisualOverflow = namedFlowFragment ? namedFlowFragment->visualOverflowRectForBox(*renderBox()) : renderBox()->visualOverflowRect(); renderBox()->flipForWritingMode(layerBoundsWithVisualOverflow); // Layers are in physical coordinates, so the overflow has to be flipped. - layerBoundsWithVisualOverflow.moveBy(offset); + layerBoundsWithVisualOverflow.move(offsetFromRootLocal); if (this != clipRectsContext.rootLayer || clipRectsContext.respectOverflowClip == RespectOverflowClip) backgroundRect.intersect(layerBoundsWithVisualOverflow); } else { // Shift the bounds to be for our region only. - LayoutRect bounds = renderBox()->borderBoxRectInRegion(clipRectsContext.region); - if (clipRectsContext.region) - bounds = clipRectsContext.region->rectFlowPortionForBox(renderBox(), bounds); + LayoutRect bounds = renderBox()->borderBoxRectInRegion(namedFlowFragment); + if (namedFlowFragment) + bounds = namedFlowFragment->rectFlowPortionForBox(renderBox(), bounds); - bounds.moveBy(offset); + bounds.move(offsetFromRootLocal); if (this != clipRectsContext.rootLayer || clipRectsContext.respectOverflowClip == RespectOverflowClip) backgroundRect.intersect(bounds); + // Boxes inside flow threads don't have their logical left computed to avoid + // floats. Instead, that information is kept in their RenderBoxRegionInfo structure. + // As such, the layer bounds must be enlarged to encompass their background rect + // to ensure intersecting them won't result in an empty rect, which would eventually + // cause paint rejection. + if (flowThread && flowThread->isRenderNamedFlowThread()) { + if (flowThread->style().isHorizontalWritingMode()) + layerBounds.shiftMaxXEdgeTo(std::max(layerBounds.maxX(), backgroundRect.rect().maxX())); + else + layerBounds.shiftMaxYEdgeTo(std::max(layerBounds.maxY(), backgroundRect.rect().maxY())); + } } } } @@ -5572,43 +5750,52 @@ LayoutRect RenderLayer::childrenClipRect() const // FIXME: Regions not accounted for. RenderLayer* clippingRootLayer = clippingRootForPainting(); LayoutRect layerBounds; - ClipRect backgroundRect, foregroundRect, outlineRect; - ClipRectsContext clipRectsContext(clippingRootLayer, 0, TemporaryClipRects); + ClipRect backgroundRect; + ClipRect foregroundRect; + ClipRectsContext clipRectsContext(clippingRootLayer, TemporaryClipRects); // Need to use temporary clip rects, because the value of 'dontClipToOverflow' may be different from the painting path (<rdar://problem/11844909>). - calculateRects(clipRectsContext, renderer().view().unscaledDocumentRect(), layerBounds, backgroundRect, foregroundRect, outlineRect); + calculateRects(clipRectsContext, renderer().view().unscaledDocumentRect(), layerBounds, backgroundRect, foregroundRect, offsetFromAncestor(clipRectsContext.rootLayer)); return clippingRootLayer->renderer().localToAbsoluteQuad(FloatQuad(foregroundRect.rect())).enclosingBoundingBox(); } +LayoutRect RenderLayer::clipRectRelativeToAncestor(RenderLayer* ancestor, LayoutSize offsetFromAncestor, const LayoutRect& constrainingRect) const +{ + LayoutRect layerBounds; + ClipRect backgroundRect; + ClipRect foregroundRect; + auto clipRectType = !m_enclosingPaginationLayer || m_enclosingPaginationLayer == ancestor ? PaintingClipRects : TemporaryClipRects; + ClipRectsContext clipRectsContext(ancestor, clipRectType); + calculateRects(clipRectsContext, constrainingRect, layerBounds, backgroundRect, foregroundRect, offsetFromAncestor); + return backgroundRect.rect(); +} + LayoutRect RenderLayer::selfClipRect() const { // FIXME: border-radius not accounted for. // FIXME: Regions not accounted for. RenderLayer* clippingRootLayer = clippingRootForPainting(); - LayoutRect layerBounds; - ClipRect backgroundRect, foregroundRect, outlineRect; - ClipRectsContext clipRectsContext(clippingRootLayer, 0, PaintingClipRects); - calculateRects(clipRectsContext, renderer().view().documentRect(), layerBounds, backgroundRect, foregroundRect, outlineRect); - return clippingRootLayer->renderer().localToAbsoluteQuad(FloatQuad(backgroundRect.rect())).enclosingBoundingBox(); + LayoutRect clipRect = clipRectRelativeToAncestor(clippingRootLayer, offsetFromAncestor(clippingRootLayer), renderer().view().documentRect()); + return clippingRootLayer->renderer().localToAbsoluteQuad(FloatQuad(clipRect)).enclosingBoundingBox(); } -LayoutRect RenderLayer::localClipRect() const +LayoutRect RenderLayer::localClipRect(bool& clipExceedsBounds) const { + clipExceedsBounds = false; // FIXME: border-radius not accounted for. // FIXME: Regions not accounted for. RenderLayer* clippingRootLayer = clippingRootForPainting(); - LayoutRect layerBounds; - ClipRect backgroundRect, foregroundRect, outlineRect; - ClipRectsContext clipRectsContext(clippingRootLayer, 0, PaintingClipRects); - calculateRects(clipRectsContext, LayoutRect::infiniteRect(), layerBounds, backgroundRect, foregroundRect, outlineRect); - - LayoutRect clipRect = backgroundRect.rect(); - if (clipRect == LayoutRect::infiniteRect()) + LayoutSize offsetFromRoot = offsetFromAncestor(clippingRootLayer); + LayoutRect clipRect = clipRectRelativeToAncestor(clippingRootLayer, offsetFromRoot, LayoutRect::infiniteRect()); + if (clipRect.isInfinite()) return clipRect; - LayoutPoint clippingRootOffset; - convertToLayerCoords(clippingRootLayer, clippingRootOffset); - clipRect.moveBy(-clippingRootOffset); + if (renderer().hasClip()) { + // CSS clip may be larger than our border box. + LayoutRect cssClipRect = downcast<RenderBox>(renderer()).clipRect(toLayoutPoint(offsetFromRoot), currentRenderNamedFlowFragment()); + clipExceedsBounds = !clipRect.contains(cssClipRect); + } + clipRect.move(-offsetFromRoot); return clipRect; } @@ -5633,47 +5820,52 @@ void RenderLayer::repaintBlockSelectionGaps() return; LayoutRect rect = m_blockSelectionGapsBounds; - rect.move(-scrolledContentOffset()); + rect.moveBy(-scrollPosition()); if (renderer().hasOverflowClip() && !usesCompositedScrolling()) - rect.intersect(toRenderBox(renderer()).overflowClipRect(LayoutPoint(), 0)); // FIXME: Regions not accounted for. + rect.intersect(downcast<RenderBox>(renderer()).overflowClipRect(LayoutPoint(), nullptr)); // FIXME: Regions not accounted for. if (renderer().hasClip()) - rect.intersect(toRenderBox(renderer()).clipRect(LayoutPoint(), 0)); // FIXME: Regions not accounted for. + rect.intersect(downcast<RenderBox>(renderer()).clipRect(LayoutPoint(), nullptr)); // FIXME: Regions not accounted for. if (!rect.isEmpty()) renderer().repaintRectangle(rect); } -bool RenderLayer::intersectsDamageRect(const LayoutRect& layerBounds, const LayoutRect& damageRect, const RenderLayer* rootLayer, const LayoutPoint* offsetFromRoot, RenderRegion* region) const +bool RenderLayer::intersectsDamageRect(const LayoutRect& layerBounds, const LayoutRect& damageRect, const RenderLayer* rootLayer, const LayoutSize& offsetFromRoot, const LayoutRect* cachedBoundingBox) const { // Always examine the canvas and the root. - // FIXME: Could eliminate the isRoot() check if we fix background painting so that the RenderView + // FIXME: Could eliminate the isDocumentElementRenderer() check if we fix background painting so that the RenderView // paints the root's background. - if (isRootLayer() || renderer().isRoot()) + if (isRootLayer() || renderer().isDocumentElementRenderer()) return true; - // If we aren't an inline flow, and our layer bounds do intersect the damage rect, then we - // can go ahead and return true. - if (!renderer().isRenderInline()) { - LayoutRect b = layerBounds; - b.inflate(renderer().view().maximalOutlineSize()); - if (b.intersects(damageRect)) - return true; - } + if (damageRect.isInfinite()) + return true; + if (damageRect.isEmpty()) + return false; + + // If we aren't an inline flow, and our layer bounds do intersect the damage rect, then we can return true. + if (!renderer().isRenderInline() && layerBounds.intersects(damageRect)) + return true; + + RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment(); // When using regions, some boxes might have their frame rect relative to the flow thread, which could // cause the paint rejection algorithm to prevent them from painting when using different width regions. // e.g. an absolutely positioned box with bottom:0px and right:0px would have it's frameRect.x relative // to the flow thread, not the last region (in which it will end up because of bottom:0px) - if (region && renderer().flowThreadContainingBlock()) { - LayoutRect b = layerBounds; - b.moveBy(region->visualOverflowRectForBox(toRenderBoxModelObject(&renderer())).location()); - b.inflate(renderer().view().maximalOutlineSize()); - if (b.intersects(damageRect)) + if (namedFlowFragment && renderer().flowThreadContainingBlock()) { + LayoutRect adjustedBounds = layerBounds; + adjustedBounds.moveBy(namedFlowFragment->visualOverflowRectForBox(downcast<RenderBoxModelObject>(renderer())).location()); + if (adjustedBounds.intersects(damageRect)) return true; } - + // Otherwise we need to compute the bounding box of this single layer and see if it intersects - // the damage rect. - return boundingBox(rootLayer, 0, offsetFromRoot).intersects(damageRect); + // the damage rect. It's possible the fragment computed the bounding box already, in which case we + // can use the cached value. + if (cachedBoundingBox) + return cachedBoundingBox->intersects(damageRect); + + return boundingBox(rootLayer, offsetFromRoot).intersects(damageRect); } LayoutRect RenderLayer::localBoundingBox(CalculateLayerBoundsFlags flags) const @@ -5688,10 +5880,10 @@ LayoutRect RenderLayer::localBoundingBox(CalculateLayerBoundsFlags flags) const // as part of our bounding box. We do this because we are the responsible layer for both hit testing and painting those // floats. LayoutRect result; - if (renderer().isInline() && renderer().isRenderInline()) - result = toRenderInline(renderer()).linesVisualOverflowBoundingBox(); - else if (renderer().isTableRow()) { - RenderTableRow& tableRow = toRenderTableRow(renderer()); + if (renderer().isInline() && is<RenderInline>(renderer())) + result = downcast<RenderInline>(renderer()).linesVisualOverflowBoundingBox(); + else if (is<RenderTableRow>(renderer())) { + auto& tableRow = downcast<RenderTableRow>(renderer()); // Our bounding box is just the union of all of our cells' border/overflow rects. for (RenderTableCell* cell = tableRow.firstCell(); cell; cell = cell->nextCell()) { LayoutRect bbox = cell->borderBoxRect(); @@ -5704,7 +5896,7 @@ LayoutRect RenderLayer::localBoundingBox(CalculateLayerBoundsFlags flags) const RenderBox* box = renderBox(); ASSERT(box); if (!(flags & DontConstrainForMask) && box->hasMask()) { - result = box->maskClipRect(); + result = box->maskClipRect(LayoutPoint()); box->flipForWritingMode(result); // The mask clip rect is in physical coordinates, so we have to flip, since localBoundingBox is not. } else { LayoutRect bbox = box->borderBoxRect(); @@ -5714,54 +5906,79 @@ LayoutRect RenderLayer::localBoundingBox(CalculateLayerBoundsFlags flags) const result.unite(overflowRect); } } - - result.inflate(renderer().view().maximalOutlineSize()); // Used to apply a fudge factor to dirty-rect checks on blocks/tables. return result; } -LayoutRect RenderLayer::boundingBox(const RenderLayer* ancestorLayer, CalculateLayerBoundsFlags flags, const LayoutPoint* offsetFromRoot) const +LayoutRect RenderLayer::boundingBox(const RenderLayer* ancestorLayer, const LayoutSize& offsetFromRoot, CalculateLayerBoundsFlags flags) const { LayoutRect result = localBoundingBox(flags); - if (renderer().isBox()) - renderBox()->flipForWritingMode(result); - else - renderer().containingBlock()->flipForWritingMode(result); + if (renderer().view().frameView().hasFlippedBlockRenderers()) { + if (renderer().isBox()) + renderBox()->flipForWritingMode(result); + else + renderer().containingBlock()->flipForWritingMode(result); + } + + PaginationInclusionMode inclusionMode = ExcludeCompositedPaginatedLayers; + if (flags & UseFragmentBoxesIncludingCompositing) + inclusionMode = IncludeCompositedPaginatedLayers; - if (enclosingPaginationLayer() && (flags & UseFragmentBoxes)) { + const RenderLayer* paginationLayer = nullptr; + if (flags & UseFragmentBoxesExcludingCompositing || flags & UseFragmentBoxesIncludingCompositing) + paginationLayer = enclosingPaginationLayerInSubtree(ancestorLayer, inclusionMode); + + const RenderLayer* childLayer = this; + bool isPaginated = paginationLayer; + while (paginationLayer) { // Split our box up into the actual fragment boxes that render in the columns/pages and unite those together to // get our true bounding box. - LayoutPoint offsetWithinPaginationLayer; - convertToLayerCoords(enclosingPaginationLayer(), offsetWithinPaginationLayer); - result.moveBy(offsetWithinPaginationLayer); + result.move(childLayer->offsetFromAncestor(paginationLayer)); - RenderFlowThread& enclosingFlowThread = toRenderFlowThread(enclosingPaginationLayer()->renderer()); + auto& enclosingFlowThread = downcast<RenderFlowThread>(paginationLayer->renderer()); result = enclosingFlowThread.fragmentsBoundingBox(result); - LayoutPoint delta; - if (offsetFromRoot) - delta = *offsetFromRoot; - else - enclosingPaginationLayer()->convertToLayerCoords(ancestorLayer, delta); - result.moveBy(delta); - return result; + childLayer = paginationLayer; + paginationLayer = paginationLayer->parent()->enclosingPaginationLayerInSubtree(ancestorLayer, inclusionMode); } - LayoutPoint delta; - if (offsetFromRoot) - delta = *offsetFromRoot; - else - convertToLayerCoords(ancestorLayer, delta); + if (isPaginated) { + result.move(childLayer->offsetFromAncestor(ancestorLayer)); + return result; + } - result.moveBy(delta); + result.move(offsetFromRoot); return result; } +bool RenderLayer::getOverlapBoundsIncludingChildrenAccountingForTransformAnimations(LayoutRect& bounds) const +{ + // The animation will override the display transform, so don't include it. + CalculateLayerBoundsFlags boundsFlags = DefaultCalculateLayerBoundsFlags & ~IncludeSelfTransform; + + bounds = calculateLayerBounds(this, LayoutSize(), boundsFlags); + + LayoutRect animatedBounds = bounds; + if (renderer().animation().computeExtentOfAnimation(renderer(), animatedBounds)) { + bounds = animatedBounds; + return true; + } + + return false; +} + IntRect RenderLayer::absoluteBoundingBox() const { - return pixelSnappedIntRect(boundingBox(root())); + const RenderLayer* rootLayer = root(); + return snappedIntRect(boundingBox(rootLayer, offsetFromAncestor(rootLayer))); } -LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, const LayoutPoint* offsetFromRoot, CalculateLayerBoundsFlags flags) const +FloatRect RenderLayer::absoluteBoundingBoxForPainting() const +{ + const RenderLayer* rootLayer = root(); + return snapRectToDevicePixels(boundingBox(rootLayer, offsetFromAncestor(rootLayer)), renderer().document().deviceScaleFactor()); +} + +LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, const LayoutSize& offsetFromRoot, CalculateLayerBoundsFlags flags) const { if (!isSelfPaintingLayer()) return LayoutRect(); @@ -5776,13 +5993,14 @@ LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, c } LayoutRect boundingBoxRect = localBoundingBox(flags); + if (renderer().view().frameView().hasFlippedBlockRenderers()) { + if (is<RenderBox>(renderer())) + downcast<RenderBox>(renderer()).flipForWritingMode(boundingBoxRect); + else + renderer().containingBlock()->flipForWritingMode(boundingBoxRect); + } - if (renderer().isBox()) - toRenderBox(renderer()).flipForWritingMode(boundingBoxRect); - else - renderer().containingBlock()->flipForWritingMode(boundingBoxRect); - - if (renderer().isRoot()) { + if (renderer().isDocumentElementRenderer()) { // If the root layer becomes composited (e.g. because some descendant with negative z-index is composited), // then it has to be big enough to cover the viewport in order to display the background. This is akin // to the code in RenderBox::paintRootBoxFillLayers(). @@ -5794,14 +6012,13 @@ LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, c LayoutRect unionBounds = boundingBoxRect; if (flags & UseLocalClipRectIfPossible) { - LayoutRect localClipRect = this->localClipRect(); - if (localClipRect != LayoutRect::infiniteRect()) { + bool clipExceedsBounds = false; + LayoutRect localClipRect = this->localClipRect(clipExceedsBounds); + if (!localClipRect.isInfinite() && !clipExceedsBounds) { if ((flags & IncludeSelfTransform) && paintsWithTransform(PaintBehaviorNormal)) localClipRect = transform()->mapRect(localClipRect); - LayoutPoint ancestorRelOffset; - convertToLayerCoords(ancestorLayer, ancestorRelOffset); - localClipRect.moveBy(ancestorRelOffset); + localClipRect.move(offsetFromAncestor(ancestorLayer)); return localClipRect; } } @@ -5813,7 +6030,7 @@ LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, c if (RenderLayer* reflection = reflectionLayer()) { if (!reflection->isComposited()) { - LayoutRect childUnionBounds = reflection->calculateLayerBounds(this, 0, descendantFlags); + LayoutRect childUnionBounds = reflection->calculateLayerBounds(this, reflection->offsetFromAncestor(this), descendantFlags); unionBounds.unite(childUnionBounds); } } @@ -5824,64 +6041,50 @@ LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, c LayerListMutationDetector mutationChecker(const_cast<RenderLayer*>(this)); #endif + auto computeLayersUnion = [this, &unionBounds, flags, descendantFlags] (const RenderLayer& childLayer) { + if (!(flags & IncludeCompositedDescendants) && childLayer.isComposited()) + return; + LayoutRect childBounds = childLayer.calculateLayerBounds(this, childLayer.offsetFromAncestor(this), descendantFlags); + // Ignore child layer (and behave as if we had overflow: hidden) when it is positioned off the parent layer so much + // that we hit the max LayoutUnit value. + unionBounds.checkedUnite(childBounds); + }; + if (Vector<RenderLayer*>* negZOrderList = this->negZOrderList()) { - size_t listSize = negZOrderList->size(); - for (size_t i = 0; i < listSize; ++i) { - RenderLayer* curLayer = negZOrderList->at(i); - if (flags & IncludeCompositedDescendants || !curLayer->isComposited()) { - LayoutRect childUnionBounds = curLayer->calculateLayerBounds(this, 0, descendantFlags); - unionBounds.unite(childUnionBounds); - } - } + for (auto* childLayer : *negZOrderList) + computeLayersUnion(*childLayer); } if (Vector<RenderLayer*>* posZOrderList = this->posZOrderList()) { - size_t listSize = posZOrderList->size(); - for (size_t i = 0; i < listSize; ++i) { - RenderLayer* curLayer = posZOrderList->at(i); + for (auto* childLayer : *posZOrderList) { // The RenderNamedFlowThread is ignored when we calculate the bounds of the RenderView. - if ((flags & IncludeCompositedDescendants || !curLayer->isComposited()) && !curLayer->isFlowThreadCollectingGraphicsLayersUnderRegions()) { - LayoutRect childUnionBounds = curLayer->calculateLayerBounds(this, 0, descendantFlags); - unionBounds.unite(childUnionBounds); - } + if (childLayer->isFlowThreadCollectingGraphicsLayersUnderRegions()) + continue; + computeLayersUnion(*childLayer); } } if (Vector<RenderLayer*>* normalFlowList = this->normalFlowList()) { - size_t listSize = normalFlowList->size(); - for (size_t i = 0; i < listSize; ++i) { - RenderLayer* curLayer = normalFlowList->at(i); + for (auto* childLayer : *normalFlowList) { // RenderView will always return the size of the document, before reaching this point, // so there's no way we could hit a RenderNamedFlowThread here. - ASSERT(!curLayer->isFlowThreadCollectingGraphicsLayersUnderRegions()); - if (flags & IncludeCompositedDescendants || !curLayer->isComposited()) { - LayoutRect curAbsBounds = curLayer->calculateLayerBounds(this, 0, descendantFlags); - unionBounds.unite(curAbsBounds); - } + ASSERT(!childLayer->isFlowThreadCollectingGraphicsLayersUnderRegions()); + computeLayersUnion(*childLayer); } } - -#if ENABLE(CSS_FILTERS) + // FIXME: We can optimize the size of the composited layers, by not enlarging // filtered areas with the outsets if we know that the filter is going to render in hardware. // https://bugs.webkit.org/show_bug.cgi?id=81239 if (flags & IncludeLayerFilterOutsets) renderer().style().filterOutsets().expandRect(unionBounds); -#endif if ((flags & IncludeSelfTransform) && paintsWithTransform(PaintBehaviorNormal)) { TransformationMatrix* affineTrans = transform(); boundingBoxRect = affineTrans->mapRect(boundingBoxRect); unionBounds = affineTrans->mapRect(unionBounds); } - - LayoutPoint ancestorRelOffset; - if (offsetFromRoot) - ancestorRelOffset = *offsetFromRoot; - else - convertToLayerCoords(ancestorLayer, ancestorRelOffset); - unionBounds.moveBy(ancestorRelOffset); - + unionBounds.move(offsetFromRoot); return unionBounds; } @@ -5903,42 +6106,30 @@ void RenderLayer::clearClipRects(ClipRectsType typeToClear) m_clipRectsCache = nullptr; else { ASSERT(typeToClear < NumCachedClipRectsTypes); - RefPtr<ClipRects> dummy; - m_clipRectsCache->setClipRects(typeToClear, RespectOverflowClip, dummy); - m_clipRectsCache->setClipRects(typeToClear, IgnoreOverflowClip, dummy); + m_clipRectsCache->setClipRects(typeToClear, RespectOverflowClip, nullptr); + m_clipRectsCache->setClipRects(typeToClear, IgnoreOverflowClip, nullptr); } } -#if USE(ACCELERATED_COMPOSITING) - RenderLayerBacking* RenderLayer::ensureBacking() { if (!m_backing) { - m_backing = adoptPtr(new RenderLayerBacking(*this)); + m_backing = std::make_unique<RenderLayerBacking>(*this); compositor().layerBecameComposited(*this); -#if ENABLE(CSS_FILTERS) updateOrRemoveFilterEffectRenderer(); -#endif -#if ENABLE(CSS_COMPOSITING) - backing()->setBlendMode(m_blendMode); -#endif } return m_backing.get(); } void RenderLayer::clearBacking(bool layerBeingDestroyed) { - if (m_backing && !renderer().documentBeingDestroyed()) + if (m_backing && !renderer().renderTreeBeingDestroyed()) compositor().layerBecameNonComposited(*this); - m_backing.clear(); + m_backing = nullptr; -#if ENABLE(CSS_FILTERS) if (!layerBeingDestroyed) updateOrRemoveFilterEffectRenderer(); -#else - UNUSED_PARAM(layerBeingDestroyed); -#endif } bool RenderLayer::hasCompositedMask() const @@ -5948,36 +6139,54 @@ bool RenderLayer::hasCompositedMask() const GraphicsLayer* RenderLayer::layerForScrolling() const { - return m_backing ? m_backing->scrollingContentsLayer() : 0; + return m_backing ? m_backing->scrollingContentsLayer() : nullptr; } GraphicsLayer* RenderLayer::layerForHorizontalScrollbar() const { - return m_backing ? m_backing->layerForHorizontalScrollbar() : 0; + return m_backing ? m_backing->layerForHorizontalScrollbar() : nullptr; } GraphicsLayer* RenderLayer::layerForVerticalScrollbar() const { - return m_backing ? m_backing->layerForVerticalScrollbar() : 0; + return m_backing ? m_backing->layerForVerticalScrollbar() : nullptr; } GraphicsLayer* RenderLayer::layerForScrollCorner() const { - return m_backing ? m_backing->layerForScrollCorner() : 0; + return m_backing ? m_backing->layerForScrollCorner() : nullptr; } -#endif - bool RenderLayer::paintsWithTransform(PaintBehavior paintBehavior) const { -#if USE(ACCELERATED_COMPOSITING) bool paintsToWindow = !isComposited() || backing()->paintsIntoWindow(); -#else - bool paintsToWindow = true; -#endif return transform() && ((paintBehavior & PaintBehaviorFlattenCompositingLayers) || paintsToWindow); } +bool RenderLayer::shouldPaintMask(PaintBehavior paintBehavior, PaintLayerFlags paintFlags) const +{ + if (!renderer().hasMask()) + return false; + + bool paintsToWindow = !isComposited() || backing()->paintsIntoWindow(); + if (paintsToWindow || (paintBehavior & PaintBehaviorFlattenCompositingLayers)) + return true; + + return (paintFlags & PaintLayerPaintingCompositingMaskPhase); +} + +bool RenderLayer::shouldApplyClipPath(PaintBehavior paintBehavior, PaintLayerFlags paintFlags) const +{ + if (!renderer().hasClipPath()) + return false; + + bool paintsToWindow = !isComposited() || backing()->paintsIntoWindow(); + if (paintsToWindow || (paintBehavior & PaintBehaviorFlattenCompositingLayers)) + return true; + + return (paintFlags & PaintLayerPaintingCompositingClipPathPhase); +} + bool RenderLayer::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) const { if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant()) @@ -5986,15 +6195,19 @@ bool RenderLayer::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) if (paintsWithTransparency(PaintBehaviorNormal)) return false; + if (renderer().isDocumentElementRenderer()) { + // Normally the document element doens't have a layer. If it does have a layer, its background propagates to the RenderView + // so this layer doesn't draw it. + return false; + } + // We can't use hasVisibleContent(), because that will be true if our renderer is hidden, but some child // is visible and that child doesn't cover the entire rect. if (renderer().style().visibility() != VISIBLE) return false; -#if ENABLE(CSS_FILTERS) if (paintsWithFilters() && renderer().style().filter().hasFilterThatAffectsOpacity()) return false; -#endif // FIXME: Handle simple transforms. if (paintsWithTransform(PaintBehaviorNormal)) @@ -6006,6 +6219,10 @@ bool RenderLayer::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) if (m_zOrderListsDirty || m_normalFlowListDirty) return false; + // Table painting is special; a table paints its sections. + if (renderer().isTablePart()) + return false; + // FIXME: We currently only check the immediate renderer, // which will miss many cases. if (renderer().backgroundIsKnownToBeOpaqueInRect(localRect)) @@ -6026,7 +6243,7 @@ bool RenderLayer::listBackgroundIsKnownToBeOpaqueInRect(const Vector<RenderLayer if (!list || list->isEmpty()) return false; - for (Vector<RenderLayer*>::const_reverse_iterator iter = list->rbegin(); iter != list->rend(); ++iter) { + for (auto iter = list->rbegin(); iter != list->rend(); ++iter) { const RenderLayer* childLayer = *iter; if (childLayer->isComposited()) continue; @@ -6034,10 +6251,8 @@ bool RenderLayer::listBackgroundIsKnownToBeOpaqueInRect(const Vector<RenderLayer if (!childLayer->canUseConvertToLayerCoords()) continue; - LayoutPoint childOffset; LayoutRect childLocalRect(localRect); - childLayer->convertToLayerCoords(this, childOffset); - childLocalRect.moveBy(-childOffset); + childLocalRect.move(-childLayer->offsetFromAncestor(this)); if (childLayer->backgroundIsKnownToBeOpaqueInRect(childLocalRect)) return true; @@ -6050,17 +6265,13 @@ void RenderLayer::setParent(RenderLayer* parent) if (parent == m_parent) return; -#if USE(ACCELERATED_COMPOSITING) - if (m_parent && !renderer().documentBeingDestroyed()) + if (m_parent && !renderer().renderTreeBeingDestroyed()) compositor().layerWillBeRemoved(*m_parent, *this); -#endif m_parent = parent; - -#if USE(ACCELERATED_COMPOSITING) - if (m_parent && !renderer().documentBeingDestroyed()) + + if (m_parent && !renderer().renderTreeBeingDestroyed()) compositor().layerWasAdded(*m_parent, *this); -#endif } void RenderLayer::dirtyZOrderLists() @@ -6074,15 +6285,13 @@ void RenderLayer::dirtyZOrderLists() m_negZOrderList->clear(); m_zOrderListsDirty = true; -#if USE(ACCELERATED_COMPOSITING) - if (!renderer().documentBeingDestroyed()) { + if (!renderer().renderTreeBeingDestroyed()) { if (isFlowThreadCollectingGraphicsLayersUnderRegions()) - toRenderFlowThread(renderer()).setNeedsLayerToRegionMappingsUpdate(); + downcast<RenderFlowThread>(renderer()).setNeedsLayerToRegionMappingsUpdate(); compositor().setCompositingLayersNeedRebuild(); if (acceleratedCompositingForOverflowScrollEnabled()) compositor().setShouldReevaluateCompositingAfterLayout(); } -#endif } void RenderLayer::dirtyStackingContainerZOrderLists() @@ -6100,15 +6309,13 @@ void RenderLayer::dirtyNormalFlowList() m_normalFlowList->clear(); m_normalFlowListDirty = true; -#if USE(ACCELERATED_COMPOSITING) - if (!renderer().documentBeingDestroyed()) { + if (!renderer().renderTreeBeingDestroyed()) { if (isFlowThreadCollectingGraphicsLayersUnderRegions()) - toRenderFlowThread(renderer()).setNeedsLayerToRegionMappingsUpdate(); + downcast<RenderFlowThread>(renderer()).setNeedsLayerToRegionMappingsUpdate(); compositor().setCompositingLayersNeedRebuild(); if (acceleratedCompositingForOverflowScrollEnabled()) compositor().setShouldReevaluateCompositingAfterLayout(); } -#endif } void RenderLayer::rebuildZOrderLists() @@ -6119,13 +6326,9 @@ void RenderLayer::rebuildZOrderLists() m_zOrderListsDirty = false; } -void RenderLayer::rebuildZOrderLists(CollectLayersBehavior behavior, OwnPtr<Vector<RenderLayer*>>& posZOrderList, OwnPtr<Vector<RenderLayer*>>& negZOrderList) +void RenderLayer::rebuildZOrderLists(CollectLayersBehavior behavior, std::unique_ptr<Vector<RenderLayer*>>& posZOrderList, std::unique_ptr<Vector<RenderLayer*>>& negZOrderList) { -#if USE(ACCELERATED_COMPOSITING) bool includeHiddenLayers = compositor().inCompositingMode(); -#else - bool includeHiddenLayers = false; -#endif for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) if (!m_reflection || reflectionLayer() != child) child->collectLayers(includeHiddenLayers, behavior, posZOrderList, negZOrderList); @@ -6149,7 +6352,7 @@ void RenderLayer::updateNormalFlowList() // Ignore non-overflow layers and reflections. if (child->isNormalFlowOnly() && (!m_reflection || reflectionLayer() != child)) { if (!m_normalFlowList) - m_normalFlowList = adoptPtr(new Vector<RenderLayer*>); + m_normalFlowList = std::make_unique<Vector<RenderLayer*>>(); m_normalFlowList->append(child); } } @@ -6157,7 +6360,7 @@ void RenderLayer::updateNormalFlowList() m_normalFlowListDirty = false; } -void RenderLayer::collectLayers(bool includeHiddenLayers, CollectLayersBehavior behavior, OwnPtr<Vector<RenderLayer*>>& posBuffer, OwnPtr<Vector<RenderLayer*>>& negBuffer) +void RenderLayer::collectLayers(bool includeHiddenLayers, CollectLayersBehavior behavior, std::unique_ptr<Vector<RenderLayer*>>& posBuffer, std::unique_ptr<Vector<RenderLayer*>>& negBuffer) { updateDescendantDependentFlags(); @@ -6166,11 +6369,11 @@ void RenderLayer::collectLayers(bool includeHiddenLayers, CollectLayersBehavior bool includeHiddenLayer = includeHiddenLayers || (m_hasVisibleContent || (m_hasVisibleDescendant && isStacking)); if (includeHiddenLayer && !isNormalFlowOnly()) { // Determine which buffer the child should be in. - OwnPtr<Vector<RenderLayer*>>& buffer = (zIndex() >= 0) ? posBuffer : negBuffer; + std::unique_ptr<Vector<RenderLayer*>>& buffer = (zIndex() >= 0) ? posBuffer : negBuffer; // Create the buffer if it doesn't exist yet. if (!buffer) - buffer = adoptPtr(new Vector<RenderLayer*>); + buffer = std::make_unique<Vector<RenderLayer*>>(); // Append ourselves at the end of the appropriate buffer. buffer->append(this); @@ -6189,7 +6392,7 @@ void RenderLayer::collectLayers(bool includeHiddenLayers, CollectLayersBehavior void RenderLayer::updateLayerListsIfNeeded() { - bool shouldUpdateDescendantsAreContiguousInStackingOrder = isStackingContext() && (m_zOrderListsDirty || m_normalFlowListDirty); + bool shouldUpdateDescendantsAreContiguousInStackingOrder = (m_zOrderListsDirty || m_normalFlowListDirty) && isStackingContext(); updateZOrderLists(); updateNormalFlowList(); @@ -6213,37 +6416,25 @@ void RenderLayer::updateDescendantsLayerListsIfNeeded(bool recursive) if (isStackingContainer()) { if (Vector<RenderLayer*>* list = negZOrderList()) { - size_t listSize = list->size(); - for (size_t i = 0; i < listSize; ++i) { - RenderLayer* childLayer = list->at(i); + for (auto* childLayer : *list) layersToUpdate.append(childLayer); - } } } if (Vector<RenderLayer*>* list = normalFlowList()) { - size_t listSize = list->size(); - for (size_t i = 0; i < listSize; ++i) { - RenderLayer* childLayer = list->at(i); + for (auto* childLayer : *list) layersToUpdate.append(childLayer); - } } if (isStackingContainer()) { if (Vector<RenderLayer*>* list = posZOrderList()) { - size_t listSize = list->size(); - for (size_t i = 0; i < listSize; ++i) { - RenderLayer* childLayer = list->at(i); + for (auto* childLayer : *list) layersToUpdate.append(childLayer); - } } } - size_t listSize = layersToUpdate.size(); - for (size_t i = 0; i < listSize; ++i) { - RenderLayer* childLayer = layersToUpdate.at(i); + for (auto* childLayer : layersToUpdate) { childLayer->updateLayerListsIfNeeded(); - if (recursive) childLayer->updateDescendantsLayerListsIfNeeded(true); } @@ -6251,33 +6442,30 @@ void RenderLayer::updateDescendantsLayerListsIfNeeded(bool recursive) void RenderLayer::updateCompositingAndLayerListsIfNeeded() { -#if USE(ACCELERATED_COMPOSITING) if (compositor().inCompositingMode()) { if (isDirtyStackingContainer() || m_normalFlowListDirty) compositor().updateCompositingLayers(CompositingUpdateOnHitTest, this); return; } -#endif + updateLayerListsIfNeeded(); } void RenderLayer::repaintIncludingDescendants() { renderer().repaint(); - for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling()) - curr->repaintIncludingDescendants(); + for (RenderLayer* current = firstChild(); current; current = current->nextSibling()) + current->repaintIncludingDescendants(); // If this is a region, we must also repaint the flow thread's layer since it is the one // doing the actual painting of the flowed content, but only if the region is valid. if (renderer().isRenderNamedFlowFragmentContainer()) { - RenderNamedFlowFragment* region = toRenderBlockFlow(renderer()).renderNamedFlowFragment(); - if (region->isValid()) - region->flowThread()->layer()->repaintIncludingDescendants(); + RenderNamedFlowFragment& region = *downcast<RenderBlockFlow>(renderer()).renderNamedFlowFragment(); + if (region.isValid()) + region.flowThread()->layer()->repaintIncludingDescendants(); } } -#if USE(ACCELERATED_COMPOSITING) - void RenderLayer::setBackingNeedsRepaint(GraphicsLayer::ShouldClipToLayer shouldClip) { ASSERT(isComposited()); @@ -6298,19 +6486,17 @@ void RenderLayer::setBackingNeedsRepaintInRect(const LayoutRect& r, GraphicsLaye // If we're trying to repaint the placeholder document layer, propagate the // repaint to the native view system. LayoutRect absRect(r); - LayoutPoint delta; - convertToLayerCoords(root(), delta); - absRect.moveBy(delta); + absRect.move(offsetFromAncestor(root())); renderer().view().repaintViewRectangle(absRect); } else - backing()->setContentsNeedDisplayInRect(pixelSnappedIntRect(r), shouldClip); + backing()->setContentsNeedDisplayInRect(r, shouldClip); } // Since we're only painting non-composited layers, we know that they all share the same repaintContainer. void RenderLayer::repaintIncludingNonCompositingDescendants(RenderLayerModelObject* repaintContainer) { - renderer().repaintUsingContainer(repaintContainer, pixelSnappedIntRect(renderer().clippedOverflowRectForRepaint(repaintContainer))); + renderer().repaintUsingContainer(repaintContainer, renderer().clippedOverflowRectForRepaint(repaintContainer)); for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling()) { if (!curr->isComposited()) @@ -6318,48 +6504,56 @@ void RenderLayer::repaintIncludingNonCompositingDescendants(RenderLayerModelObje } } +static bool createsStackingContext(const RenderLayer& layer) +{ + auto& renderer = layer.renderer(); + return renderer.hasTransformRelatedProperty() + || renderer.hasClipPath() + || renderer.hasFilter() + || renderer.hasMask() + || renderer.hasBackdropFilter() +#if ENABLE(CSS_COMPOSITING) + || renderer.hasBlendMode() +#endif + || renderer.isTransparent() + || renderer.isPositioned() + || renderer.style().hasFlowFrom() + || renderer.hasReflection() + || renderer.style().hasIsolation() + || layer.needsCompositedScrolling() +#if PLATFORM(IOS) + || layer.hasAcceleratedTouchScrolling() #endif + || (renderer.style().willChange() && renderer.style().willChange()->canCreateStackingContext()); +} bool RenderLayer::shouldBeNormalFlowOnly() const { - return (renderer().hasOverflowClip() - || renderer().hasReflection() - || renderer().hasMask() + if (createsStackingContext(*this)) + return false; + + return renderer().hasOverflowClip() || renderer().isCanvas() || renderer().isVideo() || renderer().isEmbeddedObject() || renderer().isRenderIFrame() - || (renderer().style().specifiesColumns() && !isRootLayer())) - && !renderer().isPositioned() - && !renderer().hasTransform() - && !renderer().hasClipPath() -#if ENABLE(CSS_FILTERS) - && !renderer().hasFilter() -#endif -#if PLATFORM(IOS) - && !hasAcceleratedTouchScrolling() -#endif -#if ENABLE(CSS_COMPOSITING) - && !renderer().hasBlendMode() -#endif - && !isTransparent() - && !needsCompositedScrolling() - && !renderer().style().hasFlowFrom() - ; + || (renderer().style().specifiesColumns() && !isRootLayer()) + || renderer().isInFlowRenderFlowThread(); } bool RenderLayer::shouldBeSelfPaintingLayer() const { - return !isNormalFlowOnly() - || hasOverlayScrollbars() + if (!isNormalFlowOnly()) + return true; + + return hasOverlayScrollbars() || needsCompositedScrolling() - || renderer().hasReflection() - || renderer().hasMask() || renderer().isTableRow() || renderer().isCanvas() || renderer().isVideo() || renderer().isEmbeddedObject() - || renderer().isRenderIFrame(); + || renderer().isRenderIFrame() + || renderer().isInFlowRenderFlowThread(); } void RenderLayer::updateSelfPaintingLayer() @@ -6377,33 +6571,66 @@ void RenderLayer::updateSelfPaintingLayer() parent()->dirtyAncestorChainHasSelfPaintingLayerDescendantStatus(); } -bool RenderLayer::hasNonEmptyChildRenderers() const +static bool hasVisibleBoxDecorationsOrBackground(const RenderElement& renderer) +{ + return renderer.hasVisibleBoxDecorations() || renderer.style().hasOutline(); +} + +// Constrain the depth and breadth of the search for performance. +static const int maxDescendentDepth = 3; +static const int maxSiblingCount = 20; + +static bool hasPaintingNonLayerDescendants(const RenderElement& renderer, int depth) { - // Some HTML can cause whitespace text nodes to have renderers, like: - // <div> - // <img src=...> - // </div> - // so test for 0x0 RenderTexts here - for (RenderObject* child = renderer().firstChild(); child; child = child->nextSibling()) { - if (!child->hasLayer()) { - if (child->isRenderInline() || !child->isBox()) + if (depth > maxDescendentDepth) + return true; + + int siblingCount = 0; + for (const auto& child : childrenOfType<RenderObject>(renderer)) { + if (++siblingCount > maxSiblingCount) + return true; + + if (is<RenderText>(child)) { + const auto& renderText = downcast<RenderText>(child); + if (renderText.linesBoundingBox().isEmpty()) + continue; + + if (renderer.style().userSelect() != SELECT_NONE) return true; - - if (toRenderBox(child)->width() > 0 || toRenderBox(child)->height() > 0) + + if (!renderText.text()->containsOnlyWhitespace()) return true; } + + if (!is<RenderElement>(child)) + continue; + + const RenderElement& renderElementChild = downcast<RenderElement>(child); + + if (is<RenderLayerModelObject>(renderElementChild) && downcast<RenderLayerModelObject>(renderElementChild).hasSelfPaintingLayer()) + continue; + + if (hasVisibleBoxDecorationsOrBackground(renderElementChild)) + return true; + + if (is<RenderReplaced>(renderElementChild)) + return true; + + if (hasPaintingNonLayerDescendants(renderElementChild, depth + 1)) + return true; } + return false; } -static bool hasBoxDecorations(const RenderStyle& style) +bool RenderLayer::hasNonEmptyChildRenderers() const { - return style.hasBorder() || style.hasBorderRadius() || style.hasOutline() || style.hasAppearance() || style.boxShadow() || style.hasFilter(); + return hasPaintingNonLayerDescendants(renderer(), 0); } -bool RenderLayer::hasBoxDecorationsOrBackground() const +bool RenderLayer::hasVisibleBoxDecorationsOrBackground() const { - return hasBoxDecorations(renderer().style()) || renderer().hasBackground(); + return WebCore::hasVisibleBoxDecorationsOrBackground(renderer()); } bool RenderLayer::hasVisibleBoxDecorations() const @@ -6411,20 +6638,23 @@ bool RenderLayer::hasVisibleBoxDecorations() const if (!hasVisibleContent()) return false; - return hasBoxDecorationsOrBackground() || hasOverflowControls(); + return hasVisibleBoxDecorationsOrBackground() || hasOverflowControls(); } bool RenderLayer::isVisuallyNonEmpty() const { ASSERT(!m_visibleDescendantStatusDirty); - if (hasVisibleContent() && hasNonEmptyChildRenderers()) - return true; + if (!hasVisibleContent() || !renderer().style().opacity()) + return false; - if (renderer().isReplaced() || renderer().hasMask()) + if (renderer().isRenderReplaced() || hasOverflowControls()) return true; - if (hasVisibleBoxDecorations()) + if (hasVisibleBoxDecorationsOrBackground()) + return true; + + if (hasNonEmptyChildRenderers()) return true; return false; @@ -6443,6 +6673,21 @@ void RenderLayer::updateStackingContextsAfterStyleChange(const RenderStyle* oldS dirtyZOrderLists(); else clearZOrderLists(); + +#if ENABLE(CSS_COMPOSITING) + if (parent()) { + if (isStackingContext) { + if (!hasNotIsolatedBlendingDescendantsStatusDirty() && hasNotIsolatedBlendingDescendants()) + parent()->dirtyAncestorChainHasBlendingDescendants(); + } else { + if (hasNotIsolatedBlendingDescendantsStatusDirty()) + parent()->dirtyAncestorChainHasBlendingDescendants(); + else if (hasNotIsolatedBlendingDescendants()) + parent()->updateAncestorChainHasBlendingDescendants(); + } + } +#endif + return; } @@ -6455,16 +6700,6 @@ void RenderLayer::updateStackingContextsAfterStyleChange(const RenderStyle* oldS } } -static bool overflowRequiresScrollbar(EOverflow overflow) -{ - return overflow == OSCROLL; -} - -static bool overflowDefinesAutomaticScrollbar(EOverflow overflow) -{ - return overflow == OAUTO || overflow == OOVERLAY; -} - void RenderLayer::updateScrollbarsAfterStyleChange(const RenderStyle* oldStyle) { // Overflow are a box concept. @@ -6480,22 +6715,18 @@ void RenderLayer::updateScrollbarsAfterStyleChange(const RenderStyle* oldStyle) EOverflow overflowY = box->style().overflowY(); // To avoid doing a relayout in updateScrollbarsAfterLayout, we try to keep any automatic scrollbar that was already present. - bool needsHorizontalScrollbar = (hasHorizontalScrollbar() && overflowDefinesAutomaticScrollbar(overflowX)) || overflowRequiresScrollbar(overflowX); - bool needsVerticalScrollbar = (hasVerticalScrollbar() && overflowDefinesAutomaticScrollbar(overflowY)) || overflowRequiresScrollbar(overflowY); + bool needsHorizontalScrollbar = box->hasOverflowClip() && ((hasHorizontalScrollbar() && styleDefinesAutomaticScrollbar(box->style(), HorizontalScrollbar)) || styleRequiresScrollbar(box->style(), HorizontalScrollbar)); + bool needsVerticalScrollbar = box->hasOverflowClip() && ((hasVerticalScrollbar() && styleDefinesAutomaticScrollbar(box->style(), VerticalScrollbar)) || styleRequiresScrollbar(box->style(), VerticalScrollbar)); setHasHorizontalScrollbar(needsHorizontalScrollbar); setHasVerticalScrollbar(needsVerticalScrollbar); - // With overflow: scroll, scrollbars are always visible but may be disabled. + // With non-overlay overflow:scroll, scrollbars are always visible but may be disabled. // When switching to another value, we need to re-enable them (see bug 11985). - if (needsHorizontalScrollbar && oldStyle && oldStyle->overflowX() == OSCROLL && overflowX != OSCROLL) { - ASSERT(hasHorizontalScrollbar()); + if (m_hBar && needsHorizontalScrollbar && oldStyle && oldStyle->overflowX() == OSCROLL && overflowX != OSCROLL) m_hBar->setEnabled(true); - } - if (needsVerticalScrollbar && oldStyle && oldStyle->overflowY() == OSCROLL && overflowY != OSCROLL) { - ASSERT(hasVerticalScrollbar()); + if (m_vBar && needsVerticalScrollbar && oldStyle && oldStyle->overflowY() == OSCROLL && overflowY != OSCROLL) m_vBar->setEnabled(true); - } if (!m_scrollDimensionsDirty) updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow()); @@ -6509,9 +6740,7 @@ void RenderLayer::setAncestorChainHasOutOfFlowPositionedDescendant(RenderBlock* layer->m_hasOutOfFlowPositionedDescendantDirty = false; layer->m_hasOutOfFlowPositionedDescendant = true; -#if USE(ACCELERATED_COMPOSITING) layer->updateNeedsCompositedScrolling(); -#endif if (&layer->renderer() == containingBlock) break; @@ -6530,29 +6759,11 @@ void RenderLayer::updateOutOfFlowPositioned(const RenderStyle* oldStyle) bool wasOutOfFlowPositioned = oldStyle && (oldStyle->position() == AbsolutePosition || oldStyle->position() == FixedPosition); if (parent() && (renderer().isOutOfFlowPositioned() != wasOutOfFlowPositioned)) { parent()->dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus(); -#if USE(ACCELERATED_COMPOSITING) - if (!renderer().documentBeingDestroyed() && acceleratedCompositingForOverflowScrollEnabled()) + if (!renderer().renderTreeBeingDestroyed() && acceleratedCompositingForOverflowScrollEnabled()) compositor().setShouldReevaluateCompositingAfterLayout(); -#endif } } -#if USE(ACCELERATED_COMPOSITING) - -inline bool RenderLayer::needsCompositingLayersRebuiltForClip(const RenderStyle* oldStyle, const RenderStyle* newStyle) const -{ - ASSERT(newStyle); - return oldStyle && (oldStyle->clip() != newStyle->clip() || oldStyle->hasClip() != newStyle->hasClip()); -} - -inline bool RenderLayer::needsCompositingLayersRebuiltForOverflow(const RenderStyle* oldStyle, const RenderStyle* newStyle) const -{ - ASSERT(newStyle); - return !isComposited() && oldStyle && (oldStyle->overflowX() != newStyle->overflowX()) && stackingContainer()->hasCompositingDescendant(); -} - -#endif // USE(ACCELERATED_COMPOSITING) - void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle) { bool isNormalFlowOnly = shouldBeNormalFlowOnly(); @@ -6564,14 +6775,13 @@ void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle dirtyStackingContainerZOrderLists(); } - if (renderer().style().overflowX() == OMARQUEE && renderer().style().marqueeBehavior() != MNONE && renderer().isBox()) { + if (renderer().isHTMLMarquee() && renderer().style().marqueeBehavior() != MNONE && renderer().isBox()) { if (!m_marquee) - m_marquee = adoptPtr(new RenderMarquee(this)); - FeatureObserver::observe(&renderer().document(), renderer().isHTMLMarquee() ? FeatureObserver::HTMLMarqueeElement : FeatureObserver::CSSOverflowMarquee); + m_marquee = std::make_unique<RenderMarquee>(this); m_marquee->updateMarqueeStyle(); } else if (m_marquee) { - m_marquee.clear(); + m_marquee = nullptr; } updateScrollbarsAfterStyleChange(oldStyle); @@ -6588,7 +6798,6 @@ void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle createReflection(); else m_reflection->setStyle(createReflectionStyle()); - FeatureObserver::observe(&renderer().document(), FeatureObserver::Reflection); } // FIXME: Need to detect a swap from custom to native scrollbars (and vice versa). @@ -6605,33 +6814,13 @@ void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle #if ENABLE(CSS_COMPOSITING) updateBlendMode(); #endif -#if ENABLE(CSS_FILTERS) updateOrRemoveFilterClients(); -#endif -#if USE(ACCELERATED_COMPOSITING) updateNeedsCompositedScrolling(); - const RenderStyle& newStyle = renderer().style(); - if (compositor().updateLayerCompositingState(*this) - || needsCompositingLayersRebuiltForClip(oldStyle, &newStyle) - || needsCompositingLayersRebuiltForOverflow(oldStyle, &newStyle)) - compositor().setCompositingLayersNeedRebuild(); - else if (isComposited()) - backing()->updateGraphicsLayerGeometry(); - - if (oldStyle) { - // Compositing layers keep track of whether they are clipped by any of the ancestors. - // When the current layer's clipping behaviour changes, we need to propagate it to the descendants. - const RenderStyle& style = renderer().style(); - bool wasClipping = oldStyle->hasClip() || oldStyle->overflowX() != OVISIBLE || oldStyle->overflowY() != OVISIBLE; - bool isClipping = style.hasClip() || style.overflowX() != OVISIBLE || style.overflowY() != OVISIBLE; - if (isClipping != wasClipping) { - if (checkIfDescendantClippingContextNeedsUpdate(isClipping)) - compositor().setCompositingLayersNeedRebuild(); - } - } -#endif + compositor().layerStyleChanged(diff, *this, oldStyle); + + updateOrRemoveFilterEffectRenderer(); #if PLATFORM(IOS) && ENABLE(TOUCH_EVENTS) if (diff == StyleDifferenceRecompositeLayer || diff >= StyleDifferenceLayoutPositionedMovementOnly) @@ -6639,18 +6828,6 @@ void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle #else UNUSED_PARAM(diff); #endif - -#if ENABLE(CSS_FILTERS) - updateOrRemoveFilterEffectRenderer(); -#if USE(ACCELERATED_COMPOSITING) - bool backingDidCompositeLayers = isComposited() && backing()->canCompositeFilters(); - if (isComposited() && backingDidCompositeLayers && !backing()->canCompositeFilters()) { - // The filters used to be drawn by platform code, but now the platform cannot draw them anymore. - // Fallback to drawing them in software. - setBackingNeedsRepaint(); - } -#endif -#endif } void RenderLayer::updateScrollableAreaSet(bool hasOverflow) @@ -6663,18 +6840,23 @@ void RenderLayer::updateScrollableAreaSet(bool hasOverflow) bool isScrollable = hasOverflow && isVisibleToHitTest; bool addedOrRemoved = false; - if (isScrollable) - addedOrRemoved = frameView.addScrollableArea(this); - else + + ASSERT(m_registeredScrollableArea == frameView.containsScrollableArea(this)); + + if (isScrollable) { + if (!m_registeredScrollableArea) { + addedOrRemoved = frameView.addScrollableArea(this); + m_registeredScrollableArea = true; + } + } else if (m_registeredScrollableArea) { addedOrRemoved = frameView.removeScrollableArea(this); + m_registeredScrollableArea = false; + } - if (addedOrRemoved) { -#if USE(ACCELERATED_COMPOSITING) + if (addedOrRemoved) updateNeedsCompositedScrolling(); -#endif - } -#if PLATFORM(IOS) +#if ENABLE(IOS_TOUCH_EVENTS) if (addedOrRemoved) { if (isScrollable && !hasAcceleratedTouchScrolling()) registerAsTouchEventListenerForScrolling(); @@ -6690,7 +6872,7 @@ void RenderLayer::updateScrollableAreaSet(bool hasOverflow) void RenderLayer::updateScrollCornerStyle() { RenderElement* actualRenderer = rendererForScrollbar(renderer()); - RefPtr<RenderStyle> corner = renderer().hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(SCROLLBAR_CORNER), &actualRenderer->style()) : PassRefPtr<RenderStyle>(0); + auto corner = renderer().hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(SCROLLBAR_CORNER), &actualRenderer->style()) : nullptr; if (!corner) { m_scrollCorner = nullptr; @@ -6698,17 +6880,17 @@ void RenderLayer::updateScrollCornerStyle() } if (!m_scrollCorner) { - m_scrollCorner = createRenderer<RenderScrollbarPart>(renderer().document(), corner.releaseNonNull()); + m_scrollCorner = createRenderer<RenderScrollbarPart>(renderer().document(), WTFMove(*corner)); m_scrollCorner->setParent(&renderer()); m_scrollCorner->initializeStyle(); } else - m_scrollCorner->setStyle(corner.releaseNonNull()); + m_scrollCorner->setStyle(WTFMove(*corner)); } void RenderLayer::updateResizerStyle() { RenderElement* actualRenderer = rendererForScrollbar(renderer()); - RefPtr<RenderStyle> resizer = renderer().hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(RESIZER), &actualRenderer->style()) : PassRefPtr<RenderStyle>(0); + auto resizer = renderer().hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(RESIZER), &actualRenderer->style()) : nullptr; if (!resizer) { m_resizer = nullptr; @@ -6716,16 +6898,16 @@ void RenderLayer::updateResizerStyle() } if (!m_resizer) { - m_resizer = createRenderer<RenderScrollbarPart>(renderer().document(), resizer.releaseNonNull()); + m_resizer = createRenderer<RenderScrollbarPart>(renderer().document(), WTFMove(*resizer)); m_resizer->setParent(&renderer()); m_resizer->initializeStyle(); } else - m_resizer->setStyle(resizer.releaseNonNull()); + m_resizer->setStyle(WTFMove(*resizer)); } RenderLayer* RenderLayer::reflectionLayer() const { - return m_reflection ? m_reflection->layer() : 0; + return m_reflection ? m_reflection->layer() : nullptr; } void RenderLayer::createReflection() @@ -6738,17 +6920,17 @@ void RenderLayer::createReflection() void RenderLayer::removeReflection() { - if (!m_reflection->documentBeingDestroyed()) + if (!m_reflection->renderTreeBeingDestroyed()) m_reflection->removeLayers(this); - m_reflection->setParent(0); + m_reflection->setParent(nullptr); m_reflection = nullptr; } -PassRef<RenderStyle> RenderLayer::createReflectionStyle() +RenderStyle RenderLayer::createReflectionStyle() { auto newStyle = RenderStyle::create(); - newStyle.get().inheritFrom(&renderer().style()); + newStyle.inheritFrom(renderer().style()); // Map in our transform. TransformOperations transform; @@ -6774,29 +6956,29 @@ PassRef<RenderStyle> RenderLayer::createReflectionStyle() transform.operations().append(TranslateTransformOperation::create(renderer().style().boxReflect()->offset(), Length(0, Fixed), TransformOperation::TRANSLATE)); break; } - newStyle.get().setTransform(transform); + newStyle.setTransform(transform); // Map in our mask. - newStyle.get().setMaskBoxImage(renderer().style().boxReflect()->mask()); + newStyle.setMaskBoxImage(renderer().style().boxReflect()->mask()); + + // Style has transform and mask, so needs to be stacking context. + newStyle.setZIndex(0); return newStyle; } -#if ENABLE(CSS_FILTERS) - void RenderLayer::updateOrRemoveFilterClients() { if (!hasFilter()) { FilterInfo::remove(*this); return; } - -#if ENABLE(SVG) - if (renderer().style().filter().hasReferenceFilter()) + // Add the filter as a client to this renderer, unless we are a RenderLayer accommodating + // an SVG. In that case it takes care of its own resource management for filters. + if (renderer().style().filter().hasReferenceFilter() && !renderer().isSVGRoot()) FilterInfo::get(*this).updateReferenceFilterClients(renderer().style().filter()); else if (FilterInfo* filterInfo = FilterInfo::getIfExists(*this)) filterInfo->removeReferenceFilterClients(); -#endif } void RenderLayer::updateOrRemoveFilterEffectRenderer() @@ -6808,7 +6990,7 @@ void RenderLayer::updateOrRemoveFilterEffectRenderer() // Don't delete the whole filter info here, because we might use it // for loading SVG reference filter files. if (FilterInfo* filterInfo = FilterInfo::getIfExists(*this)) - filterInfo->setRenderer(0); + filterInfo->setRenderer(nullptr); // Early-return only if we *don't* have reference filters. // For reference filters, we still want the FilterEffect graph built @@ -6820,154 +7002,171 @@ void RenderLayer::updateOrRemoveFilterEffectRenderer() FilterInfo& filterInfo = FilterInfo::get(*this); if (!filterInfo.renderer()) { RefPtr<FilterEffectRenderer> filterRenderer = FilterEffectRenderer::create(); - RenderingMode renderingMode = renderer().frame().settings().acceleratedFiltersEnabled() ? Accelerated : Unaccelerated; - filterRenderer->setRenderingMode(renderingMode); - filterInfo.setRenderer(filterRenderer.release()); + filterRenderer->setFilterScale(page().deviceScaleFactor()); + filterRenderer->setRenderingMode(renderer().settings().acceleratedFiltersEnabled() ? Accelerated : Unaccelerated); + filterInfo.setRenderer(WTFMove(filterRenderer)); // We can optimize away code paths in other places if we know that there are no software filters. renderer().view().setHasSoftwareFilters(true); + } else if (filterInfo.renderer()->filterScale() != page().deviceScaleFactor()) { + filterInfo.renderer()->setFilterScale(page().deviceScaleFactor()); + filterInfo.renderer()->clearIntermediateResults(); } // If the filter fails to build, remove it from the layer. It will still attempt to // go through regular processing (e.g. compositing), but never apply anything. - if (!filterInfo.renderer()->build(&renderer(), renderer().style().filter(), FilterProperty)) - filterInfo.setRenderer(0); + if (!filterInfo.renderer()->build(renderer(), renderer().style().filter(), FilterProperty)) + filterInfo.setRenderer(nullptr); } void RenderLayer::filterNeedsRepaint() { - renderer().element()->setNeedsStyleRecalc(SyntheticStyleChange); + // We use the enclosing element so that we recalculate style for the ancestor of an anonymous object. + if (Element* element = enclosingElement()) + element->invalidateStyleAndLayerComposition(); renderer().repaint(); } -#endif - -void RenderLayer::paintNamedFlowThreadInsideRegion(GraphicsContext* context, RenderNamedFlowFragment* region, LayoutRect paintDirtyRect, LayoutPoint paintOffset, PaintBehavior paintBehavior, PaintLayerFlags paintFlags) +void RenderLayer::paintNamedFlowThreadInsideRegion(GraphicsContext& context, RenderNamedFlowFragment* region, LayoutRect paintDirtyRect, LayoutPoint paintOffset, PaintBehavior paintBehavior, PaintLayerFlags paintFlags) { - LayoutRect regionContentBox = toRenderBox(region->layerOwner()).contentBoxRect(); - LayoutSize moveOffset = region->flowThreadPortionLocation() - (paintOffset + regionContentBox.location()); - IntPoint adjustedPaintOffset = roundedIntPoint(-moveOffset); - paintDirtyRect.move(moveOffset); + LayoutRect regionContentBox = downcast<RenderBox>(region->layerOwner()).contentBoxRect(); + CurrentRenderRegionMaintainer regionMaintainer(*region); + region->setRegionObjectsRegionStyle(); - context->save(); - context->translate(adjustedPaintOffset.x(), adjustedPaintOffset.y()); + LayoutSize moveOffset = region->flowThreadPortionLocation() - (paintOffset + regionContentBox.location()) + toLayoutSize(region->fragmentContainer().scrollPosition()); + FloatPoint adjustedPaintOffset = roundPointToDevicePixels(toLayoutPoint(moveOffset), renderer().document().deviceScaleFactor()); + context.save(); + context.translate(-adjustedPaintOffset.x(), -adjustedPaintOffset.y()); - region->setRegionObjectsRegionStyle(); - paint(context, paintDirtyRect, paintBehavior, 0, region, paintFlags | PaintLayerTemporaryClipRects); + LayoutSize subpixelOffset = moveOffset - toLayoutSize(LayoutPoint(adjustedPaintOffset)); + paintDirtyRect.move(moveOffset); + paint(context, paintDirtyRect, LayoutSize(-subpixelOffset.width(), -subpixelOffset.height()), paintBehavior, nullptr, paintFlags | PaintLayerTemporaryClipRects); region->restoreRegionObjectsOriginalStyle(); - - context->restore(); + context.restore(); } -void RenderLayer::paintFlowThreadIfRegion(GraphicsContext* context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, LayoutPoint paintOffset, LayoutRect dirtyRect, bool isPaintingOverflowContents) +void RenderLayer::paintFlowThreadIfRegionForFragments(const LayerFragments& fragments, GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags) { if (!renderer().isRenderNamedFlowFragmentContainer()) return; - RenderBlockFlow* renderNamedFlowFragmentContainer = toRenderBlockFlow(&renderer()); - RenderNamedFlowFragment* flowFragment = renderNamedFlowFragmentContainer->renderNamedFlowFragment(); + RenderBlockFlow& renderNamedFlowFragmentContainer = downcast<RenderBlockFlow>(renderer()); + RenderNamedFlowFragment* flowFragment = renderNamedFlowFragmentContainer.renderNamedFlowFragment(); if (!flowFragment->isValid()) return; - - ClipRect regionClipRect; + RenderNamedFlowThread* flowThread = flowFragment->namedFlowThread(); RenderLayer* flowThreadLayer = flowThread->layer(); - bool isLastRegionWithRegionFragmentBreak = (flowFragment->isLastRegion() && flowFragment->style().regionFragment() == BreakRegionFragment); - if (flowFragment->hasOverflowClip() || isLastRegionWithRegionFragmentBreak) { - regionClipRect = renderNamedFlowFragmentContainer->paddingBoxRect(); + + LayoutRect regionClipRect = LayoutRect::infiniteRect(); + if (flowFragment->shouldClipFlowThreadContent()) { + regionClipRect = renderNamedFlowFragmentContainer.paddingBoxRect(); // When the layer of the flow fragment's container is composited, the flow fragment container receives a // GraphicsLayer of its own so the clipping coordinates (caused by overflow:hidden) must be relative to the // GraphicsLayer coordinates in which the fragment gets painted. So what is computed so far is enough. // If the layer of the flowFragment is not composited, then we change the coordinates to be relative to the flow // thread's layer. - if (!isComposited()) { - LayoutPoint regionOffsetFromRoot; - convertToLayerCoords(flowThreadLayer, regionOffsetFromRoot); - regionClipRect.moveBy(regionOffsetFromRoot); - } - } else { - if (paintingInfo.rootLayer != this && parent()) { - ClipRectsContext clipRectsContext(paintingInfo.rootLayer, flowFragment, - (paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, - IgnoreOverlayScrollbarSize, (isPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip); - regionClipRect = backgroundClipRect(clipRectsContext); - } else - regionClipRect = dirtyRect; + if (!isComposited()) + regionClipRect.move(offsetFromAncestor(paintingInfo.rootLayer)); } + + for (const auto& fragment : fragments) { + ClipRect clipRect = fragment.foregroundRect; + if (flowFragment->shouldClipFlowThreadContent()) + clipRect.intersect(regionClipRect); - // Optimize clipping for the single fragment case. - if (!regionClipRect.isEmpty() && regionClipRect != LayoutRect::infiniteRect()) - clipToRect(paintingInfo.rootLayer, context, paintingInfo.paintDirtyRect, regionClipRect); + bool shouldClip = !clipRect.isInfinite(); + // Optimize clipping for the single fragment case. + if (shouldClip) + clipToRect(context, paintingInfo, clipRect); - flowThreadLayer->paintNamedFlowThreadInsideRegion(context, flowFragment, paintingInfo.paintDirtyRect, paintOffset, paintingInfo.paintBehavior, paintFlags); + flowThreadLayer->paintNamedFlowThreadInsideRegion(context, flowFragment, paintingInfo.paintDirtyRect, fragment.layerBounds.location() + paintingInfo.subpixelOffset, + paintingInfo.paintBehavior, paintFlags); - if (!regionClipRect.isEmpty() && regionClipRect != LayoutRect::infiniteRect()) - restoreClip(context, paintingInfo.paintDirtyRect, regionClipRect); + if (shouldClip) + restoreClip(context, paintingInfo, clipRect); + } } -RenderLayer* RenderLayer::hitTestFlowThreadIfRegion(RenderLayer* rootLayer, const HitTestRequest& request, HitTestResult& result, const LayoutRect& hitTestRect, +RenderLayer* RenderLayer::hitTestFlowThreadIfRegionForFragments(const LayerFragments& fragments, RenderLayer*, const HitTestRequest& request, HitTestResult& result, const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, - double* zOffsetForDescendants) + double* zOffsetForDescendants, double* zOffset, + const HitTestingTransformState* unflattenedTransformState, bool depthSortDescendants) { if (!renderer().isRenderNamedFlowFragmentContainer()) - return 0; - - RenderNamedFlowFragment* region = toRenderBlockFlow(&renderer())->renderNamedFlowFragment(); - if (!region->isValid()) - return 0; - - RenderFlowThread* flowThread = region->flowThread(); - - // If the hit location is inside a clipped out area, don't forward the hit test to the flow thread. - if (rootLayer != this && parent()) { - ClipRectsContext clipRectsContext(rootLayer, hitTestLocation.region(), TemporaryClipRects); - ClipRect clipRect = backgroundClipRect(clipRectsContext); - if (!clipRect.intersects(hitTestLocation)) - return 0; - } - - LayoutPoint regionOffsetFromRoot; - convertToLayerCoords(rootLayer, regionOffsetFromRoot); + return nullptr; - LayoutPoint portionLocation = region->flowThreadPortionRect().location(); + RenderNamedFlowFragment& region = *downcast<RenderBlockFlow>(renderer()).renderNamedFlowFragment(); + if (!region.isValid()) + return nullptr; + RenderFlowThread* flowThread = region.flowThread(); + LayoutPoint portionLocation = region.flowThreadPortionRect().location(); if (flowThread->style().isFlippedBlocksWritingMode()) { // The portion location coordinate must be translated into physical coordinates. if (flowThread->style().isHorizontalWritingMode()) - portionLocation.setY(flowThread->height() - (portionLocation.y() + region->contentHeight())); + portionLocation.setY(flowThread->height() - (portionLocation.y() + region.contentHeight())); else - portionLocation.setX(flowThread->width() - (portionLocation.x() + region->contentWidth())); + portionLocation.setX(flowThread->width() - (portionLocation.x() + region.contentWidth())); } - LayoutRect regionContentBox = toRenderBlockFlow(&renderer())->contentBoxRect(); - LayoutSize hitTestOffset = portionLocation - (regionOffsetFromRoot + regionContentBox.location()); + LayoutRect regionContentBox = downcast<RenderBlockFlow>(renderer()).contentBoxRect(); + + RenderLayer* resultLayer = nullptr; + for (int i = fragments.size() - 1; i >= 0; --i) { + const LayerFragment& fragment = fragments.at(i); + + if (!fragment.backgroundRect.intersects(hitTestLocation)) + continue; + + LayoutSize hitTestOffset = portionLocation - (fragment.layerBounds.location() + regionContentBox.location()) + toLayoutSize(region.fragmentContainer().scrollPosition()); - // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView. - HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowShadowContent); + // Always ignore clipping, since the RenderFlowThread has nothing to do with the bounds of the FrameView. + HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowUserAgentShadowContent); - // Make a new temporary HitTestLocation in the new region. - HitTestLocation newHitTestLocation(hitTestLocation, hitTestOffset, region); + // Make a new temporary HitTestLocation in the new region. + HitTestLocation newHitTestLocation(hitTestLocation, hitTestOffset); - // Expand the hit-test rect to the flow thread's coordinate system. - LayoutRect hitTestRectInFlowThread = hitTestRect; - hitTestRectInFlowThread.move(hitTestOffset.width(), hitTestOffset.height()); - hitTestRectInFlowThread.expand(LayoutSize(fabs((double)hitTestOffset.width()), fabs((double)hitTestOffset.height()))); + // Expand the hit-test rect to the flow thread's coordinate system. + LayoutRect hitTestRectInFlowThread = hitTestRect; + hitTestRectInFlowThread.move(hitTestOffset); + hitTestRectInFlowThread.expand(LayoutSize(fabs((double)hitTestOffset.width()), fabs((double)hitTestOffset.height()))); - return flowThread->layer()->hitTestLayer(flowThread->layer(), 0, newRequest, result, hitTestRectInFlowThread, newHitTestLocation, false, transformState, zOffsetForDescendants); + CurrentRenderRegionMaintainer regionMaintainer(region); + + HitTestResult tempResult(result.hitTestLocation()); + RenderLayer* hitLayer = flowThread->layer()->hitTestLayer(flowThread->layer(), nullptr, newRequest, tempResult, hitTestRectInFlowThread, newHitTestLocation, false, transformState, zOffsetForDescendants); + if (result.isRectBasedTest()) + result.append(tempResult); + if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState)) { + resultLayer = hitLayer; + if (!result.isRectBasedTest()) + result = tempResult; + if (!depthSortDescendants) + break; + } + } + + return resultLayer; +} + +RenderNamedFlowFragment* RenderLayer::currentRenderNamedFlowFragment() const +{ + return renderer().currentRenderNamedFlowFragment(); } } // namespace WebCore -#ifndef NDEBUG +#if ENABLE(TREE_DEBUGGING) void showLayerTree(const WebCore::RenderLayer* layer) { if (!layer) return; - WTF::String output = externalRepresentation(&layer->renderer().frame(), WebCore::RenderAsTextShowAllLayers | WebCore::RenderAsTextShowLayerNesting | WebCore::RenderAsTextShowCompositedLayers | WebCore::RenderAsTextShowAddresses | WebCore::RenderAsTextShowIDAndClass | WebCore::RenderAsTextDontUpdateLayout | WebCore::RenderAsTextShowLayoutState | WebCore::RenderAsTextShowOverflow); + WTF::String output = externalRepresentation(&layer->renderer().frame(), WebCore::RenderAsTextShowAllLayers | WebCore::RenderAsTextShowLayerNesting | WebCore::RenderAsTextShowCompositedLayers | WebCore::RenderAsTextShowAddresses | WebCore::RenderAsTextShowIDAndClass | WebCore::RenderAsTextDontUpdateLayout | WebCore::RenderAsTextShowLayoutState | WebCore::RenderAsTextShowOverflow | WebCore::RenderAsTextShowSVGGeometry); fprintf(stderr, "%s\n", output.utf8().data()); } |