From 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c Mon Sep 17 00:00:00 2001 From: Lorry Tar Creator Date: Tue, 27 Jun 2017 06:07:23 +0000 Subject: webkitgtk-2.16.5 --- Source/WebCore/rendering/RenderLayer.cpp | 4437 ++++++++++++++++-------------- 1 file changed, 2318 insertions(+), 2119 deletions(-) (limited to 'Source/WebCore/rendering/RenderLayer.cpp') 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 #include #include -#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 { + WTF_MAKE_FAST_ALLOCATED; +public: + static Ref create() + { + return adoptRef(*new ClipRects); + } + + static Ref 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) + { + 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(clipRectsType); + if (respectOverflow == RespectOverflowClip) + index += static_cast(NumCachedClipRectsTypes); + ASSERT_WITH_SECURITY_IMPLICATION(index < NumCachedClipRectsTypes * 2); + return index; + } + + RefPtr 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(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(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> posZOrderList; - OwnPtr> negZOrderList; + std::unique_ptr> posZOrderList; + std::unique_ptr> 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(); 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 style = renderer().animation().getAnimatedStyleForRenderer(&renderer()); - style->applyTransform(currTransform, renderBox()->pixelSnappedBorderBoxRect().size(), applyOrigin); + FloatRect pixelSnappedBorderRect = snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor()); + std::unique_ptr 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(*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(*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* 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 childOutOfFlowDescendantContainingBlocks; for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) { @@ -1107,29 +1217,41 @@ void RenderLayer::updateDescendantDependentFlags(HashSet* 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* 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* 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* 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* 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* 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(renderer())) { + auto& inlineFlow = downcast(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(*ancestor) && !is(*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(*ancestor).topLeftLocationOffset(); } - curr = curr->parent(); + ancestor = ancestor->parent(); } - if (curr->isBox() && curr->isTableRow()) { + if (is(ancestor)) { // Put ourselves into the row coordinate space. - localPoint -= toRenderBox(curr)->topLeftLocationOffset(); + localPoint -= downcast(*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(positionedParent->renderer())) { + LayoutSize offset = downcast(positionedParent->renderer()).offsetForInFlowPositionedInline(&downcast(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(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(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(nextLayer->renderer()) && downcast(nextLayer->renderer()).canBeScrolledAndHasScrollableArea()) return nextLayer; } - return 0; + return nullptr; +} + +IntRect RenderLayer::scrollableAreaBoundingBox(bool* isInsideFixed) const +{ + 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; } -IntRect RenderLayer::scrollableAreaBoundingBox() const +bool RenderLayer::forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const { - return renderer().absoluteBoundingBoxRect(); + 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(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(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(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(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(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(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(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(this); -#endif const RenderLayer* current = this; while (current) { if (current->isRootLayer()) return const_cast(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(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(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(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(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(parentLayer->renderer())) { + RenderRegion* region = downcast(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 . - 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 (is(*ownerElement)) + frameElementBase = downcast(ownerElement); - if (ownerElement->hasTagName(frameTag) || ownerElement->hasTagName(iframeTag)) - frameElementBase = toHTMLFrameElementBase(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