#include "config.h"
#include "RenderLayer.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 "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"
#include "FrameLoader.h"
#include "FrameLoaderClient.h"
#include "FrameSelection.h"
#include "FrameTree.h"
#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 "HitTestingTransformState.h"
#include "HitTestRequest.h"
#include "HitTestResult.h"
#include "Logging.h"
#include "MainFrame.h"
#include "NoEventDispatchAssertion.h"
#include "OverflowEvent.h"
#include "OverlapTestRequestClient.h"
#include "Page.h"
#include "PlatformMouseEvent.h"
#include "RenderFlexibleBox.h"
#include "RenderFlowThread.h"
#include "RenderGeometryMap.h"
#include "RenderImage.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"
#include "RenderReplica.h"
#include "RenderSVGResourceClipper.h"
#include "RenderScrollbar.h"
#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"
#include "ScrollbarTheme.h"
#include "ScrollingCoordinator.h"
#include "Settings.h"
#include "ShadowRoot.h"
#include "SourceGraphic.h"
#include "StyleProperties.h"
#include "StyleResolver.h"
#include "TextStream.h"
#include "TransformationMatrix.h"
#include "TranslateTransformOperation.h"
#include "WheelEventTestTrigger.h"
#include <stdio.h>
#include <wtf/StdLibExtras.h>
#include <wtf/text/CString.h>
#if ENABLE(CSS_SCROLL_SNAP)
#include "AxisScrollSnapOffsets.h"
#endif
#define MIN_INTERSECT_FOR_REVEAL 32
namespace WebCore {
using namespace HTMLNames;
class ClipRects : public RefCounted<ClipRects> {
WTF_MAKE_FAST_ALLOCATED;
public:
static Ref<ClipRects> create()
{
return adoptRef(*new ClipRects);
}
static Ref<ClipRects> create(const ClipRects& other)
{
return adoptRef(*new ClipRects(other));
}
void reset()
{
m_overflowClipRect.reset();
m_fixedClipRect.reset();
m_posClipRect.reset();
m_fixed = false;
}
const ClipRect& overflowClipRect() const { return m_overflowClipRect; }
void setOverflowClipRect(const ClipRect& clipRect) { m_overflowClipRect = clipRect; }
const ClipRect& fixedClipRect() const { return m_fixedClipRect; }
void setFixedClipRect(const ClipRect& clipRect) { m_fixedClipRect = clipRect; }
const ClipRect& posClipRect() const { return m_posClipRect; }
void setPosClipRect(const ClipRect& clipRect) { m_posClipRect = clipRect; }
bool fixed() const { return m_fixed; }
void setFixed(bool fixed) { m_fixed = fixed; }
bool operator==(const ClipRects& other) const
{
return m_overflowClipRect == other.overflowClipRect()
&& m_fixedClipRect == other.fixedClipRect()
&& m_posClipRect == other.posClipRect()
&& m_fixed == other.fixed();
}
ClipRects& operator=(const ClipRects& other)
{
m_overflowClipRect = other.overflowClipRect();
m_fixedClipRect = other.fixedClipRect();
m_posClipRect = other.posClipRect();
m_fixed = other.fixed();
return *this;
}
private:
ClipRects() = default;
ClipRects(const LayoutRect& clipRect)
: m_overflowClipRect(clipRect)
, m_fixedClipRect(clipRect)
, m_posClipRect(clipRect)
{
}
ClipRects(const ClipRects& other)
: RefCounted()
, m_fixed(other.fixed())
, m_overflowClipRect(other.overflowClipRect())
, m_fixedClipRect(other.fixedClipRect())
, m_posClipRect(other.posClipRect())
{
}
bool m_fixed { false };
ClipRect m_overflowClipRect;
ClipRect m_fixedClipRect;
ClipRect m_posClipRect;
};
class ClipRectsCache {
WTF_MAKE_FAST_ALLOCATED;
public:
ClipRectsCache()
{
#ifndef NDEBUG
for (int i = 0; i < NumCachedClipRectsTypes; ++i) {
m_clipRectsRoot[i] = 0;
m_scrollbarRelevancy[i] = IgnoreOverlayScrollbarSize;
}
#endif
}
ClipRects* getClipRects(ClipRectsType clipRectsType, ShouldRespectOverflowClip respectOverflow) const
{
return m_clipRects[getIndex(clipRectsType, respectOverflow)].get();
}
void setClipRects(ClipRectsType clipRectsType, ShouldRespectOverflowClip respectOverflow, RefPtr<ClipRects>&& clipRects)
{
m_clipRects[getIndex(clipRectsType, respectOverflow)] = WTFMove(clipRects);
}
#ifndef NDEBUG
const RenderLayer* m_clipRectsRoot[NumCachedClipRectsTypes];
OverlayScrollbarSizeRelevancy m_scrollbarRelevancy[NumCachedClipRectsTypes];
#endif
private:
unsigned getIndex(ClipRectsType clipRectsType, ShouldRespectOverflowClip respectOverflow) const
{
unsigned index = static_cast<unsigned>(clipRectsType);
if (respectOverflow == RespectOverflowClip)
index += static_cast<unsigned>(NumCachedClipRectsTypes);
ASSERT_WITH_SECURITY_IMPLICATION(index < NumCachedClipRectsTypes * 2);
return index;
}
RefPtr<ClipRects> m_clipRects[NumCachedClipRectsTypes * 2];
};
void makeMatrixRenderable(TransformationMatrix& matrix, bool has3DRendering)
{
#if !ENABLE(3D_TRANSFORMS)
UNUSED_PARAM(has3DRendering);
matrix.makeAffine();
#else
if (!has3DRendering)
matrix.makeAffine();
#endif
}
RenderLayer::RenderLayer(RenderLayerModelObject& rendererLayerModelObject)
: m_isRootLayer(rendererLayerModelObject.isRenderView())
, m_forcedStackingContext(rendererLayerModelObject.isMedia())
, m_inResizeMode(false)
, m_scrollDimensionsDirty(true)
, m_normalFlowListDirty(true)
, m_hasSelfPaintingLayerDescendant(false)
, m_hasSelfPaintingLayerDescendantDirty(false)
, m_hasOutOfFlowPositionedDescendant(false)
, m_hasOutOfFlowPositionedDescendantDirty(true)
, m_needsCompositedScrolling(false)
, m_descendantsAreContiguousInStackingOrder(false)
, m_usedTransparency(false)
, m_paintingInsideReflection(false)
, m_inOverflowRelayout(false)
, m_repaintStatus(NeedsNormalRepaint)
, m_visibleContentStatusDirty(true)
, m_hasVisibleContent(false)
, m_visibleDescendantStatusDirty(false)
, m_hasVisibleDescendant(false)
, m_registeredScrollableArea(false)
, m_3DTransformedDescendantStatusDirty(true)
, m_has3DTransformedDescendant(false)
, m_hasCompositingDescendant(false)
, m_hasTransformedAncestor(false)
, m_has3DTransformedAncestor(false)
, m_indirectCompositingReason(static_cast<unsigned>(IndirectCompositingReason::None))
, m_viewportConstrainedNotCompositedReason(NoNotCompositedReason)
#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
, m_containsDirtyOverlayScrollbars(false)
, m_updatingMarqueePosition(false)
#if !ASSERT_DISABLED
, m_layerListMutationAllowed(true)
#endif
, m_hasFilterInfo(false)
, 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(nullptr)
, m_previous(nullptr)
, m_next(nullptr)
, m_first(nullptr)
, m_last(nullptr)
, m_staticInlinePosition(0)
, m_staticBlockPosition(0)
, m_enclosingPaginationLayer(nullptr)
{
m_isNormalFlowOnly = shouldBeNormalFlowOnly();
m_isSelfPaintingLayer = shouldBeSelfPaintingLayer();
m_zOrderListsDirty = isStackingContainer();
if (!renderer().firstChild()) {
m_visibleContentStatusDirty = false;
m_hasVisibleContent = renderer().style().visibility() == VISIBLE;
}
if (Element* element = renderer().element()) {
m_scrollPosition = element->savedLayerScrollPosition();
if (!m_scrollPosition.isZero())
scrollAnimator().setCurrentPosition(m_scrollPosition);
element->setSavedLayerScrollPosition(IntPoint());
}
}
RenderLayer::~RenderLayer()
{
if (inResizeMode())
renderer().frame().eventHandler().resizeLayerDestroyed();
ASSERT(m_registeredScrollableArea == renderer().view().frameView().containsScrollableArea(this));
if (m_registeredScrollableArea)
renderer().view().frameView().removeScrollableArea(this);
#if ENABLE(IOS_TOUCH_EVENTS)
unregisterAsTouchEventListenerForScrolling();
#endif
if (Element* element = renderer().element())
element->setSavedLayerScrollPosition(m_scrollPosition);
destroyScrollbar(HorizontalScrollbar);
destroyScrollbar(VerticalScrollbar);
if (auto* scrollingCoordinator = renderer().page().scrollingCoordinator())
scrollingCoordinator->willDestroyScrollableArea(*this);
if (m_reflection)
removeReflection();
FilterInfo::remove(*this);
clearBacking(true);
}
String RenderLayer::name() const
{
StringBuilder name;
if (Element* element = renderer().element()) {
name.append(" <");
name.append(element->tagName().convertToLowercaseWithoutLocale());
name.append('>');
if (element->hasID()) {
name.appendLiteral(" id=\'");
name.append(element->getIdAttribute());
name.append('\'');
}
if (element->hasClass()) {
name.appendLiteral(" class=\'");
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)");
return name.toString();
}
RenderLayerCompositor& RenderLayer::compositor() const
{
return renderer().view().compositor();
}
void RenderLayer::contentChanged(ContentChangeType changeType)
{
if ((changeType == CanvasChanged || changeType == VideoChanged || changeType == FullScreenChanged || changeType == ImageChanged) && compositor().updateLayerCompositingState(*this))
compositor().setCompositingLayersNeedRebuild();
if (m_backing)
m_backing->contentChanged(changeType);
}
bool RenderLayer::canRender3DTransforms() const
{
return compositor().canRender3DTransforms();
}
bool RenderLayer::paintsWithFilters() const
{
if (!renderer().hasFilter())
return false;
if (!isComposited())
return true;
if (!m_backing || !m_backing->canCompositeFilters())
return true;
return false;
}
bool RenderLayer::requiresFullLayerImageForFilters() const
{
if (!paintsWithFilters())
return false;
auto* renderer = filterRenderer();
return renderer && renderer->hasFilterThatMovesPixels();
}
FilterEffectRenderer* RenderLayer::filterRenderer() const
{
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(), nullptr);
updateLayerPositions(&geometryMap, flags);
}
void RenderLayer::updateLayerPositions(RenderGeometryMap* geometryMap, UpdateLayerPositionsFlags flags)
{
updateLayerPosition();
applyPostLayoutScrollPositionIfNeeded();
if (geometryMap)
geometryMap->pushMappingsToAncestor(this, parent());
clearClipRects();
if (hasOverflowControls()) {
LayoutSize offsetFromRoot;
if (geometryMap)
offsetFromRoot = LayoutSize(toFloatSize(geometryMap->absolutePoint(FloatPoint())));
else {
offsetFromRoot = offsetFromAncestor(root());
}
positionOverflowControls(roundedIntSize(offsetFromRoot));
}
updateDescendantDependentFlags();
if (flags & UpdatePagination)
updatePagination();
else
m_enclosingPaginationLayer = nullptr;
if (m_hasVisibleContent) {
ASSERT(!renderer().view().layoutStateEnabled());
RenderLayerModelObject* repaintContainer = renderer().containerForRepaint();
LayoutRect oldRepaintRect = m_repaintRect;
LayoutRect oldOutlineBox = m_outlineBox;
computeRepaintRects(repaintContainer, geometryMap);
if ((flags & CheckForRepaint) && m_hasComputedRepaintRect) {
if (!renderer().view().printing()) {
bool didRepaint = false;
if (m_repaintStatus & NeedsFullRepaint) {
renderer().repaintUsingContainer(repaintContainer, oldRepaintRect);
if (m_repaintRect != oldRepaintRect) {
renderer().repaintUsingContainer(repaintContainer, m_repaintRect);
didRepaint = true;
}
} else if (shouldRepaintAfterLayout()) {
renderer().repaintAfterLayoutIfNeeded(repaintContainer, oldRepaintRect, oldOutlineBox, &m_repaintRect, &m_outlineBox);
didRepaint = true;
}
if (didRepaint && renderer().isRenderNamedFlowFragmentContainer()) {
RenderNamedFlowFragment& region = *downcast<RenderBlockFlow>(renderer()).renderNamedFlowFragment();
if (region.isValid())
region.flowThread()->layer()->repaintIncludingDescendants();
}
}
}
} else
clearRepaintRects();
m_repaintStatus = NeedsNormalRepaint;
m_hasTransformedAncestor = flags & SeenTransformedLayer;
m_has3DTransformedAncestor = flags & Seen3DTransformedLayer;
if (m_reflection)
m_reflection->layout();
bool isUpdateRoot = (flags & IsCompositingUpdateRoot);
if (isComposited())
flags &= ~IsCompositingUpdateRoot;
if (renderer().isInFlowRenderFlowThread()) {
updatePagination();
flags |= UpdatePagination;
}
if (transform()) {
flags |= SeenTransformedLayer;
if (!transform()->isAffine())
flags |= Seen3DTransformedLayer;
}
for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
child->updateLayerPositions(geometryMap, flags);
if ((flags & UpdateCompositingLayers) && isComposited()) {
RenderLayerBacking::UpdateAfterLayoutFlags updateFlags = RenderLayerBacking::CompositingChildrenOnly;
if (flags & NeedsFullRepaintInBacking)
updateFlags |= RenderLayerBacking::NeedsFullRepaint;
if (isUpdateRoot)
updateFlags |= RenderLayerBacking::IsUpdateRoot;
backing()->updateAfterLayout(updateFlags);
}
if (m_marquee) {
bool oldUpdatingMarqueePosition = m_updatingMarqueePosition;
m_updatingMarqueePosition = true;
m_marquee->updateMarqueePosition();
m_updatingMarqueePosition = oldUpdatingMarqueePosition;
}
if (geometryMap)
geometryMap->popMappingsToAncestor(parent());
renderer().document().markers().invalidateRectsForAllMarkers();
}
LayoutRect RenderLayer::repaintRectIncludingNonCompositingDescendants() const
{
LayoutRect repaintRect = m_repaintRect;
for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
if (child->isComposited())
continue;
repaintRect.uniteIfNonZero(child->repaintRectIncludingNonCompositingDescendants());
}
return repaintRect;
}
void RenderLayer::setAncestorChainHasSelfPaintingLayerDescendant()
{
for (RenderLayer* layer = this; layer; layer = layer->parent()) {
if (!layer->m_hasSelfPaintingLayerDescendantDirty && layer->hasSelfPaintingLayerDescendant())
break;
layer->m_hasSelfPaintingLayerDescendantDirty = false;
layer->m_hasSelfPaintingLayerDescendant = true;
}
}
void RenderLayer::dirtyAncestorChainHasSelfPaintingLayerDescendantStatus()
{
for (RenderLayer* layer = this; layer; layer = layer->parent()) {
layer->m_hasSelfPaintingLayerDescendantDirty = true;
if (layer->isSelfPaintingLayer()) {
ASSERT(!parent() || parent()->m_hasSelfPaintingLayerDescendantDirty || parent()->hasSelfPaintingLayerDescendant());
break;
}
}
}
bool RenderLayer::acceleratedCompositingForOverflowScrollEnabled() const
{
return renderer().settings().acceleratedCompositingForOverflowScrollEnabled();
}
void RenderLayer::updateDescendantsAreContiguousInStackingOrder()
{
if (!isStackingContext() || !acceleratedCompositingForOverflowScrollEnabled())
return;
ASSERT(!m_normalFlowListDirty);
ASSERT(!m_zOrderListsDirty);
std::unique_ptr<Vector<RenderLayer*>> posZOrderList;
std::unique_ptr<Vector<RenderLayer*>> negZOrderList;
rebuildZOrderLists(StopAtStackingContexts, posZOrderList, negZOrderList);
HashMap<const RenderLayer*, int> lookup;
if (negZOrderList) {
int stackingOrderIndex = -1;
size_t listSize = negZOrderList->size();
for (size_t i = 0; i < listSize; ++i) {
RenderLayer* currentLayer = negZOrderList->at(listSize - i - 1);
if (!currentLayer->isStackingContext())
continue;
lookup.set(currentLayer, stackingOrderIndex--);
}
}
if (posZOrderList) {
size_t listSize = posZOrderList->size();
int stackingOrderIndex = 1;
for (size_t i = 0; i < listSize; ++i) {
RenderLayer* currentLayer = posZOrderList->at(i);
if (!currentLayer->isStackingContext())
continue;
lookup.set(currentLayer, stackingOrderIndex++);
}
}
int minIndex = 0;
int maxIndex = 0;
int count = 0;
bool firstIteration = true;
updateDescendantsAreContiguousInStackingOrderRecursive(lookup, minIndex, maxIndex, count, firstIteration);
}
void RenderLayer::updateDescendantsAreContiguousInStackingOrderRecursive(const HashMap<const RenderLayer*, int>& lookup, int& minIndex, int& maxIndex, int& count, bool firstIteration)
{
if (isStackingContext() && !firstIteration) {
if (lookup.contains(this)) {
minIndex = std::min(minIndex, lookup.get(this));
maxIndex = std::max(maxIndex, lookup.get(this));
count++;
}
return;
}
for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
int childMinIndex = 0;
int childMaxIndex = 0;
int childCount = 0;
child->updateDescendantsAreContiguousInStackingOrderRecursive(lookup, childMinIndex, childMaxIndex, childCount, false);
if (childCount) {
count += childCount;
minIndex = std::min(minIndex, childMinIndex);
maxIndex = std::max(maxIndex, childMaxIndex);
}
}
if (!isStackingContext()) {
bool newValue = maxIndex - minIndex == count;
bool didUpdate = newValue != m_descendantsAreContiguousInStackingOrder;
m_descendantsAreContiguousInStackingOrder = newValue;
if (didUpdate)
updateNeedsCompositedScrolling();
}
}
void RenderLayer::computeRepaintRects(const RenderLayerModelObject* repaintContainer, const RenderGeometryMap* geometryMap)
{
ASSERT(!m_visibleContentStatusDirty);
if (!isSelfPaintingLayer()) {
clearRepaintRects();
return;
}
m_hasComputedRepaintRect = true;
m_repaintRect = renderer().clippedOverflowRectForRepaint(repaintContainer);
m_outlineBox = renderer().outlineBoundsForRepaint(repaintContainer, geometryMap);
}
void RenderLayer::computeRepaintRectsIncludingDescendants()
{
computeRepaintRects(renderer().containerForRepaint());
for (RenderLayer* layer = firstChild(); layer; layer = layer->nextSibling())
layer->computeRepaintRectsIncludingDescendants();
}
void RenderLayer::clearRepaintRects()
{
ASSERT(!m_visibleContentStatusDirty);
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);
}
void RenderLayer::updateLayerPositionsAfterOverflowScroll()
{
RenderGeometryMap geometryMap(UseTransforms);
if (this != renderer().view().layer())
geometryMap.pushMappingsToAncestor(parent(), nullptr);
updateLayerPositionsAfterScroll(&geometryMap, IsOverflowScroll);
}
void RenderLayer::updateLayerPositionsAfterScroll(RenderGeometryMap* geometryMap, UpdateLayerPositionsAfterScrollFlags flags)
{
updateDescendantDependentFlags();
if (!m_hasVisibleDescendant && !m_hasVisibleContent)
return;
bool positionChanged = updateLayerPosition();
if (positionChanged)
flags |= HasChangedAncestor;
if (flags & HasChangedAncestor || flags & HasSeenViewportConstrainedAncestor || flags & IsOverflowScroll)
clearClipRects();
if (renderer().style().hasViewportConstrainedPosition())
flags |= HasSeenViewportConstrainedAncestor;
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 (shouldComputeRepaintRects) {
if (isVisuallyEmpty)
clearRepaintRects();
else computeRepaintRects(renderer().containerForRepaint(), geometryMap);
} else {
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())
child->updateLayerPositionsAfterScroll(geometryMap, flags);
if (m_marquee) {
bool oldUpdatingMarqueePosition = m_updatingMarqueePosition;
m_updatingMarqueePosition = true;
m_marquee->updateMarqueePosition();
m_updatingMarqueePosition = oldUpdatingMarqueePosition;
}
if (shouldPushAndPopMappings)
geometryMap->popMappingsToAncestor(parent());
renderer().document().markers().invalidateRectsForAllMarkers();
}
void RenderLayer::positionNewlyCreatedOverflowControls()
{
if (!backing()->hasUnpositionedOverflowControlsLayers())
return;
RenderGeometryMap geometryMap(UseTransforms);
if (this != renderer().view().layer() && parent())
geometryMap.pushMappingsToAncestor(parent(), nullptr);
LayoutPoint offsetFromRoot = LayoutPoint(geometryMap.absolutePoint(FloatPoint()));
positionOverflowControls(toIntSize(roundedIntPoint(offsetFromRoot)));
}
#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)
m_blendMode = 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()
{
bool hasTransform = renderer().hasTransform();
bool had3DTransform = has3DTransform();
bool hadTransform = !!m_transform;
if (hasTransform != hadTransform) {
if (hasTransform)
m_transform = std::make_unique<TransformationMatrix>();
else
m_transform = nullptr;
clearClipRectsIncludingDescendants();
}
if (hasTransform) {
RenderBox* box = renderBox();
ASSERT(box);
m_transform->makeIdentity();
box->style().applyTransform(*m_transform, snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor()), RenderStyle::IncludeTransformOrigin);
makeMatrixRenderable(*m_transform, canRender3DTransforms());
}
if (had3DTransform != has3DTransform())
dirty3DTransformedDescendantStatus();
}
TransformationMatrix RenderLayer::currentTransform(RenderStyle::ApplyTransformOrigin applyOrigin) const
{
if (!m_transform)
return TransformationMatrix();
RenderBox* box = renderBox();
if (renderer().animation().isRunningAcceleratedAnimationOnRenderer(renderer(), CSSPropertyTransform, AnimationBase::Running | AnimationBase::Paused)) {
TransformationMatrix currTransform;
FloatRect pixelSnappedBorderRect = snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor());
std::unique_ptr<RenderStyle> style = renderer().animation().getAnimatedStyleForRenderer(renderer());
style->applyTransform(currTransform, pixelSnappedBorderRect, applyOrigin);
makeMatrixRenderable(currTransform, canRender3DTransforms());
return currTransform;
}
if (applyOrigin == RenderStyle::ExcludeTransformOrigin) {
TransformationMatrix currTransform;
FloatRect pixelSnappedBorderRect = snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor());
box->style().applyTransform(currTransform, pixelSnappedBorderRect, RenderStyle::ExcludeTransformOrigin);
makeMatrixRenderable(currTransform, canRender3DTransforms());
return currTransform;
}
return *m_transform;
}
TransformationMatrix RenderLayer::renderableTransform(PaintBehavior paintBehavior) const
{
if (!m_transform)
return TransformationMatrix();
if (paintBehavior & PaintBehaviorFlattenCompositingLayers) {
TransformationMatrix matrix = *m_transform;
makeMatrixRenderable(matrix, false );
return matrix;
}
return *m_transform;
}
RenderLayer* RenderLayer::enclosingOverflowClipLayer(IncludeSelfOrNot includeSelf) const
{
const RenderLayer* layer = (includeSelf == IncludeSelf) ? this : parent();
while (layer) {
if (layer->renderer().hasOverflowClip())
return const_cast<RenderLayer*>(layer);
layer = layer->parent();
}
return nullptr;
}
bool RenderLayer::hasCompositedLayerInEnclosingPaginationChain() const
{
if (!m_enclosingPaginationLayer)
return false;
if (m_enclosingPaginationLayer->isComposited())
return true;
if (m_enclosingPaginationLayer == this)
return false;
if (isComposited())
return true;
if (isNormalFlowOnly())
return parent()->hasCompositedLayerInEnclosingPaginationChain();
for (const auto* containingBlock = renderer().containingBlock(); containingBlock && !is<RenderView>(*containingBlock); containingBlock = containingBlock->containingBlock()) {
if (containingBlock->hasLayer())
return containingBlock->layer()->hasCompositedLayerInEnclosingPaginationChain();
}
return false;
}
void RenderLayer::updatePagination()
{
m_enclosingPaginationLayer = nullptr;
if (!parent())
return;
if (renderer().isInFlowRenderFlowThread()) {
m_enclosingPaginationLayer = this;
return;
}
if (isNormalFlowOnly()) {
if (parent()->hasTransform())
m_enclosingPaginationLayer = nullptr;
else
m_enclosingPaginationLayer = parent()->enclosingPaginationLayer(IncludeCompositedPaginatedLayers);
return;
}
for (const auto* containingBlock = renderer().containingBlock(); containingBlock && !is<RenderView>(*containingBlock); containingBlock = containingBlock->containingBlock()) {
if (containingBlock->hasLayer()) {
if (containingBlock->layer()->hasTransform())
m_enclosingPaginationLayer = nullptr;
else
m_enclosingPaginationLayer = containingBlock->layer()->enclosingPaginationLayer(IncludeCompositedPaginatedLayers);
return;
}
}
}
bool RenderLayer::canBeStackingContainer() const
{
if (isStackingContext() || !stackingContainer())
return true;
return m_descendantsAreContiguousInStackingOrder;
}
void RenderLayer::setHasVisibleContent()
{
if (m_hasVisibleContent && !m_visibleContentStatusDirty) {
ASSERT(!parent() || parent()->hasVisibleDescendant());
return;
}
m_visibleContentStatusDirty = false;
m_hasVisibleContent = true;
computeRepaintRects(renderer().containerForRepaint());
if (!isNormalFlowOnly()) {
for (RenderLayer* sc = stackingContainer(); sc; sc = sc->stackingContainer()) {
sc->dirtyZOrderLists();
if (sc->hasVisibleContent())
break;
}
}
if (parent())
parent()->setAncestorChainHasVisibleDescendant();
}
void RenderLayer::dirtyVisibleContentStatus()
{
m_visibleContentStatusDirty = true;
if (parent())
parent()->dirtyAncestorChainVisibleDescendantStatus();
}
void RenderLayer::dirtyAncestorChainVisibleDescendantStatus()
{
for (RenderLayer* layer = this; layer; layer = layer->parent()) {
if (layer->m_visibleDescendantStatusDirty)
break;
layer->m_visibleDescendantStatusDirty = true;
}
}
void RenderLayer::setAncestorChainHasVisibleDescendant()
{
for (RenderLayer* layer = this; layer; layer = layer->parent()) {
if (!layer->m_visibleDescendantStatusDirty && layer->hasVisibleDescendant())
break;
layer->m_hasVisibleDescendant = true;
layer->m_visibleDescendantStatusDirty = false;
}
}
void RenderLayer::updateDescendantDependentFlags(HashSet<const RenderObject*>* outOfFlowDescendantContainingBlocks)
{
if (m_visibleDescendantStatusDirty || m_hasSelfPaintingLayerDescendantDirty || m_hasOutOfFlowPositionedDescendantDirty || hasNotIsolatedBlendingDescendantsStatusDirty()) {
bool hasVisibleDescendant = false;
bool hasSelfPaintingLayerDescendant = false;
bool hasOutOfFlowPositionedDescendant = false;
#if ENABLE(CSS_COMPOSITING)
bool hasNotIsolatedBlendingDescendants = false;
#endif
HashSet<const RenderObject*> childOutOfFlowDescendantContainingBlocks;
for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
childOutOfFlowDescendantContainingBlocks.clear();
child->updateDescendantDependentFlags(&childOutOfFlowDescendantContainingBlocks);
bool childIsOutOfFlowPositioned = child->renderer().isOutOfFlowPositioned();
if (childIsOutOfFlowPositioned)
childOutOfFlowDescendantContainingBlocks.add(child->renderer().containingBlock());
if (outOfFlowDescendantContainingBlocks) {
HashSet<const RenderObject*>::const_iterator it = childOutOfFlowDescendantContainingBlocks.begin();
for (; it != childOutOfFlowDescendantContainingBlocks.end(); ++it)
outOfFlowDescendantContainingBlocks->add(*it);
}
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
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;
m_hasOutOfFlowPositionedDescendant = hasOutOfFlowPositionedDescendant;
if (m_hasOutOfFlowPositionedDescendantDirty)
updateNeedsCompositedScrolling();
m_hasOutOfFlowPositionedDescendantDirty = false;
#if ENABLE(CSS_COMPOSITING)
m_hasNotIsolatedBlendingDescendants = hasNotIsolatedBlendingDescendants;
if (m_hasNotIsolatedBlendingDescendantsStatusDirty) {
m_hasNotIsolatedBlendingDescendantsStatusDirty = false;
updateSelfPaintingLayer();
}
#endif
}
if (m_visibleContentStatusDirty) {
if (renderer().style().visibility() == VISIBLE)
m_hasVisibleContent = true;
else {
m_hasVisibleContent = false;
RenderObject* r = renderer().firstChild();
while (r) {
if (r->style().visibility() == VISIBLE && !r->hasLayer()) {
m_hasVisibleContent = true;
break;
}
RenderObject* child = nullptr;
if (!r->hasLayer() && (child = r->firstChildSlow()))
r = child;
else if (r->nextSibling())
r = r->nextSibling();
else {
do {
r = r->parent();
if (r == &renderer())
r = nullptr;
} while (r && !r->nextSibling());
if (r)
r = r->nextSibling();
}
}
}
m_visibleContentStatusDirty = false;
}
}
void RenderLayer::dirty3DTransformedDescendantStatus()
{
RenderLayer* curr = stackingContainer();
if (curr)
curr->m_3DTransformedDescendantStatusDirty = true;
while (curr && curr->preserves3D()) {
curr->m_3DTransformedDescendantStatusDirty = true;
curr = curr->stackingContainer();
}
}
bool RenderLayer::update3DTransformedDescendantStatus()
{
if (m_3DTransformedDescendantStatusDirty) {
m_has3DTransformedDescendant = false;
updateZOrderLists();
if (Vector<RenderLayer*>* positiveZOrderList = posZOrderList()) {
for (auto* layer : *positiveZOrderList)
m_has3DTransformedDescendant |= layer->update3DTransformedDescendantStatus();
}
if (Vector<RenderLayer*>* negativeZOrderList = negZOrderList()) {
for (auto* layer : *negativeZOrderList)
m_has3DTransformedDescendant |= layer->update3DTransformedDescendantStatus();
}
m_3DTransformedDescendantStatusDirty = false;
}
if (preserves3D())
return has3DTransform() || m_has3DTransformedDescendant;
return has3DTransform();
}
bool RenderLayer::updateLayerPosition()
{
LayoutPoint localPoint;
LayoutSize inlineBoundingBoxOffset; if (renderer().isInline() && is<RenderInline>(renderer())) {
auto& inlineFlow = downcast<RenderInline>(renderer());
IntRect lineBox = inlineFlow.linesBoundingBox();
setSize(lineBox.size());
inlineBoundingBoxOffset = toLayoutSize(lineBox.location());
localPoint += inlineBoundingBoxOffset;
} else if (RenderBox* box = renderBox()) {
setSize(snappedIntRect(box->frameRect()).size());
box->applyTopLeftLocationOffset(localPoint);
}
if (!renderer().isOutOfFlowPositioned()) {
auto* ancestor = renderer().parent();
while (ancestor && !ancestor->hasLayer()) {
if (is<RenderBox>(*ancestor) && !is<RenderTableRow>(*ancestor)) {
localPoint += downcast<RenderBox>(*ancestor).topLeftLocationOffset();
}
ancestor = ancestor->parent();
}
if (is<RenderTableRow>(ancestor)) {
localPoint -= downcast<RenderTableRow>(*ancestor).topLeftLocationOffset();
}
}
RenderLayer* positionedParent;
if (renderer().isOutOfFlowPositioned() && (positionedParent = enclosingAncestorForPosition(renderer().style().position()))) {
if (positionedParent->renderer().hasOverflowClip())
localPoint -= toLayoutSize(positionedParent->scrollPosition());
if (renderer().isOutOfFlowPositioned() && positionedParent->renderer().isInFlowPositioned() && is<RenderInline>(positionedParent->renderer())) {
LayoutSize offset = downcast<RenderInline>(positionedParent->renderer()).offsetForInFlowPositionedInline(&downcast<RenderBox>(renderer()));
localPoint += offset;
}
} else if (parent()) {
if (parent()->renderer().hasOverflowClip())
localPoint -= toLayoutSize(parent()->scrollPosition());
}
bool positionOrOffsetChanged = false;
if (renderer().isInFlowPositioned()) {
LayoutSize newOffset = downcast<RenderBoxModelObject>(renderer()).offsetForInFlowPosition();
positionOrOffsetChanged = newOffset != m_offsetForInFlowPosition;
m_offsetForInFlowPosition = newOffset;
localPoint.move(m_offsetForInFlowPosition);
} else {
m_offsetForInFlowPosition = LayoutSize();
}
localPoint -= inlineBoundingBoxOffset;
positionOrOffsetChanged |= location() != localPoint;
setLocation(localPoint);
return positionOrOffsetChanged;
}
TransformationMatrix RenderLayer::perspectiveTransform() const
{
RenderBox* box = renderBox();
if (!box)
return TransformationMatrix();
if (!box->hasTransformRelatedProperty())
return TransformationMatrix();
const RenderStyle& style = box->style();
if (!style.hasPerspective())
return TransformationMatrix();
const FloatRect borderBox = snapRectToDevicePixels(box->borderBoxRect(), box->document().deviceScaleFactor());
float perspectiveOriginX = floatValueForLength(style.perspectiveOriginX(), borderBox.width());
float perspectiveOriginY = floatValueForLength(style.perspectiveOriginY(), borderBox.height());
perspectiveOriginX -= borderBox.width() / 2.0f;
perspectiveOriginY -= borderBox.height() / 2.0f;
TransformationMatrix t;
t.translate(perspectiveOriginX, perspectiveOriginY);
t.applyPerspective(style.perspective());
t.translate(-perspectiveOriginX, -perspectiveOriginY);
return t;
}
FloatPoint RenderLayer::perspectiveOrigin() const
{
if (!renderer().hasTransformRelatedProperty())
return FloatPoint();
const LayoutRect borderBox = downcast<RenderBox>(renderer()).borderBoxRect();
const RenderStyle& style = renderer().style();
return FloatPoint(floatValueForLength(style.perspectiveOriginX(), borderBox.width()),
floatValueForLength(style.perspectiveOriginY(), borderBox.height()));
}
RenderLayer* RenderLayer::stackingContainer() const
{
RenderLayer* layer = parent();
while (layer && !layer->isStackingContainer())
layer = layer->parent();
ASSERT(!layer || layer->isStackingContainer());
return layer;
}
static inline bool isContainerForPositioned(RenderLayer& layer, EPosition position)
{
switch (position) {
case FixedPosition:
return layer.renderer().canContainFixedPositionObjects();
case AbsolutePosition:
return layer.renderer().canContainAbsolutelyPositionedObjects();
default:
ASSERT_NOT_REACHED();
return false;
}
}
RenderLayer* RenderLayer::enclosingAncestorForPosition(EPosition position) const
{
RenderLayer* curr = parent();
while (curr && !isContainerForPositioned(*curr, position))
curr = curr->parent();
return curr;
}
static RenderLayer* parentLayerCrossFrame(const RenderLayer& layer)
{
if (layer.parent())
return layer.parent();
HTMLFrameOwnerElement* ownerElement = layer.renderer().document().ownerElement();
if (!ownerElement)
return nullptr;
RenderElement* ownerRenderer = ownerElement->renderer();
if (!ownerRenderer)
return nullptr;
return ownerRenderer->enclosingLayer();
}
RenderLayer* RenderLayer::enclosingScrollableLayer() const
{
for (RenderLayer* nextLayer = parentLayerCrossFrame(*this); nextLayer; nextLayer = parentLayerCrossFrame(*nextLayer)) {
if (is<RenderBox>(nextLayer->renderer()) && downcast<RenderBox>(nextLayer->renderer()).canBeScrolledAndHasScrollableArea())
return nextLayer;
}
return nullptr;
}
IntRect RenderLayer::scrollableAreaBoundingBox(bool* isInsideFixed) const
{
return renderer().absoluteBoundingBoxRect( true, isInsideFixed);
}
bool RenderLayer::isRubberBandInProgress() const
{
#if ENABLE(RUBBER_BANDING)
if (!scrollsOverflow())
return false;
if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
return scrollAnimator->isRubberBandInProgress();
#endif
return false;
}
bool RenderLayer::forceUpdateScrollbarsOnMainThreadForPerformanceTesting() const
{
return renderer().settings().forceUpdateScrollbarsOnMainThreadForPerformanceTesting();
}
RenderLayer* RenderLayer::enclosingTransformedAncestor() const
{
RenderLayer* curr = parent();
while (curr && !curr->isRootLayer() && !curr->transform())
curr = curr->parent();
return curr;
}
static inline const RenderLayer* compositingContainer(const RenderLayer& layer)
{
return layer.isNormalFlowOnly() ? layer.parent() : layer.stackingContainer();
}
inline bool RenderLayer::shouldRepaintAfterLayout() const
{
if (m_repaintStatus == NeedsNormalRepaint)
return true;
ASSERT(m_repaintStatus == NeedsFullRepaintForPositionedMovementLayout);
return !isComposited() || backing()->paintsIntoCompositedAncestor();
}
bool compositedWithOwnBackingStore(const RenderLayer& layer)
{
return layer.isComposited() && !layer.backing()->paintsIntoCompositedAncestor();
}
RenderLayer* RenderLayer::enclosingCompositingLayer(IncludeSelfOrNot includeSelf) const
{
if (includeSelf == IncludeSelf && isComposited())
return const_cast<RenderLayer*>(this);
for (const RenderLayer* curr = compositingContainer(*this); curr; curr = compositingContainer(*curr)) {
if (curr->isComposited())
return const_cast<RenderLayer*>(curr);
}
return nullptr;
}
RenderLayer* RenderLayer::enclosingCompositingLayerForRepaint(IncludeSelfOrNot includeSelf) const
{
if (includeSelf == IncludeSelf && compositedWithOwnBackingStore(*this))
return const_cast<RenderLayer*>(this);
for (const RenderLayer* curr = compositingContainer(*this); curr; curr = compositingContainer(*curr)) {
if (compositedWithOwnBackingStore(*curr))
return const_cast<RenderLayer*>(curr);
}
return nullptr;
}
RenderLayer* RenderLayer::enclosingFilterLayer(IncludeSelfOrNot includeSelf) const
{
const RenderLayer* curr = (includeSelf == IncludeSelf) ? this : parent();
for (; curr; curr = curr->parent()) {
if (curr->requiresFullLayerImageForFilters())
return const_cast<RenderLayer*>(curr);
}
return nullptr;
}
RenderLayer* RenderLayer::enclosingFilterRepaintLayer() const
{
for (const RenderLayer* curr = this; curr; curr = curr->parent()) {
if ((curr != this && curr->requiresFullLayerImageForFilters()) || compositedWithOwnBackingStore(*curr) || curr->isRootLayer())
return const_cast<RenderLayer*>(curr);
}
return nullptr;
}
void RenderLayer::setFilterBackendNeedsRepaintingInRect(const LayoutRect& rect)
{
if (rect.isEmpty())
return;
LayoutRect rectForRepaint = rect;
renderer().style().filterOutsets().expandRect(rectForRepaint);
FilterInfo& filterInfo = FilterInfo::get(*this);
filterInfo.expandDirtySourceRect(rectForRepaint);
RenderLayer* parentLayer = enclosingFilterRepaintLayer();
ASSERT(parentLayer);
FloatQuad repaintQuad(rectForRepaint);
LayoutRect parentLayerRect = renderer().localToContainerQuad(repaintQuad, &parentLayer->renderer()).enclosingBoundingBox();
if (parentLayer->isComposited()) {
if (!parentLayer->backing()->paintsIntoWindow()) {
parentLayer->setBackingNeedsRepaintInRect(parentLayerRect);
return;
}
parentLayer = renderer().view().layer();
parentLayerRect = renderer().localToContainerQuad(repaintQuad, &parentLayer->renderer()).enclosingBoundingBox();
}
if (parentLayer->paintsWithFilters()) {
parentLayer->setFilterBackendNeedsRepaintingInRect(parentLayerRect);
return;
}
if (parentLayer->isRootLayer()) {
downcast<RenderView>(parentLayer->renderer()).repaintViewRectangle(parentLayerRect);
return;
}
ASSERT_NOT_REACHED();
}
bool RenderLayer::hasAncestorWithFilterOutsets() const
{
for (const RenderLayer* curr = this; curr; curr = curr->parent()) {
if (curr->renderer().style().hasFilterOutsets())
return true;
}
return false;
}
RenderLayer* RenderLayer::clippingRootForPainting() const
{
if (isComposited())
return const_cast<RenderLayer*>(this);
const RenderLayer* current = this;
while (current) {
if (current->isRootLayer())
return const_cast<RenderLayer*>(current);
current = compositingContainer(*current);
ASSERT(current);
if (current->transform() || compositedWithOwnBackingStore(*current))
return const_cast<RenderLayer*>(current);
}
ASSERT_NOT_REACHED();
return nullptr;
}
LayoutPoint RenderLayer::absoluteToContents(const LayoutPoint& absolutePoint) const
{
return LayoutPoint(renderer().absoluteToLocal(absolutePoint, UseTransforms));
}
bool RenderLayer::cannotBlitToWindow() const
{
if (isTransparent() || hasReflection() || hasTransform())
return true;
if (!parent())
return false;
return parent()->cannotBlitToWindow();
}
RenderLayer* RenderLayer::transparentPaintingAncestor()
{
if (isComposited())
return nullptr;
for (RenderLayer* curr = parent(); curr; curr = curr->parent()) {
if (curr->isComposited())
return nullptr;
if (curr->isTransparent())
return curr;
}
return nullptr;
}
enum TransparencyClipBoxBehavior {
PaintingTransparencyClipBox,
HitTestingTransparencyClipBox
};
enum TransparencyClipBoxMode {
DescendantsOfTransparencyClipBox,
RootOfTransparencyClipBox
};
static LayoutRect transparencyClipBox(const RenderLayer&, const RenderLayer* rootLayer, TransparencyClipBoxBehavior, TransparencyClipBoxMode, PaintBehavior = 0);
static void expandClipRectForRegionAndReflection(LayoutRect& clipRect, const RenderLayer& layer, const RenderLayer* rootLayer,
TransparencyClipBoxBehavior transparencyBehavior, PaintBehavior paintBehavior)
{
if (layer.renderer().isRenderNamedFlowFragmentContainer()) {
RenderBlockFlow& regionContainer = downcast<RenderBlockFlow>(layer.renderer());
RenderNamedFlowFragment& region = *regionContainer.renderNamedFlowFragment();
RenderLayer* flowThreadLayer = region.flowThread()->layer();
if (flowThreadLayer && (!layer.reflection() || layer.reflectionLayer() != flowThreadLayer)) {
LayoutRect flowThreadClipRect = transparencyClipBox(*flowThreadLayer, rootLayer, transparencyBehavior, DescendantsOfTransparencyClipBox, paintBehavior);
LayoutSize moveOffset = (regionContainer.contentBoxRect().location() + layer.offsetFromAncestor(flowThreadLayer)) - region.flowThreadPortionRect().location();
flowThreadClipRect.move(moveOffset);
clipRect.unite(flowThreadClipRect);
}
}
}
static void expandClipRectForDescendantsAndReflection(LayoutRect& clipRect, const RenderLayer& layer, const RenderLayer* rootLayer,
TransparencyClipBoxBehavior transparencyBehavior, PaintBehavior paintBehavior)
{
if (!layer.renderer().hasMask()) {
for (RenderLayer* curr = layer.firstChild(); curr; curr = curr->nextSibling()) {
if (!layer.reflection() || layer.reflectionLayer() != curr)
clipRect.unite(transparencyClipBox(*curr, rootLayer, transparencyBehavior, DescendantsOfTransparencyClipBox, paintBehavior));
}
}
expandClipRectForRegionAndReflection(clipRect, layer, rootLayer, transparencyBehavior, paintBehavior);
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,
TransparencyClipBoxMode transparencyMode, PaintBehavior paintBehavior)
{
if (rootLayer != &layer && ((transparencyBehavior == PaintingTransparencyClipBox && layer.paintsWithTransform(paintBehavior))
|| (transparencyBehavior == HitTestingTransparencyClipBox && layer.hasTransform()))) {
RenderLayer::PaginationInclusionMode mode = transparencyBehavior == HitTestingTransparencyClipBox ? RenderLayer::IncludeCompositedPaginatedLayers : RenderLayer::ExcludeCompositedPaginatedLayers;
const RenderLayer* paginationLayer = transparencyMode == DescendantsOfTransparencyClipBox ? layer.enclosingPaginationLayer(mode) : nullptr;
const RenderLayer* rootLayerForTransform = paginationLayer ? paginationLayer : rootLayer;
LayoutSize delta = layer.offsetFromAncestor(rootLayerForTransform);
TransformationMatrix transform;
transform.translate(delta.width(), delta.height());
transform.multiply(*layer.transform());
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;
auto& enclosingFlowThread = downcast<RenderFlowThread>(paginationLayer->renderer());
result = enclosingFlowThread.fragmentsBoundingBox(result);
result.move(paginationLayer->offsetFromAncestor(rootLayer));
return result;
}
LayoutRect clipRect = layer.boundingBox(rootLayer, layer.offsetFromAncestor(rootLayer), transparencyBehavior == HitTestingTransparencyClipBox ? RenderLayer::UseFragmentBoxesIncludingCompositing : RenderLayer::UseFragmentBoxesExcludingCompositing);
expandClipRectForDescendantsAndReflection(clipRect, layer, rootLayer, transparencyBehavior, paintBehavior);
layer.renderer().style().filterOutsets().expandRect(clipRect);
return clipRect;
}
static LayoutRect paintingExtent(const RenderLayer& currentLayer, const RenderLayer* rootLayer, const LayoutRect& paintDirtyRect, PaintBehavior paintBehavior)
{
return intersection(transparencyClipBox(currentLayer, rootLayer, PaintingTransparencyClipBox, RootOfTransparencyClipBox, paintBehavior), paintDirtyRect);
}
void RenderLayer::beginTransparencyLayers(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, const LayoutRect& dirtyRect)
{
if (context.paintingDisabled() || (paintsWithTransparency(paintingInfo.paintBehavior) && m_usedTransparency))
return;
RenderLayer* ancestor = transparentPaintingAncestor();
if (ancestor)
ancestor->beginTransparencyLayers(context, paintingInfo, dirtyRect);
if (paintsWithTransparency(paintingInfo.paintBehavior)) {
ASSERT(isStackingContext());
m_usedTransparency = true;
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));
context.fillRect(pixelSnappedClipRect);
#endif
}
}
#if PLATFORM(IOS)
void RenderLayer::willBeDestroyed()
{
if (RenderLayerBacking* layerBacking = backing())
layerBacking->layerWillBeDestroyed();
}
#endif
void RenderLayer::addChild(RenderLayer* child, RenderLayer* beforeChild)
{
RenderLayer* prevSibling = beforeChild ? beforeChild->previousSibling() : lastChild();
if (prevSibling) {
child->setPreviousSibling(prevSibling);
prevSibling->setNextSibling(child);
ASSERT(prevSibling != child);
} else
setFirstChild(child);
if (beforeChild) {
beforeChild->setPreviousSibling(child);
child->setNextSibling(beforeChild);
ASSERT(beforeChild != child);
} else
setLastChild(child);
child->setParent(this);
if (child->isNormalFlowOnly())
dirtyNormalFlowList();
if (!child->isNormalFlowOnly() || child->firstChild()) {
child->dirtyStackingContainerZOrderLists();
}
child->updateDescendantDependentFlags();
if (child->m_hasVisibleContent || child->m_hasVisibleDescendant)
setAncestorChainHasVisibleDescendant();
if (child->isSelfPaintingLayer() || child->hasSelfPaintingLayerDescendant())
setAncestorChainHasSelfPaintingLayerDescendant();
if (child->renderer().isOutOfFlowPositioned() || child->hasOutOfFlowPositionedDescendant())
setAncestorChainHasOutOfFlowPositionedDescendant(child->renderer().containingBlock());
#if ENABLE(CSS_COMPOSITING)
if (child->hasBlendMode() || (child->hasNotIsolatedBlendingDescendants() && !child->isolatesBlending()))
updateAncestorChainHasBlendingDescendants();
#endif
compositor().layerWasAdded(*this, *child);
}
RenderLayer* RenderLayer::removeChild(RenderLayer* oldChild)
{
if (!renderer().renderTreeBeingDestroyed())
compositor().layerWillBeRemoved(*this, *oldChild);
if (oldChild->previousSibling())
oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
if (oldChild->nextSibling())
oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
if (m_first == oldChild)
m_first = oldChild->nextSibling();
if (m_last == oldChild)
m_last = oldChild->previousSibling();
if (oldChild->isNormalFlowOnly())
dirtyNormalFlowList();
if (!oldChild->isNormalFlowOnly() || oldChild->firstChild()) {
oldChild->dirtyStackingContainerZOrderLists();
}
if (oldChild->renderer().isOutOfFlowPositioned() || oldChild->hasOutOfFlowPositionedDescendant())
dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus();
oldChild->setPreviousSibling(nullptr);
oldChild->setNextSibling(nullptr);
oldChild->setParent(nullptr);
oldChild->updateDescendantDependentFlags();
if (oldChild->m_hasVisibleContent || oldChild->m_hasVisibleDescendant)
dirtyAncestorChainVisibleDescendantStatus();
if (oldChild->isSelfPaintingLayer() || oldChild->hasSelfPaintingLayerDescendant())
dirtyAncestorChainHasSelfPaintingLayerDescendantStatus();
#if ENABLE(CSS_COMPOSITING)
if (oldChild->hasBlendMode() || (oldChild->hasNotIsolatedBlendingDescendants() && !oldChild->isolatesBlending()))
dirtyAncestorChainHasBlendingDescendants();
#endif
return oldChild;
}
void RenderLayer::removeOnlyThisLayer()
{
if (!m_parent)
return;
renderer().setHasLayer(false);
compositor().layerWillBeRemoved(*m_parent, *this);
clearClipRectsIncludingDescendants();
RenderLayer* nextSib = nextSibling();
if (reflection())
removeChild(reflectionLayer());
RenderLayer* current = m_first;
while (current) {
RenderLayer* next = current->nextSibling();
removeChild(current);
m_parent->addChild(current, nextSib);
current->setRepaintStatus(NeedsFullRepaint);
ASSERT(!renderer().hasLayer());
current->updateLayerPositions(); current = next;
}
m_parent->removeChild(this);
renderer().destroyLayer();
}
void RenderLayer::insertOnlyThisLayer()
{
if (!m_parent && renderer().parent()) {
RenderLayer* parentLayer = renderer().parent()->enclosingLayer();
ASSERT(parentLayer);
RenderLayer* beforeChild = parentLayer->reflectionLayer() != this ? renderer().parent()->findNextLayer(parentLayer, &renderer()) : nullptr;
parentLayer->addChild(this, beforeChild);
}
for (auto& child : childrenOfType<RenderElement>(renderer()))
child.moveLayers(m_parent, this);
clearClipRectsIncludingDescendants();
}
void RenderLayer::convertToPixelSnappedLayerCoords(const RenderLayer* ancestorLayer, IntPoint& roundedLocation, ColumnOffsetAdjustment adjustForColumns) const
{
LayoutPoint location = convertToLayerCoords(ancestorLayer, roundedLocation, adjustForColumns);
roundedLocation = roundedIntPoint(location);
}
static inline const RenderLayer* accumulateOffsetTowardsAncestor(const RenderLayer* layer, const RenderLayer* ancestorLayer, LayoutPoint& location, RenderLayer::ColumnOffsetAdjustment adjustForColumns)
{
ASSERT(ancestorLayer != layer);
const RenderLayerModelObject& renderer = layer->renderer();
EPosition position = renderer.style().position();
RenderFlowThread* fixedFlowThreadContainer = position == FixedPosition ? renderer.flowThreadContainingBlock() : nullptr;
if (fixedFlowThreadContainer && !fixedFlowThreadContainer->isOutOfFlowPositioned())
fixedFlowThreadContainer = nullptr;
if (position == FixedPosition && !fixedFlowThreadContainer && (!ancestorLayer || ancestorLayer == renderer.view().layer())) {
FloatPoint absPos = renderer.localToAbsolute(FloatPoint(), IsFixed);
location += LayoutSize(absPos.x(), absPos.y());
return ancestorLayer;
}
if (position == FixedPosition && !fixedFlowThreadContainer) {
RenderLayer* fixedPositionContainerLayer = nullptr;
bool foundAncestor = false;
for (RenderLayer* currLayer = layer->parent(); currLayer; currLayer = currLayer->parent()) {
if (currLayer == ancestorLayer)
foundAncestor = true;
if (isContainerForPositioned(*currLayer, FixedPosition)) {
fixedPositionContainerLayer = currLayer;
ASSERT_UNUSED(foundAncestor, foundAncestor);
break;
}
}
ASSERT(fixedPositionContainerLayer);
if (fixedPositionContainerLayer != ancestorLayer) {
LayoutSize fixedContainerCoords = layer->offsetFromAncestor(fixedPositionContainerLayer);
LayoutSize ancestorCoords = ancestorLayer->offsetFromAncestor(fixedPositionContainerLayer);
location += (fixedContainerCoords - ancestorCoords);
return ancestorLayer;
}
}
if (position == FixedPosition && fixedFlowThreadContainer) {
ASSERT(ancestorLayer);
if (ancestorLayer->isOutOfFlowRenderFlowThread()) {
location += toLayoutSize(layer->location());
return ancestorLayer;
}
if (ancestorLayer == renderer.view().layer()) {
location += toLayoutSize(layer->location());
FloatPoint absPos = renderer.view().localToAbsolute(FloatPoint(), IsFixed);
location += LayoutSize(absPos.x(), absPos.y());
return ancestorLayer;
}
}
RenderLayer* parentLayer;
if (position == AbsolutePosition || position == FixedPosition) {
parentLayer = layer->parent();
bool foundAncestorFirst = false;
while (parentLayer) {
if (isContainerForPositioned(*parentLayer, position))
break;
if (parentLayer == ancestorLayer) {
foundAncestorFirst = true;
break;
}
parentLayer = parentLayer->parent();
}
if (renderer.flowThreadContainingBlock() && !layer->isOutOfFlowRenderFlowThread())
ASSERT(parentLayer != renderer.view().layer());
if (foundAncestorFirst) {
RenderLayer* positionedAncestor = parentLayer->enclosingAncestorForPosition(position);
LayoutSize thisCoords = layer->offsetFromAncestor(positionedAncestor);
LayoutSize ancestorCoords = ancestorLayer->offsetFromAncestor(positionedAncestor);
location += (thisCoords - ancestorCoords);
return ancestorLayer;
}
} else
parentLayer = layer->parent();
if (!parentLayer)
return nullptr;
location += toLayoutSize(layer->location());
if (adjustForColumns == RenderLayer::AdjustForColumns) {
if (RenderLayer* parentLayer = layer->parent()) {
if (is<RenderMultiColumnFlowThread>(parentLayer->renderer())) {
RenderRegion* region = downcast<RenderMultiColumnFlowThread>(parentLayer->renderer()).physicalTranslationFromFlowToRegion(location);
if (region)
location.moveBy(region->topLeftLocation() + -parentLayer->renderBox()->topLeftLocation());
}
}
}
return parentLayer;
}
LayoutPoint RenderLayer::convertToLayerCoords(const RenderLayer* ancestorLayer, const LayoutPoint& location, ColumnOffsetAdjustment adjustForColumns) const
{
if (ancestorLayer == this)
return location;
const RenderLayer* currLayer = this;
LayoutPoint locationInLayerCoords = location;
while (currLayer && currLayer != ancestorLayer)
currLayer = accumulateOffsetTowardsAncestor(currLayer, ancestorLayer, locationInLayerCoords, adjustForColumns);
return locationInLayerCoords;
}
LayoutSize RenderLayer::offsetFromAncestor(const RenderLayer* ancestorLayer, ColumnOffsetAdjustment adjustForColumns) const
{
return toLayoutSize(convertToLayerCoords(ancestorLayer, LayoutPoint(), adjustForColumns));
}
#if PLATFORM(IOS)
bool RenderLayer::hasAcceleratedTouchScrolling() const
{
#if ENABLE(ACCELERATED_OVERFLOW_SCROLLING)
if (!scrollsOverflow())
return false;
return renderer().style().useTouchOverflowScrolling() || renderer().settings().alwaysUseAcceleratedOverflowScroll();
#else
return false;
#endif
}
bool RenderLayer::hasTouchScrollableOverflow() const
{
return hasAcceleratedTouchScrolling() && (hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
}
#if ENABLE(TOUCH_EVENTS)
bool RenderLayer::handleTouchEvent(const PlatformTouchEvent& touchEvent)
{
if (usesAcceleratedScrolling())
return false;
return ScrollableArea::handleTouchEvent(touchEvent);
}
#endif
#endif // PLATFORM(IOS)
bool RenderLayer::usesAcceleratedScrolling() const
{
#if PLATFORM(IOS)
return hasTouchScrollableOverflow();
#else
return needsCompositedScrolling();
#endif
}
#if ENABLE(IOS_TOUCH_EVENTS)
void RenderLayer::registerAsTouchEventListenerForScrolling()
{
if (!renderer().element() || m_registeredAsTouchEventListenerForScrolling)
return;
renderer().document().addTouchEventHandler(*renderer().element());
m_registeredAsTouchEventListenerForScrolling = true;
}
void RenderLayer::unregisterAsTouchEventListenerForScrolling()
{
if (!renderer().element() || !m_registeredAsTouchEventListenerForScrolling)
return;
renderer().document().removeTouchEventHandler(*renderer().element());
m_registeredAsTouchEventListenerForScrolling = false;
}
#endif // ENABLE(IOS_TOUCH_EVENTS)
bool RenderLayer::usesCompositedScrolling() const
{
return isComposited() && backing()->hasScrollingLayer();
}
bool RenderLayer::usesAsyncScrolling() const
{
return hasAcceleratedTouchScrolling() && usesCompositedScrolling();
}
bool RenderLayer::needsCompositedScrolling() const
{
return m_needsCompositedScrolling;
}
void RenderLayer::updateNeedsCompositedScrolling()
{
bool oldNeedsCompositedScrolling = m_needsCompositedScrolling;
if (!renderer().view().frameView().containsScrollableArea(this))
m_needsCompositedScrolling = false;
else {
bool forceUseCompositedScrolling = acceleratedCompositingForOverflowScrollEnabled()
&& canBeStackingContainer()
&& !hasOutOfFlowPositionedDescendant();
#if !PLATFORM(IOS) && ENABLE(ACCELERATED_OVERFLOW_SCROLLING)
m_needsCompositedScrolling = forceUseCompositedScrolling || renderer().style().useTouchOverflowScrolling();
#else
m_needsCompositedScrolling = forceUseCompositedScrolling;
#endif
}
if (oldNeedsCompositedScrolling != m_needsCompositedScrolling) {
updateSelfPaintingLayer();
if (isStackingContainer())
dirtyZOrderLists();
else
clearZOrderLists();
dirtyStackingContainerZOrderLists();
compositor().setShouldReevaluateCompositingAfterLayout();
compositor().setCompositingLayersNeedRebuild();
}
}
static inline int adjustedScrollDelta(int beginningDelta) {
const int speedReducer = 12;
int adjustedDelta = beginningDelta / speedReducer;
if (adjustedDelta > 1)
adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(adjustedDelta))) - 1;
else if (adjustedDelta < -1)
adjustedDelta = static_cast<int>(adjustedDelta * sqrt(static_cast<double>(-adjustedDelta))) + 1;
return adjustedDelta;
}
static inline IntSize adjustedScrollDelta(const IntSize& delta)
{
return IntSize(adjustedScrollDelta(delta.width()), adjustedScrollDelta(delta.height()));
}
void RenderLayer::panScrollFromPoint(const IntPoint& sourcePoint)
{
IntPoint lastKnownMousePosition = renderer().frame().eventHandler().lastKnownMousePosition();
static IntPoint previousMousePosition;
if (lastKnownMousePosition.x() < 0 || lastKnownMousePosition.y() < 0)
lastKnownMousePosition = previousMousePosition;
else
previousMousePosition = lastKnownMousePosition;
IntSize delta = lastKnownMousePosition - sourcePoint;
if (abs(delta.width()) <= ScrollView::noPanScrollRadius) delta.setWidth(0);
if (abs(delta.height()) <= ScrollView::noPanScrollRadius)
delta.setHeight(0);
scrollByRecursively(adjustedScrollDelta(delta), ScrollOffsetClamped);
}
void RenderLayer::scrollByRecursively(const IntSize& delta, ScrollOffsetClamping clamp, ScrollableArea** scrolledArea)
{
if (delta.isZero())
return;
bool restrictedByLineClamp = false;
if (renderer().parent())
restrictedByLineClamp = !renderer().parent()->style().lineClamp().isNone();
if (renderer().hasOverflowClip() && !restrictedByLineClamp) {
ScrollOffset newScrollOffset = scrollOffset() + delta;
scrollToOffset(newScrollOffset, clamp);
if (scrolledArea)
*scrolledArea = this;
IntSize remainingScrollOffset = newScrollOffset - scrollOffset();
if (!remainingScrollOffset.isZero() && renderer().parent()) {
if (RenderLayer* scrollableLayer = enclosingScrollableLayer())
scrollableLayer->scrollByRecursively(remainingScrollOffset, clamp, scrolledArea);
renderer().frame().eventHandler().updateAutoscrollRenderer();
}
} else {
renderer().view().frameView().scrollBy(delta);
if (scrolledArea)
*scrolledArea = &renderer().view().frameView();
}
}
void RenderLayer::setPostLayoutScrollPosition(std::optional<ScrollPosition> position)
{
m_postLayoutScrollPosition = position;
}
void RenderLayer::applyPostLayoutScrollPositionIfNeeded()
{
if (!m_postLayoutScrollPosition)
return;
scrollToOffset(scrollOffsetFromPosition(m_postLayoutScrollPosition.value()), ScrollOffsetClamped);
m_postLayoutScrollPosition = std::nullopt;
}
void RenderLayer::scrollToXPosition(int x, ScrollOffsetClamping clamp)
{
ScrollPosition position(x, m_scrollPosition.y());
scrollToOffset(scrollOffsetFromPosition(position), clamp);
}
void RenderLayer::scrollToYPosition(int y, ScrollOffsetClamping clamp)
{
ScrollPosition position(m_scrollPosition.x(), y);
scrollToOffset(scrollOffsetFromPosition(position), clamp);
}
ScrollOffset RenderLayer::clampScrollOffset(const ScrollOffset& scrollOffset) const
{
return scrollOffset.constrainedBetween(IntPoint(), maximumScrollOffset());
}
void RenderLayer::scrollToOffset(const ScrollOffset& scrollOffset, ScrollOffsetClamping clamp)
{
ScrollOffset newScrollOffset = clamp == ScrollOffsetClamped ? clampScrollOffset(scrollOffset) : scrollOffset;
if (newScrollOffset != this->scrollOffset())
scrollToOffsetWithoutAnimation(newScrollOffset);
}
void RenderLayer::scrollTo(const ScrollPosition& position)
{
RenderBox* box = renderBox();
if (!box)
return;
LOG_WITH_STREAM(Scrolling, stream << "RenderLayer::scrollTo " << position);
ScrollPosition newPosition = position;
if (!box->isHTMLMarquee()) {
if (m_scrollDimensionsDirty)
computeScrollDimensions();
#if PLATFORM(IOS)
if (adjustForIOSCaretWhenScrolling()) {
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
}
if (m_scrollPosition == newPosition) {
#if PLATFORM(IOS)
if (m_requiresScrollBoundsOriginUpdate)
updateCompositingLayersAfterScroll();
#endif
return;
}
m_scrollPosition = newPosition;
RenderView& view = renderer().view();
if (!view.frameView().isInRenderTreeLayout()) {
updateLayerPositionsAfterOverflowScroll();
#if ENABLE(DASHBOARD_SUPPORT)
view.frameView().updateAnnotatedRegions();
#endif
view.frameView().updateWidgetPositions();
if (!m_updatingMarqueePosition) {
updateCompositingLayersAfterScroll();
}
#if PLATFORM(IOS) && ENABLE(TOUCH_EVENTS)
renderer().document().setTouchEventRegionsNeedUpdate();
#endif
DebugPageOverlays::didLayout(renderer().frame());
}
Frame& frame = renderer().frame();
RenderLayerModelObject* repaintContainer = renderer().containerForRepaint();
frame.selection().setCaretRectNeedsUpdate();
LayoutRect rectForRepaint = m_hasComputedRepaintRect ? m_repaintRect : renderer().clippedOverflowRectForRepaint(repaintContainer);
FloatQuad quadForFakeMouseMoveEvent = FloatQuad(rectForRepaint);
if (repaintContainer)
quadForFakeMouseMoveEvent = repaintContainer->localToAbsoluteQuad(quadForFakeMouseMoveEvent);
frame.eventHandler().dispatchFakeMouseMoveEventSoonInQuad(quadForFakeMouseMoveEvent);
bool requiresRepaint = true;
if (compositor().inCompositingMode() && usesCompositedScrolling())
requiresRepaint = false;
if (requiresRepaint)
renderer().repaintUsingContainer(repaintContainer, rectForRepaint);
if (Element* element = renderer().element())
element->document().eventQueue().enqueueOrDispatchScrollEvent(*element);
if (scrollsOverflow())
view.frameView().didChangeScrollOffset();
view.frameView().viewportContentsChanged();
}
static inline bool frameElementAndViewPermitScroll(HTMLFrameElementBase* frameElementBase, FrameView& frameView)
{
if (frameElementBase && frameElementBase->scrollingMode() != ScrollbarAlwaysOff)
return true;
if (frameView.wasScrolledByUser())
return false;
return !frameView.frame().eventHandler().autoscrollInProgress();
}
bool RenderLayer::allowsCurrentScroll() const
{
if (!renderer().hasOverflowClip())
return false;
if (renderer().parent() && !renderer().parent()->style().lineClamp().isNone())
return false;
RenderBox* box = renderBox();
ASSERT(box);
if (renderer().frame().eventHandler().autoscrollInProgress()) {
return box->canBeProgramaticallyScrolled();
}
return box->hasHorizontalOverflow() || box->hasVerticalOverflow();
}
void RenderLayer::scrollRectToVisible(SelectionRevealMode revealMode, const LayoutRect& absoluteRect, bool insideFixed, const ScrollAlignment& alignX, const ScrollAlignment& alignY)
{
LOG_WITH_STREAM(Scrolling, stream << "Layer " << this << " scrollRectToVisible " << absoluteRect);
RenderLayer* parentLayer = nullptr;
LayoutRect newRect = absoluteRect;
FrameView& frameView = renderer().view().frameView();
if (renderer().parent())
parentLayer = renderer().parent()->enclosingLayer();
if (allowsCurrentScroll()) {
RenderBox* box = renderBox();
ASSERT(box);
LayoutRect localExposeRect(box->absoluteToLocalQuad(FloatQuad(FloatRect(absoluteRect))).boundingBox());
LayoutRect layerBounds(0, 0, box->clientWidth(), box->clientHeight());
LayoutRect revealRect = getRectToExpose(layerBounds, localExposeRect, insideFixed, alignX, alignY);
ScrollOffset clampedScrollOffset = clampScrollOffset(scrollOffset() + toIntSize(roundedIntRect(revealRect).location()));
if (clampedScrollOffset != 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().isRenderView()) {
HTMLFrameOwnerElement* ownerElement = renderer().document().ownerElement();
if (ownerElement && ownerElement->renderer()) {
HTMLFrameElementBase* frameElementBase = nullptr;
if (is<HTMLFrameElementBase>(*ownerElement))
frameElementBase = downcast<HTMLFrameElementBase>(ownerElement);
if (frameElementAndViewPermitScroll(frameElementBase, frameView)) {
NoEventDispatchAssertion assertNoEventDispatch;
LayoutRect viewRect = frameView.visibleContentRect(LegacyIOSDocumentVisibleRect);
LayoutRect exposeRect = getRectToExpose(viewRect, absoluteRect, insideFixed, alignX, alignY);
IntPoint scrollOffset(roundedIntPoint(exposeRect.location()));
scrollOffset = scrollOffset.constrainedBetween(IntPoint(), IntPoint(frameView.contentsSize()));
frameView.setScrollPosition(scrollOffset);
if (frameView.safeToPropagateScrollToParent()) {
parentLayer = ownerElement->renderer()->enclosingLayer();
newRect = frameView.contentsToContainingViewContents(enclosingIntRect(newRect));
insideFixed = false; } else
parentLayer = nullptr;
}
} else {
if (revealMode == SelectionRevealMode::RevealUpToMainFrame && frameView.frame().isMainFrame())
return;
#if !PLATFORM(IOS)
LayoutRect viewRect = frameView.visibleContentRect();
#else
LayoutRect viewRect = frameView.unobscuredContentRect();
#endif
LayoutRect targetRect = absoluteRect;
targetRect.move(0, frameView.headerHeight());
LayoutRect revealRect = getRectToExpose(viewRect, targetRect, insideFixed, alignX, alignY);
frameView.setScrollPosition(roundedIntPoint(revealRect.location()));
page().chrome().scrollRectIntoView(snappedIntRect(absoluteRect));
}
}
if (parentLayer)
parentLayer->scrollRectToVisible(revealMode, newRect, insideFixed, alignX, alignY);
}
void RenderLayer::updateCompositingLayersAfterScroll()
{
if (compositor().inCompositingMode()) {
if (RenderLayer* compositingAncestor = stackingContainer()->enclosingCompositingLayer()) {
if (usesCompositedScrolling() && !hasOutOfFlowPositionedDescendant())
compositor().updateCompositingLayers(CompositingUpdateOnCompositedScroll, compositingAncestor);
else
compositor().updateCompositingLayers(CompositingUpdateOnScroll, compositingAncestor);
}
}
}
LayoutRect RenderLayer::getRectToExpose(const LayoutRect &visibleRect, const LayoutRect &exposeRect, bool insideFixed, const ScrollAlignment& alignX, const ScrollAlignment& alignY) const
{
FrameView& frameView = renderer().view().frameView();
if (renderer().isRenderView() && insideFixed) {
if (frameView.frameScaleFactor() == 1)
return visibleRect;
if (renderer().settings().visualViewportEnabled()) {
LayoutRect unscaledExposeRect = exposeRect;
unscaledExposeRect.scale(1 / frameView.frameScaleFactor());
unscaledExposeRect.move(0, -frameView.headerHeight());
LayoutRect layoutViewport = frameView.layoutViewportRect();
LayoutRect visualViewport = frameView.visualViewportRect();
unscaledExposeRect.intersect(layoutViewport);
unscaledExposeRect.setSize(unscaledExposeRect.size().shrunkTo(visualViewport.size()));
LayoutRect requiredVisualViewport = getRectToExpose(visualViewport, unscaledExposeRect, false, alignX, alignY);
requiredVisualViewport.scale(frameView.frameScaleFactor());
requiredVisualViewport.move(0, frameView.headerHeight());
return requiredVisualViewport;
}
}
ScrollAlignment::Behavior scrollX;
LayoutRect exposeRectX(exposeRect.x(), visibleRect.y(), exposeRect.width(), visibleRect.height());
LayoutUnit intersectWidth = intersection(visibleRect, exposeRectX).width();
if (intersectWidth == exposeRect.width() || intersectWidth >= MIN_INTERSECT_FOR_REVEAL)
scrollX = ScrollAlignment::getVisibleBehavior(alignX);
else if (intersectWidth == visibleRect.width()) {
scrollX = ScrollAlignment::getVisibleBehavior(alignX);
if (scrollX == ScrollAlignment::Behavior::AlignCenter)
scrollX = ScrollAlignment::Behavior::NoScroll;
} else if (intersectWidth > 0)
scrollX = ScrollAlignment::getPartialBehavior(alignX);
else
scrollX = ScrollAlignment::getHiddenBehavior(alignX);
if (scrollX == ScrollAlignment::Behavior::AlignToClosestEdge && exposeRect.maxX() > visibleRect.maxX() && exposeRect.width() < visibleRect.width())
scrollX = ScrollAlignment::Behavior::AlignRight;
LayoutUnit x;
if (scrollX == ScrollAlignment::Behavior::NoScroll)
x = visibleRect.x();
else if (scrollX == ScrollAlignment::Behavior::AlignRight)
x = exposeRect.maxX() - visibleRect.width();
else if (scrollX == ScrollAlignment::Behavior::AlignCenter)
x = exposeRect.x() + (exposeRect.width() - visibleRect.width()) / 2;
else
x = exposeRect.x();
ScrollAlignment::Behavior scrollY;
LayoutRect exposeRectY(visibleRect.x(), exposeRect.y(), visibleRect.width(), exposeRect.height());
LayoutUnit intersectHeight = intersection(visibleRect, exposeRectY).height();
if (intersectHeight == exposeRect.height())
scrollY = ScrollAlignment::getVisibleBehavior(alignY);
else if (intersectHeight == visibleRect.height()) {
scrollY = ScrollAlignment::getVisibleBehavior(alignY);
if (scrollY == ScrollAlignment::Behavior::AlignCenter)
scrollY = ScrollAlignment::Behavior::NoScroll;
} else if (intersectHeight > 0)
scrollY = ScrollAlignment::getPartialBehavior(alignY);
else
scrollY = ScrollAlignment::getHiddenBehavior(alignY);
if (scrollY == ScrollAlignment::Behavior::AlignToClosestEdge && exposeRect.maxY() > visibleRect.maxY() && exposeRect.height() < visibleRect.height())
scrollY = ScrollAlignment::Behavior::AlignBottom;
LayoutUnit y;
if (scrollY == ScrollAlignment::Behavior::NoScroll)
y = visibleRect.y();
else if (scrollY == ScrollAlignment::Behavior::AlignBottom)
y = exposeRect.maxY() - visibleRect.height();
else if (scrollY == ScrollAlignment::Behavior::AlignCenter)
y = exposeRect.y() + (exposeRect.height() - visibleRect.height()) / 2;
else
y = exposeRect.y();
return LayoutRect(LayoutPoint(x, y), visibleRect.size());
}
void RenderLayer::autoscroll(const IntPoint& position)
{
IntPoint currentDocumentPosition = renderer().view().frameView().windowToContents(position);
scrollRectToVisible(SelectionRevealMode::Reveal, LayoutRect(currentDocumentPosition, LayoutSize(1, 1)), false, ScrollAlignment::alignToEdgeIfNeeded, ScrollAlignment::alignToEdgeIfNeeded);
}
bool RenderLayer::canResize() const
{
return (renderer().hasOverflowClip() || renderer().isRenderIFrame()) && renderer().style().resize() != RESIZE_NONE;
}
void RenderLayer::resize(const PlatformMouseEvent& evt, const LayoutSize& oldOffset)
{
if (!inResizeMode() || !canResize() || !renderer().element())
return;
Element* element = renderer().element();
auto* renderer = downcast<RenderBox>(element->renderer());
Document& document = element->document();
if (!document.frame()->eventHandler().mousePressed())
return;
float zoomFactor = renderer->style().effectiveZoom();
LayoutSize newOffset = offsetFromResizeCorner(document.view()->windowToContents(evt.position()));
newOffset.setWidth(newOffset.width() / zoomFactor);
newOffset.setHeight(newOffset.height() / zoomFactor);
LayoutSize currentSize = LayoutSize(renderer->width() / zoomFactor, renderer->height() / zoomFactor);
LayoutSize minimumSize = element->minimumSizeForResizing().shrunkTo(currentSize);
element->setMinimumSizeForResizing(minimumSize);
LayoutSize adjustedOldOffset = LayoutSize(oldOffset.width() / zoomFactor, oldOffset.height() / zoomFactor);
if (shouldPlaceBlockDirectionScrollbarOnLeft()) {
newOffset.setWidth(-newOffset.width());
adjustedOldOffset.setWidth(-adjustedOldOffset.width());
}
LayoutSize difference = (currentSize + newOffset - adjustedOldOffset).expandedTo(minimumSize) - currentSize;
StyledElement* styledElement = downcast<StyledElement>(element);
bool isBoxSizingBorder = renderer->style().boxSizing() == BORDER_BOX;
EResize resize = renderer->style().resize();
if (resize != RESIZE_VERTICAL && difference.width()) {
if (is<HTMLFormControlElement>(*element)) {
styledElement->setInlineStyleProperty(CSSPropertyMarginLeft, renderer->marginLeft() / zoomFactor, CSSPrimitiveValue::CSS_PX);
styledElement->setInlineStyleProperty(CSSPropertyMarginRight, renderer->marginRight() / zoomFactor, CSSPrimitiveValue::CSS_PX);
}
LayoutUnit baseWidth = renderer->width() - (isBoxSizingBorder ? LayoutUnit() : renderer->horizontalBorderAndPaddingExtent());
baseWidth = baseWidth / zoomFactor;
styledElement->setInlineStyleProperty(CSSPropertyWidth, roundToInt(baseWidth + difference.width()), CSSPrimitiveValue::CSS_PX);
}
if (resize != RESIZE_HORIZONTAL && difference.height()) {
if (is<HTMLFormControlElement>(*element)) {
styledElement->setInlineStyleProperty(CSSPropertyMarginTop, renderer->marginTop() / zoomFactor, CSSPrimitiveValue::CSS_PX);
styledElement->setInlineStyleProperty(CSSPropertyMarginBottom, renderer->marginBottom() / zoomFactor, CSSPrimitiveValue::CSS_PX);
}
LayoutUnit baseHeight = renderer->height() - (isBoxSizingBorder ? LayoutUnit() : renderer->verticalBorderAndPaddingExtent());
baseHeight = baseHeight / zoomFactor;
styledElement->setInlineStyleProperty(CSSPropertyHeight, roundToInt(baseHeight + difference.height()), CSSPrimitiveValue::CSS_PX);
}
document.updateLayout();
}
int RenderLayer::scrollSize(ScrollbarOrientation orientation) const
{
Scrollbar* scrollbar = ((orientation == HorizontalScrollbar) ? m_hBar : m_vBar).get();
return scrollbar ? (scrollbar->totalSize() - scrollbar->visibleSize()) : 0;
}
void RenderLayer::setScrollOffset(const ScrollOffset& offset)
{
scrollTo(scrollPositionFromOffset(offset));
}
int RenderLayer::scrollOffset(ScrollbarOrientation orientation) const
{
if (orientation == HorizontalScrollbar)
return scrollOffset().x();
if (orientation == VerticalScrollbar)
return scrollOffset().y();
return 0;
}
IntRect RenderLayer::visibleContentRectInternal(VisibleContentRectIncludesScrollbars scrollbarInclusion, VisibleContentRectBehavior) const
{
IntSize scrollbarSpace;
if (showsOverflowControls() && scrollbarInclusion == IncludeScrollbars)
scrollbarSpace = scrollbarIntrusion();
return IntRect(scrollPosition(), IntSize(std::max(0, m_layerSize.width() - scrollbarSpace.width()), std::max(0, m_layerSize.height() - scrollbarSpace.height())));
}
IntSize RenderLayer::overhangAmount() const
{
#if ENABLE(RUBBER_BANDING)
if (!renderer().settings().rubberBandingForSubScrollableRegionsEnabled())
return IntSize();
IntSize stretch;
ScrollOffset scrollOffset = scrollOffsetFromPosition(scrollPosition());
if (scrollOffset.y() < 0)
stretch.setHeight(scrollOffset.y());
else if (scrollableContentsSize().height() && scrollOffset.y() > scrollableContentsSize().height() - visibleHeight())
stretch.setHeight(scrollOffset.y() - (scrollableContentsSize().height() - visibleHeight()));
if (scrollOffset.x() < 0)
stretch.setWidth(scrollOffset.x());
else if (scrollableContentsSize().width() && scrollOffset.x() > scrollableContentsSize().width() - visibleWidth())
stretch.setWidth(scrollOffset.x() - (scrollableContentsSize().width() - visibleWidth()));
return stretch;
#else
return IntSize();
#endif
}
bool RenderLayer::isActive() const
{
return page().focusController().isActive();
}
static int cornerStart(const RenderLayer& layer, int minX, int maxX, int thickness)
{
if (layer.shouldPlaceBlockDirectionScrollbarOnLeft())
return minX + layer.renderer().style().borderLeftWidth();
return maxX - thickness - layer.renderer().style().borderRightWidth();
}
static LayoutRect cornerRect(const RenderLayer& layer, const LayoutRect& bounds)
{
int horizontalThickness;
int verticalThickness;
if (!layer.verticalScrollbar() && !layer.horizontalScrollbar()) {
horizontalThickness = ScrollbarTheme::theme().scrollbarThickness();
verticalThickness = horizontalThickness;
} else if (layer.verticalScrollbar() && !layer.horizontalScrollbar()) {
horizontalThickness = layer.verticalScrollbar()->width();
verticalThickness = horizontalThickness;
} else if (layer.horizontalScrollbar() && !layer.verticalScrollbar()) {
verticalThickness = layer.horizontalScrollbar()->height();
horizontalThickness = verticalThickness;
} else {
horizontalThickness = layer.verticalScrollbar()->width();
verticalThickness = layer.horizontalScrollbar()->height();
}
return LayoutRect(cornerStart(layer, bounds.x(), bounds.maxX(), horizontalThickness),
bounds.maxY() - verticalThickness - layer.renderer().style().borderBottomWidth(),
horizontalThickness, verticalThickness);
}
IntRect RenderLayer::scrollCornerRect() const
{
bool hasHorizontalBar = m_hBar && !m_hBar->isOverlayScrollbar();
bool hasVerticalBar = m_vBar && !m_vBar->isOverlayScrollbar();
bool hasResizer = renderer().style().resize() != RESIZE_NONE;
if ((hasHorizontalBar && hasVerticalBar) || (hasResizer && (hasHorizontalBar || hasVerticalBar)))
return snappedIntRect(cornerRect(*this, renderBox()->borderBoxRect()));
return IntRect();
}
static LayoutRect resizerCornerRect(const RenderLayer& layer, const LayoutRect& bounds)
{
ASSERT(layer.renderer().isBox());
if (layer.renderer().style().resize() == RESIZE_NONE)
return LayoutRect();
return cornerRect(layer, bounds);
}
LayoutRect RenderLayer::scrollCornerAndResizerRect() const
{
RenderBox* box = renderBox();
if (!box)
return LayoutRect();
LayoutRect scrollCornerAndResizer = scrollCornerRect();
if (scrollCornerAndResizer.isEmpty())
scrollCornerAndResizer = resizerCornerRect(*this, box->borderBoxRect());
return scrollCornerAndResizer;
}
bool RenderLayer::isScrollCornerVisible() const
{
ASSERT(renderer().isBox());
return !scrollCornerRect().isEmpty();
}
IntRect RenderLayer::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntRect& scrollbarRect) const
{
IntRect rect = scrollbarRect;
rect.move(scrollbarOffset(scrollbar));
return renderer().view().frameView().convertFromRendererToContainingView(&renderer(), rect);
}
IntRect RenderLayer::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntRect& parentRect) const
{
IntRect rect = renderer().view().frameView().convertFromContainingViewToRenderer(&renderer(), parentRect);
rect.move(-scrollbarOffset(scrollbar));
return rect;
}
IntPoint RenderLayer::convertFromScrollbarToContainingView(const Scrollbar& scrollbar, const IntPoint& scrollbarPoint) const
{
IntPoint point = scrollbarPoint;
point.move(scrollbarOffset(scrollbar));
return renderer().view().frameView().convertFromRendererToContainingView(&renderer(), point);
}
IntPoint RenderLayer::convertFromContainingViewToScrollbar(const Scrollbar& scrollbar, const IntPoint& parentPoint) const
{
IntPoint point = renderer().view().frameView().convertFromContainingViewToRenderer(&renderer(), parentPoint);
point.move(-scrollbarOffset(scrollbar));
return point;
}
IntSize RenderLayer::visibleSize() const
{
RenderBox* box = renderBox();
if (!box)
return IntSize();
return IntSize(roundToInt(box->clientWidth()), roundToInt(box->clientHeight()));
}
IntSize RenderLayer::contentsSize() const
{
return IntSize(scrollWidth(), scrollHeight());
}
IntSize RenderLayer::scrollableContentsSize() const
{
IntSize contentsSize = this->contentsSize();
if (!hasScrollableHorizontalOverflow())
contentsSize.setWidth(std::min(contentsSize.width(), visibleSize().width()));
if (!hasScrollableVerticalOverflow())
contentsSize.setHeight(std::min(contentsSize.height(), visibleSize().height()));
return contentsSize;
}
void RenderLayer::availableContentSizeChanged(AvailableSizeChangeReason reason)
{
ScrollableArea::availableContentSizeChanged(reason);
if (reason == AvailableSizeChangeReason::ScrollbarsChanged) {
if (is<RenderBlock>(renderer()))
downcast<RenderBlock>(renderer()).setShouldForceRelayoutChildren(true);
renderer().setNeedsLayout();
}
}
bool RenderLayer::shouldSuspendScrollAnimations() const
{
return renderer().view().frameView().shouldSuspendScrollAnimations();
}
#if PLATFORM(IOS)
void RenderLayer::didStartScroll()
{
page().chrome().client().didStartOverflowScroll();
}
void RenderLayer::didEndScroll()
{
page().chrome().client().didEndOverflowScroll();
}
void RenderLayer::didUpdateScroll()
{
page().chrome().client().didLayout(ChromeClient::Scroll);
}
#endif
IntPoint RenderLayer::lastKnownMousePosition() const
{
return renderer().frame().eventHandler().lastKnownMousePosition();
}
bool RenderLayer::isHandlingWheelEvent() const
{
return renderer().frame().eventHandler().isHandlingWheelEvent();
}
IntRect RenderLayer::rectForHorizontalScrollbar(const IntRect& borderBoxRect) const
{
if (!m_hBar)
return IntRect();
const RenderBox* box = renderBox();
const IntRect& scrollCorner = scrollCornerRect();
return IntRect(horizontalScrollbarStart(borderBoxRect.x()),
borderBoxRect.maxY() - box->borderBottom() - m_hBar->height(),
borderBoxRect.width() - (box->borderLeft() + box->borderRight()) - scrollCorner.width(),
m_hBar->height());
}
IntRect RenderLayer::rectForVerticalScrollbar(const IntRect& borderBoxRect) const
{
if (!m_vBar)
return IntRect();
const RenderBox* box = renderBox();
const IntRect& scrollCorner = scrollCornerRect();
return IntRect(verticalScrollbarStart(borderBoxRect.x(), borderBoxRect.maxX()),
borderBoxRect.y() + box->borderTop(),
m_vBar->width(),
borderBoxRect.height() - (box->borderTop() + box->borderBottom()) - scrollCorner.height());
}
LayoutUnit RenderLayer::verticalScrollbarStart(int minX, int maxX) const
{
const RenderBox* box = renderBox();
if (shouldPlaceBlockDirectionScrollbarOnLeft())
return minX + box->borderLeft();
return maxX - box->borderRight() - m_vBar->width();
}
LayoutUnit RenderLayer::horizontalScrollbarStart(int minX) const
{
const RenderBox* box = renderBox();
int x = minX + box->borderLeft();
if (shouldPlaceBlockDirectionScrollbarOnLeft())
x += m_vBar ? m_vBar->width() : roundToInt(resizerCornerRect(*this, box->borderBoxRect()).width());
return x;
}
IntSize RenderLayer::scrollbarOffset(const Scrollbar& scrollbar) const
{
RenderBox* box = renderBox();
if (&scrollbar == m_vBar.get())
return IntSize(verticalScrollbarStart(0, box->width()), box->borderTop());
if (&scrollbar == m_hBar.get())
return IntSize(horizontalScrollbarStart(0), box->height() - box->borderBottom() - scrollbar.height());
ASSERT_NOT_REACHED();
return IntSize();
}
void RenderLayer::invalidateScrollbarRect(Scrollbar& scrollbar, const IntRect& rect)
{
if (!showsOverflowControls())
return;
if (&scrollbar == m_vBar.get()) {
if (GraphicsLayer* layer = layerForVerticalScrollbar()) {
layer->setNeedsDisplayInRect(rect);
return;
}
} else {
if (GraphicsLayer* layer = layerForHorizontalScrollbar()) {
layer->setNeedsDisplayInRect(rect);
return;
}
}
IntRect scrollRect = rect;
RenderBox* box = renderBox();
ASSERT(box);
if (!box->parent())
return;
if (&scrollbar == m_vBar.get())
scrollRect.move(verticalScrollbarStart(0, box->width()), box->borderTop());
else
scrollRect.move(horizontalScrollbarStart(0), box->height() - box->borderBottom() - scrollbar.height());
LayoutRect repaintRect = scrollRect;
renderBox()->flipForWritingMode(repaintRect);
renderer().repaintRectangle(repaintRect);
}
void RenderLayer::invalidateScrollCornerRect(const IntRect& rect)
{
if (!showsOverflowControls())
return;
if (GraphicsLayer* layer = layerForScrollCorner()) {
layer->setNeedsDisplayInRect(rect);
return;
}
if (m_scrollCorner)
m_scrollCorner->repaintRectangle(rect);
if (m_resizer)
m_resizer->repaintRectangle(rect);
}
static inline RenderElement* rendererForScrollbar(RenderLayerModelObject& renderer)
{
if (Element* element = renderer.element()) {
if (ShadowRoot* shadowRoot = element->containingShadowRoot()) {
if (shadowRoot->mode() == ShadowRootMode::UserAgent)
return shadowRoot->host()->renderer();
}
}
return &renderer;
}
Ref<Scrollbar> RenderLayer::createScrollbar(ScrollbarOrientation orientation)
{
RefPtr<Scrollbar> widget;
ASSERT(rendererForScrollbar(renderer()));
auto& actualRenderer = *rendererForScrollbar(renderer());
bool hasCustomScrollbarStyle = is<RenderBox>(actualRenderer) && downcast<RenderBox>(actualRenderer).style().hasPseudoStyle(SCROLLBAR);
if (hasCustomScrollbarStyle)
widget = RenderScrollbar::createCustomScrollbar(*this, orientation, downcast<RenderBox>(actualRenderer).element());
else {
widget = Scrollbar::createNativeScrollbar(*this, orientation, RegularScrollbar);
didAddScrollbar(widget.get(), orientation);
if (page().expectsWheelEventTriggers())
scrollAnimator().setWheelEventTestTrigger(page().testTrigger());
}
renderer().view().frameView().addChild(*widget);
return widget.releaseNonNull();
}
void RenderLayer::destroyScrollbar(ScrollbarOrientation orientation)
{
RefPtr<Scrollbar>& scrollbar = orientation == HorizontalScrollbar ? m_hBar : m_vBar;
if (!scrollbar)
return;
if (!scrollbar->isCustomScrollbar())
willRemoveScrollbar(scrollbar.get(), orientation);
scrollbar->removeFromParent();
scrollbar = nullptr;
}
bool RenderLayer::scrollsOverflow() const
{
if (!is<RenderBox>(renderer()))
return false;
return downcast<RenderBox>(renderer()).scrollsOverflow();
}
void RenderLayer::setHasHorizontalScrollbar(bool hasScrollbar)
{
if (hasScrollbar == hasHorizontalScrollbar())
return;
if (hasScrollbar) {
m_hBar = createScrollbar(HorizontalScrollbar);
#if ENABLE(RUBBER_BANDING)
ScrollElasticity elasticity = scrollsOverflow() && renderer().settings().rubberBandingForSubScrollableRegionsEnabled() ? ScrollElasticityAutomatic : ScrollElasticityNone;
ScrollableArea::setHorizontalScrollElasticity(elasticity);
#endif
} else {
destroyScrollbar(HorizontalScrollbar);
#if ENABLE(RUBBER_BANDING)
ScrollableArea::setHorizontalScrollElasticity(ScrollElasticityNone);
#endif
}
if (m_hBar)
m_hBar->styleChanged();
if (m_vBar)
m_vBar->styleChanged();
#if ENABLE(DASHBOARD_SUPPORT)
if (renderer().document().hasAnnotatedRegions())
renderer().document().setAnnotatedRegionsDirty(true);
#endif
}
void RenderLayer::setHasVerticalScrollbar(bool hasScrollbar)
{
if (hasScrollbar == hasVerticalScrollbar())
return;
if (hasScrollbar) {
m_vBar = createScrollbar(VerticalScrollbar);
#if ENABLE(RUBBER_BANDING)
ScrollElasticity elasticity = scrollsOverflow() && renderer().settings().rubberBandingForSubScrollableRegionsEnabled() ? ScrollElasticityAutomatic : ScrollElasticityNone;
ScrollableArea::setVerticalScrollElasticity(elasticity);
#endif
} else {
destroyScrollbar(VerticalScrollbar);
#if ENABLE(RUBBER_BANDING)
ScrollableArea::setVerticalScrollElasticity(ScrollElasticityNone);
#endif
}
if (m_hBar)
m_hBar->styleChanged();
if (m_vBar)
m_vBar->styleChanged();
#if ENABLE(DASHBOARD_SUPPORT)
if (renderer().document().hasAnnotatedRegions())
renderer().document().setAnnotatedRegionsDirty(true);
#endif
}
ScrollableArea* RenderLayer::enclosingScrollableArea() const
{
if (RenderLayer* scrollableLayer = enclosingScrollableLayer())
return scrollableLayer;
return nullptr;
}
bool RenderLayer::isScrollableOrRubberbandable()
{
return renderer().isScrollableOrRubberbandableBox();
}
bool RenderLayer::hasScrollableOrRubberbandableAncestor()
{
for (RenderLayer* nextLayer = parentLayerCrossFrame(*this); nextLayer; nextLayer = parentLayerCrossFrame(*nextLayer)) {
if (nextLayer->isScrollableOrRubberbandable())
return true;
}
return false;
}
#if ENABLE(CSS_SCROLL_SNAP)
void RenderLayer::updateSnapOffsets()
{
if (!is<HTMLElement>(enclosingElement()) || !enclosingElement()->renderBox())
return;
RenderBox* box = enclosingElement()->renderBox();
updateSnapOffsetsForScrollableArea(*this, *downcast<HTMLElement>(enclosingElement()), *box, box->style());
}
bool RenderLayer::isScrollSnapInProgress() const
{
if (!scrollsOverflow())
return false;
if (ScrollAnimator* scrollAnimator = existingScrollAnimator())
return scrollAnimator->isScrollSnapInProgress();
return false;
}
#endif
bool RenderLayer::usesMockScrollAnimator() const
{
return Settings::usesMockScrollAnimator();
}
void RenderLayer::logMockScrollAnimatorMessage(const String& message) const
{
renderer().document().addConsoleMessage(MessageSource::Other, MessageLevel::Debug, "RenderLayer: " + message);
}
int RenderLayer::verticalScrollbarWidth(OverlayScrollbarSizeRelevancy relevancy) const
{
if (!m_vBar
|| !showsOverflowControls()
|| (m_vBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_vBar->shouldParticipateInHitTesting())))
return 0;
return m_vBar->width();
}
int RenderLayer::horizontalScrollbarHeight(OverlayScrollbarSizeRelevancy relevancy) const
{
if (!m_hBar
|| !showsOverflowControls()
|| (m_hBar->isOverlayScrollbar() && (relevancy == IgnoreOverlayScrollbarSize || !m_hBar->shouldParticipateInHitTesting())))
return 0;
return m_hBar->height();
}
IntSize RenderLayer::offsetFromResizeCorner(const IntPoint& absolutePoint) const
{
IntSize elementSize = size();
if (shouldPlaceBlockDirectionScrollbarOnLeft())
elementSize.setWidth(0);
IntPoint resizerPoint = IntPoint(elementSize);
IntPoint localPoint = roundedIntPoint(absoluteToContents(absolutePoint));
return localPoint - resizerPoint;
}
bool RenderLayer::hasOverflowControls() const
{
return m_hBar || m_vBar || m_scrollCorner || renderer().style().resize() != RESIZE_NONE;
}
void RenderLayer::positionOverflowControls(const IntSize& offsetFromRoot)
{
if (!m_hBar && !m_vBar && !canResize())
return;
RenderBox* box = renderBox();
if (!box)
return;
const IntRect borderBox = snappedIntRect(box->borderBoxRect());
const IntRect& scrollCorner = scrollCornerRect();
IntRect absBounds(borderBox.location() + offsetFromRoot, borderBox.size());
if (m_vBar) {
IntRect vBarRect = rectForVerticalScrollbar(borderBox);
vBarRect.move(offsetFromRoot);
m_vBar->setFrameRect(vBarRect);
}
if (m_hBar) {
IntRect hBarRect = rectForHorizontalScrollbar(borderBox);
hBarRect.move(offsetFromRoot);
m_hBar->setFrameRect(hBarRect);
}
if (m_scrollCorner)
m_scrollCorner->setFrameRect(scrollCorner);
if (m_resizer)
m_resizer->setFrameRect(resizerCornerRect(*this, borderBox));
if (isComposited())
backing()->positionOverflowControlsLayers();
}
int RenderLayer::scrollWidth() const
{
ASSERT(renderBox());
if (m_scrollDimensionsDirty)
const_cast<RenderLayer*>(this)->computeScrollDimensions();
return m_scrollSize.width();
}
int RenderLayer::scrollHeight() const
{
ASSERT(renderBox());
if (m_scrollDimensionsDirty)
const_cast<RenderLayer*>(this)->computeScrollDimensions();
return m_scrollSize.height();
}
LayoutUnit RenderLayer::overflowTop() const
{
RenderBox* box = renderBox();
LayoutRect overflowRect(box->layoutOverflowRect());
box->flipForWritingMode(overflowRect);
return overflowRect.y();
}
LayoutUnit RenderLayer::overflowBottom() const
{
RenderBox* box = renderBox();
LayoutRect overflowRect(box->layoutOverflowRect());
box->flipForWritingMode(overflowRect);
return overflowRect.maxY();
}
LayoutUnit RenderLayer::overflowLeft() const
{
RenderBox* box = renderBox();
LayoutRect overflowRect(box->layoutOverflowRect());
box->flipForWritingMode(overflowRect);
return overflowRect.x();
}
LayoutUnit RenderLayer::overflowRight() const
{
RenderBox* box = renderBox();
LayoutRect overflowRect(box->layoutOverflowRect());
box->flipForWritingMode(overflowRect);
return overflowRect.maxX();
}
void RenderLayer::computeScrollDimensions()
{
RenderBox* box = renderBox();
ASSERT(box);
m_scrollDimensionsDirty = false;
m_scrollSize.setWidth(roundToInt(overflowRight() - overflowLeft()));
m_scrollSize.setHeight(roundToInt(overflowBottom() - overflowTop()));
int scrollableLeftOverflow = roundToInt(overflowLeft() - box->borderLeft());
if (shouldPlaceBlockDirectionScrollbarOnLeft())
scrollableLeftOverflow -= verticalScrollbarWidth();
int scrollableTopOverflow = roundToInt(overflowTop() - box->borderTop());
setScrollOrigin(IntPoint(-scrollableLeftOverflow, -scrollableTopOverflow));
}
bool RenderLayer::hasScrollableHorizontalOverflow() const
{
return hasHorizontalOverflow() && renderBox()->scrollsOverflowX();
}
bool RenderLayer::hasScrollableVerticalOverflow() const
{
return hasVerticalOverflow() && renderBox()->scrollsOverflowY();
}
bool RenderLayer::hasHorizontalOverflow() const
{
ASSERT(!m_scrollDimensionsDirty);
return scrollWidth() > roundToInt(renderBox()->clientWidth());
}
bool RenderLayer::hasVerticalOverflow() const
{
ASSERT(!m_scrollDimensionsDirty);
return scrollHeight() > roundToInt(renderBox()->clientHeight());
}
static bool styleRequiresScrollbar(const RenderStyle& style, ScrollbarOrientation axis)
{
EOverflow overflow = axis == ScrollbarOrientation::HorizontalScrollbar ? style.overflowX() : style.overflowY();
bool overflowScrollActsLikeAuto = overflow == OSCROLL && !style.hasPseudoStyle(SCROLLBAR) && ScrollbarTheme::theme().usesOverlayScrollbars();
return overflow == OSCROLL && !overflowScrollActsLikeAuto;
}
static bool styleDefinesAutomaticScrollbar(const RenderStyle& style, ScrollbarOrientation axis)
{
EOverflow overflow = axis == ScrollbarOrientation::HorizontalScrollbar ? style.overflowX() : style.overflowY();
bool overflowScrollActsLikeAuto = overflow == OSCROLL && !style.hasPseudoStyle(SCROLLBAR) && ScrollbarTheme::theme().usesOverlayScrollbars();
return overflow == OAUTO || overflow == OOVERLAY || overflowScrollActsLikeAuto;
}
void RenderLayer::updateScrollbarsAfterLayout()
{
RenderBox* box = renderBox();
ASSERT(box);
if (box->style().appearance() == ListboxPart)
return;
bool hasHorizontalOverflow = this->hasHorizontalOverflow();
bool hasVerticalOverflow = this->hasVerticalOverflow();
if (m_hBar && styleRequiresScrollbar(renderer().style(), HorizontalScrollbar))
m_hBar->setEnabled(hasHorizontalOverflow);
if (m_vBar && styleRequiresScrollbar(renderer().style(), VerticalScrollbar))
m_vBar->setEnabled(hasVerticalOverflow);
bool autoHorizontalScrollBarChanged = box->hasHorizontalScrollbarWithAutoBehavior() && (hasHorizontalScrollbar() != hasHorizontalOverflow);
bool autoVerticalScrollBarChanged = box->hasVerticalScrollbarWithAutoBehavior() && (hasVerticalScrollbar() != hasVerticalOverflow);
if (autoHorizontalScrollBarChanged || autoVerticalScrollBarChanged) {
if (box->hasHorizontalScrollbarWithAutoBehavior())
setHasHorizontalScrollbar(hasHorizontalOverflow);
if (box->hasVerticalScrollbarWithAutoBehavior())
setHasVerticalScrollbar(hasVerticalOverflow);
updateSelfPaintingLayer();
#if ENABLE(DASHBOARD_SUPPORT)
if (renderer().document().hasAnnotatedRegions())
renderer().document().setAnnotatedRegionsDirty(true);
#endif
renderer().repaint();
if (renderer().style().overflowX() == OAUTO || renderer().style().overflowY() == OAUTO) {
if (!m_inOverflowRelayout) {
m_inOverflowRelayout = true;
renderer().setNeedsLayout(MarkOnlyThis);
if (is<RenderBlock>(renderer())) {
RenderBlock& block = downcast<RenderBlock>(renderer());
block.scrollbarsChanged(autoHorizontalScrollBarChanged, autoVerticalScrollBarChanged);
block.layoutBlock(true);
} else
renderer().layout();
m_inOverflowRelayout = false;
}
}
RenderObject* parent = renderer().parent();
if (parent && parent->isFlexibleBox() && renderer().isBox())
downcast<RenderFlexibleBox>(parent)->clearCachedMainSizeForChild(*renderBox());
}
if (m_hBar) {
int clientWidth = roundToInt(box->clientWidth());
int pageStep = Scrollbar::pageStep(clientWidth);
m_hBar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
m_hBar->setProportion(clientWidth, m_scrollSize.width());
}
if (m_vBar) {
int clientHeight = roundToInt(box->clientHeight());
int pageStep = Scrollbar::pageStep(clientHeight);
m_vBar->setSteps(Scrollbar::pixelsPerLineStep(), pageStep);
m_vBar->setProportion(clientHeight, m_scrollSize.height());
}
updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
}
void RenderLayer::updateScrollInfoAfterLayout()
{
RenderBox* box = renderBox();
if (!box)
return;
m_scrollDimensionsDirty = true;
ScrollOffset originalScrollOffset = scrollOffset();
computeScrollDimensions();
#if ENABLE(CSS_SCROLL_SNAP)
updateSnapOffsets();
#endif
if (!box->isHTMLMarquee() && !isRubberBandInProgress()) {
ScrollOffset clampedScrollOffset = clampScrollOffset(scrollOffset());
#if PLATFORM(IOS)
setAdjustForIOSCaretWhenScrolling(true);
#endif
if (clampedScrollOffset != scrollOffset())
scrollToOffset(clampedScrollOffset);
#if PLATFORM(IOS)
setAdjustForIOSCaretWhenScrolling(false);
#endif
}
updateScrollbarsAfterLayout();
if (originalScrollOffset != scrollOffset())
scrollToOffsetWithoutAnimation(IntPoint(scrollOffset()));
if (compositor().updateLayerCompositingState(*this))
compositor().setCompositingLayersNeedRebuild();
updateScrollSnapState();
}
bool RenderLayer::overflowControlsIntersectRect(const IntRect& localRect) const
{
const IntRect borderBox = snappedIntRect(renderBox()->borderBoxRect());
if (rectForHorizontalScrollbar(borderBox).intersects(localRect))
return true;
if (rectForVerticalScrollbar(borderBox).intersects(localRect))
return true;
if (scrollCornerRect().intersects(localRect))
return true;
if (resizerCornerRect(*this, borderBox).intersects(localRect))
return true;
return false;
}
bool RenderLayer::showsOverflowControls() const
{
#if PLATFORM(IOS)
if (hasAcceleratedTouchScrolling())
return false;
#endif
return true;
}
void RenderLayer::paintOverflowControls(GraphicsContext& context, const IntPoint& paintOffset, const IntRect& damageRect, bool paintingOverlayControls)
{
if (!renderer().hasOverflowClip())
return;
if (!showsOverflowControls())
return;
if (hasOverlayScrollbars() && !paintingOverlayControls) {
m_cachedOverlayScrollbarOffset = paintOffset;
if ((m_hBar && layerForHorizontalScrollbar()) || (m_vBar && layerForVerticalScrollbar()))
return;
IntRect localDamgeRect = damageRect;
localDamgeRect.moveBy(-paintOffset);
if (!overflowControlsIntersectRect(localDamgeRect))
return;
RenderLayer* paintingRoot = enclosingCompositingLayer();
if (!paintingRoot)
paintingRoot = renderer().view().layer();
paintingRoot->setContainsDirtyOverlayScrollbars(true);
return;
}
if (paintingOverlayControls && !hasOverlayScrollbars())
return;
IntPoint adjustedPaintOffset = paintOffset;
if (paintingOverlayControls)
adjustedPaintOffset = m_cachedOverlayScrollbarOffset;
positionOverflowControls(toIntSize(adjustedPaintOffset));
if (m_hBar && !layerForHorizontalScrollbar())
m_hBar->paint(context, damageRect);
if (m_vBar && !layerForVerticalScrollbar())
m_vBar->paint(context, damageRect);
if (layerForScrollCorner())
return;
paintScrollCorner(context, adjustedPaintOffset, damageRect);
paintResizer(context, adjustedPaintOffset, damageRect);
}
void RenderLayer::paintScrollCorner(GraphicsContext& context, const IntPoint& paintOffset, const IntRect& damageRect)
{
IntRect absRect = scrollCornerRect();
absRect.moveBy(paintOffset);
if (!absRect.intersects(damageRect))
return;
if (context.updatingControlTints()) {
updateScrollCornerStyle();
return;
}
if (m_scrollCorner) {
m_scrollCorner->paintIntoRect(context, paintOffset, absRect);
return;
}
if (!hasOverlayScrollbars())
context.fillRect(absRect, Color::white);
}
void RenderLayer::drawPlatformResizerImage(GraphicsContext& context, const LayoutRect& resizerCornerRect)
{
RefPtr<Image> resizeCornerImage;
FloatSize cornerResizerSize;
if (renderer().document().deviceScaleFactor() >= 2) {
static NeverDestroyed<Image*> resizeCornerImageHiRes(&Image::loadPlatformResource("textAreaResizeCorner@2x").leakRef());
resizeCornerImage = resizeCornerImageHiRes;
cornerResizerSize = resizeCornerImage->size();
cornerResizerSize.scale(0.5f);
} else {
static NeverDestroyed<Image*> resizeCornerImageLoRes(&Image::loadPlatformResource("textAreaResizeCorner").leakRef());
resizeCornerImage = resizeCornerImageLoRes;
cornerResizerSize = resizeCornerImage->size();
}
if (shouldPlaceBlockDirectionScrollbarOnLeft()) {
context.save();
context.translate(resizerCornerRect.x() + cornerResizerSize.width(), resizerCornerRect.y() + resizerCornerRect.height() - cornerResizerSize.height());
context.scale(FloatSize(-1.0, 1.0));
if (resizeCornerImage)
context.drawImage(*resizeCornerImage, FloatRect(FloatPoint(), cornerResizerSize));
context.restore();
return;
}
if (!resizeCornerImage)
return;
FloatRect imageRect = snapRectToDevicePixels(LayoutRect(resizerCornerRect.maxXMaxYCorner() - cornerResizerSize, cornerResizerSize), renderer().document().deviceScaleFactor());
context.drawImage(*resizeCornerImage, imageRect);
}
void RenderLayer::paintResizer(GraphicsContext& context, const LayoutPoint& paintOffset, const LayoutRect& damageRect)
{
if (renderer().style().resize() == RESIZE_NONE)
return;
RenderBox* box = renderBox();
ASSERT(box);
LayoutRect absRect = resizerCornerRect(*this, box->borderBoxRect());
absRect.moveBy(paintOffset);
if (!absRect.intersects(damageRect))
return;
if (context.updatingControlTints()) {
updateResizerStyle();
return;
}
if (m_resizer) {
m_resizer->paintIntoRect(context, paintOffset, absRect);
return;
}
drawPlatformResizerImage(context, absRect);
if (!hasOverlayScrollbars() && (m_vBar || m_hBar)) {
GraphicsContextStateSaver stateSaver(context);
context.clip(absRect);
LayoutRect largerCorner = absRect;
largerCorner.setSize(LayoutSize(largerCorner.width() + LayoutUnit::fromPixel(1), largerCorner.height() + LayoutUnit::fromPixel(1)));
context.setStrokeColor(Color(makeRGB(217, 217, 217)));
context.setStrokeThickness(1.0f);
context.setFillColor(Color::transparent);
context.drawRect(snappedIntRect(largerCorner));
}
}
bool RenderLayer::isPointInResizeControl(const IntPoint& absolutePoint) const
{
if (!canResize())
return false;
RenderBox* box = renderBox();
ASSERT(box);
IntPoint localPoint = roundedIntPoint(absoluteToContents(absolutePoint));
IntRect localBounds(IntPoint(), snappedIntRect(box->frameRect()).size());
return resizerCornerRect(*this, localBounds).contains(localPoint);
}
bool RenderLayer::hitTestOverflowControls(HitTestResult& result, const IntPoint& localPoint)
{
if (!m_hBar && !m_vBar && !canResize())
return false;
RenderBox* box = renderBox();
ASSERT(box);
IntRect resizeControlRect;
if (renderer().style().resize() != RESIZE_NONE) {
resizeControlRect = snappedIntRect(resizerCornerRect(*this, box->borderBoxRect()));
if (resizeControlRect.contains(localPoint))
return true;
}
int resizeControlSize = std::max(resizeControlRect.height(), 0);
if (m_vBar && m_vBar->shouldParticipateInHitTesting()) {
LayoutRect vBarRect(verticalScrollbarStart(0, box->width()),
box->borderTop(),
m_vBar->width(),
box->height() - (box->borderTop() + box->borderBottom()) - (m_hBar ? m_hBar->height() : resizeControlSize));
if (vBarRect.contains(localPoint)) {
result.setScrollbar(m_vBar.get());
return true;
}
}
resizeControlSize = std::max(resizeControlRect.width(), 0);
if (m_hBar && m_hBar->shouldParticipateInHitTesting()) {
LayoutRect hBarRect(horizontalScrollbarStart(0),
box->height() - box->borderBottom() - m_hBar->height(),
box->width() - (box->borderLeft() + box->borderRight()) - (m_vBar ? m_vBar->width() : resizeControlSize),
m_hBar->height());
if (hBarRect.contains(localPoint)) {
result.setScrollbar(m_hBar.get());
return true;
}
}
return false;
}
bool RenderLayer::scroll(ScrollDirection direction, ScrollGranularity granularity, float multiplier)
{
return ScrollableArea::scroll(direction, granularity, multiplier);
}
void RenderLayer::paint(GraphicsContext& context, const LayoutRect& damageRect, const LayoutSize& subpixelOffset, PaintBehavior paintBehavior, RenderObject* subtreePaintRoot, PaintLayerFlags paintFlags, SecurityOriginPaintPolicy paintPolicy)
{
OverlapTestRequestMap overlapTestRequests;
LayerPaintingInfo paintingInfo(this, enclosingIntRect(damageRect), paintBehavior, subpixelOffset, subtreePaintRoot, &overlapTestRequests, paintPolicy == SecurityOriginPaintPolicy::AccessibleOriginOnly);
paintLayer(context, paintingInfo, paintFlags);
for (auto& widget : overlapTestRequests.keys())
widget->setOverlapTestResult(false);
}
void RenderLayer::paintOverlayScrollbars(GraphicsContext& context, const LayoutRect& damageRect, PaintBehavior paintBehavior, RenderObject* subtreePaintRoot)
{
if (!m_containsDirtyOverlayScrollbars)
return;
LayerPaintingInfo paintingInfo(this, enclosingIntRect(damageRect), paintBehavior, LayoutSize(), subtreePaintRoot);
paintLayer(context, paintingInfo, PaintLayerPaintingOverlayScrollbars);
m_containsDirtyOverlayScrollbars = false;
}
static bool inContainingBlockChain(RenderLayer* startLayer, RenderLayer* endLayer)
{
if (startLayer == endLayer)
return true;
for (const auto* currentBlock = startLayer->renderer().containingBlock(); currentBlock && !is<RenderView>(*currentBlock); currentBlock = currentBlock->containingBlock()) {
if (currentBlock->layer() == endLayer)
return true;
}
return false;
}
void RenderLayer::clipToRect(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, const ClipRect& clipRect, BorderRadiusClippingRule rule)
{
float deviceScaleFactor = renderer().document().deviceScaleFactor();
bool needsClipping = !clipRect.isInfinite() && clipRect.rect() != paintingInfo.paintDirtyRect;
if (needsClipping || clipRect.affectedByRadius())
context.save();
if (needsClipping) {
LayoutRect adjustedClipRect = clipRect.rect();
adjustedClipRect.move(paintingInfo.subpixelOffset);
context.clip(snapRectToDevicePixels(adjustedClipRect, deviceScaleFactor));
}
if (clipRect.affectedByRadius()) {
for (RenderLayer* layer = rule == IncludeSelfForBorderRadius ? this : parent(); layer; layer = layer->parent()) {
if (layer->renderer().hasOverflowClip() && layer->renderer().style().hasBorderRadius() && inContainingBlockChain(this, layer)) {
LayoutRect adjustedClipRect = LayoutRect(toLayoutPoint(layer->offsetFromAncestor(paintingInfo.rootLayer, AdjustForColumns)), layer->size());
adjustedClipRect.move(paintingInfo.subpixelOffset);
FloatRoundedRect roundedRect = layer->renderer().style().getRoundedInnerBorderFor(adjustedClipRect).pixelSnappedRoundedRectForPainting(deviceScaleFactor);
if (roundedRect.intersectionIsRectangular(paintingInfo.paintDirtyRect))
context.clip(snapRectToDevicePixels(intersection(paintingInfo.paintDirtyRect, adjustedClipRect), deviceScaleFactor));
else
context.clipRoundedRect(roundedRect);
}
if (layer == paintingInfo.rootLayer)
break;
}
}
}
void RenderLayer::restoreClip(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, const ClipRect& clipRect)
{
if ((!clipRect.isInfinite() && clipRect.rect() != paintingInfo.paintDirtyRect) || clipRect.affectedByRadius())
context.restore();
}
static void performOverlapTests(OverlapTestRequestMap& overlapTestRequests, const RenderLayer* rootLayer, const RenderLayer* layer)
{
Vector<OverlapTestRequestClient*> overlappedRequestClients;
LayoutRect boundingBox = layer->boundingBox(rootLayer, layer->offsetFromAncestor(rootLayer));
for (auto& request : overlapTestRequests) {
if (!boundingBox.intersects(request.value))
continue;
request.key->setOverlapTestResult(true);
overlappedRequestClients.append(request.key);
}
for (auto* client : overlappedRequestClients)
overlapTestRequests.remove(client);
}
static inline bool shouldDoSoftwarePaint(const RenderLayer* layer, bool paintingReflection)
{
return paintingReflection && !layer->has3DTransform();
}
static inline bool shouldSuppressPaintingLayer(RenderLayer* layer)
{
if (layer->renderer().style().isNotFinal() && !layer->isRootLayer() && !layer->renderer().isDocumentElementRenderer())
return true;
if (!layer->renderer().document().visualUpdatesAllowed())
return true;
return false;
}
static inline bool paintForFixedRootBackground(const RenderLayer* layer, RenderLayer::PaintLayerFlags paintFlags)
{
return layer->renderer().isDocumentElementRenderer() && (paintFlags & RenderLayer::PaintLayerPaintingRootBackgroundOnly);
}
void RenderLayer::paintLayer(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags)
{
if (isComposited()) {
if (context.updatingControlTints() || (paintingInfo.paintBehavior & PaintBehaviorFlattenCompositingLayers))
paintFlags |= PaintLayerTemporaryClipRects;
else if (!backing()->paintsIntoWindow()
&& !backing()->paintsIntoCompositedAncestor()
&& !shouldDoSoftwarePaint(this, paintFlags & PaintLayerPaintingReflection)
&& !paintForFixedRootBackground(this, paintFlags)) {
return;
}
} else if (viewportConstrainedNotCompositedReason() == NotCompositedForBoundsOutOfView) {
ASSERT(renderer().style().position() == FixedPosition);
return;
}
if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant())
return;
if (shouldSuppressPaintingLayer(this))
return;
if (!renderer().opacity())
return;
RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment();
if (namedFlowFragment) {
ASSERT(namedFlowFragment->isValid());
if (!namedFlowFragment->flowThread()->objectShouldFragmentInFlowRegion(&renderer(), namedFlowFragment))
return;
}
if (paintsWithTransparency(paintingInfo.paintBehavior))
paintFlags |= PaintLayerHaveTransparency;
if (paintsWithTransform(paintingInfo.paintBehavior) && !(paintFlags & PaintLayerAppliedTransform)) {
TransformationMatrix layerTransform = renderableTransform(paintingInfo.paintBehavior);
if (!layerTransform.isInvertible())
return;
if (paintFlags & PaintLayerHaveTransparency) {
if (parent())
parent()->beginTransparencyLayers(context, paintingInfo, paintingInfo.paintDirtyRect);
else
beginTransparencyLayers(context, paintingInfo, paintingInfo.paintDirtyRect);
}
if (enclosingPaginationLayer(ExcludeCompositedPaginatedLayers)) {
paintTransformedLayerIntoFragments(context, paintingInfo, paintFlags);
return;
}
ClipRect clipRect = paintingInfo.paintDirtyRect;
if (parent()) {
ClipRectsContext clipRectsContext(paintingInfo.rootLayer, (paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects,
IgnoreOverlayScrollbarSize, (paintFlags & PaintLayerPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip);
clipRect = backgroundClipRect(clipRectsContext);
clipRect.intersect(paintingInfo.paintDirtyRect);
parent()->clipToRect(context, paintingInfo, clipRect);
}
paintLayerByApplyingTransform(context, paintingInfo, paintFlags);
if (parent())
parent()->restoreClip(context, paintingInfo, clipRect);
return;
}
paintLayerContentsAndReflection(context, paintingInfo, paintFlags);
}
void RenderLayer::paintLayerContentsAndReflection(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags)
{
ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant());
PaintLayerFlags localPaintFlags = paintFlags & ~(PaintLayerAppliedTransform);
if (m_reflection && !m_paintingInsideReflection) {
m_paintingInsideReflection = true;
reflectionLayer()->paintLayer(context, paintingInfo, localPaintFlags | PaintLayerPaintingReflection);
m_paintingInsideReflection = false;
}
localPaintFlags |= PaintLayerPaintingCompositingAllPhases;
paintLayerContents(context, paintingInfo, localPaintFlags);
}
bool RenderLayer::setupFontSubpixelQuantization(GraphicsContext& context, bool& didQuantizeFonts)
{
if (context.paintingDisabled())
return false;
bool scrollingOnMainThread = true;
#if ENABLE(ASYNC_SCROLLING)
if (ScrollingCoordinator* scrollingCoordinator = page().scrollingCoordinator())
scrollingOnMainThread = scrollingCoordinator->shouldUpdateScrollLayerPositionSynchronously(renderer().view().frameView());
#endif
bool contentsScrollByPainting = (renderer().hasOverflowClip() && !usesCompositedScrolling()) || (renderer().frame().ownerElement());
bool isZooming = !page().chrome().client().hasStablePageScaleFactor();
if (scrollingOnMainThread || contentsScrollByPainting || isZooming) {
didQuantizeFonts = context.shouldSubpixelQuantizeFonts();
context.setShouldSubpixelQuantizeFonts(false);
return true;
}
return false;
}
template <class ReferenceBoxClipPathOperation>
static inline LayoutRect computeReferenceBox(const RenderObject& renderer, const ReferenceBoxClipPathOperation& clippingPath, const LayoutSize& offsetFromRoot, const LayoutRect& rootRelativeBounds)
{
if (!renderer.isBox())
return rootRelativeBounds;
LayoutRect referenceBox;
const auto& box = downcast<RenderBox>(renderer);
switch (clippingPath.referenceBox()) {
case ContentBox:
referenceBox = box.contentBoxRect();
referenceBox.move(offsetFromRoot);
break;
case PaddingBox:
referenceBox = box.paddingBoxRect();
referenceBox.move(offsetFromRoot);
break;
case MarginBox:
case Fill:
case Stroke:
case ViewBox:
case BorderBox:
case BoxMissing:
referenceBox = box.borderBoxRect();
referenceBox.move(offsetFromRoot);
break;
}
return referenceBox;
}
Path RenderLayer::computeClipPath(const LayoutSize& offsetFromRoot, LayoutRect& rootRelativeBounds, WindRule& windRule) const
{
const RenderStyle& style = renderer().style();
float deviceSaleFactor = renderer().document().deviceScaleFactor();
if (is<ShapeClipPathOperation>(*style.clipPath())) {
auto& clipPath = downcast<ShapeClipPathOperation>(*style.clipPath());
FloatRect referenceBox = snapRectToDevicePixels(computeReferenceBox(renderer(), clipPath, offsetFromRoot, rootRelativeBounds), deviceSaleFactor);
windRule = clipPath.windRule();
return clipPath.pathForReferenceRect(referenceBox);
}
if (is<BoxClipPathOperation>(*style.clipPath()) && is<RenderBox>(renderer())) {
auto& clipPath = downcast<BoxClipPathOperation>(*style.clipPath());
FloatRoundedRect shapeRect = computeRoundedRectForBoxShape(clipPath.referenceBox(), downcast<RenderBox>(renderer())).pixelSnappedRoundedRectForPainting(deviceSaleFactor);
shapeRect.move(offsetFromRoot);
windRule = RULE_NONZERO;
return clipPath.pathForReferenceRect(shapeRect);
}
return Path();
}
bool RenderLayer::setupClipPath(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, const LayoutSize& offsetFromRoot, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed)
{
if (!renderer().hasClipPath() || context.paintingDisabled())
return false;
if (!rootRelativeBoundsComputed) {
rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, offsetFromRoot, 0);
rootRelativeBoundsComputed = true;
}
auto& style = renderer().style();
ASSERT(style.clipPath());
if (is<ShapeClipPathOperation>(*style.clipPath()) || (is<BoxClipPathOperation>(*style.clipPath()) && is<RenderBox>(renderer()))) {
WindRule windRule;
LayoutSize paintingOffsetFromRoot = LayoutSize(snapSizeToDevicePixel(offsetFromRoot + paintingInfo.subpixelOffset, LayoutPoint(), renderer().document().deviceScaleFactor()));
Path path = computeClipPath(paintingOffsetFromRoot, rootRelativeBounds, windRule);
context.save();
context.clipPath(path, windRule);
return true;
}
if (style.clipPath()->type() == ClipPathOperation::Reference) {
ReferenceClipPathOperation* referenceClipPathOperation = static_cast<ReferenceClipPathOperation*>(style.clipPath());
Element* element = renderer().document().getElementById(referenceClipPathOperation->fragment());
if (element && element->hasTagName(SVGNames::clipPathTag) && element->renderer()) {
context.save();
downcast<RenderSVGResourceClipper>(*element->renderer()).applyClippingToContext(renderer(), rootRelativeBounds, paintingInfo.paintDirtyRect, context);
return true;
}
}
return false;
}
std::pair<RenderLayer::FilterInfo*, std::unique_ptr<FilterEffectRendererHelper>> RenderLayer::filterPainter(GraphicsContext& context, PaintLayerFlags paintFlags) const
{
if (context.paintingDisabled())
return { };
if (paintFlags & PaintLayerPaintingOverlayScrollbars)
return { };
if (!paintsWithFilters())
return { };
auto* info = FilterInfo::getIfExists(*this);
if (!info || !info->renderer())
return { };
auto helper = std::make_unique<FilterEffectRendererHelper>(true, context);
if (!helper->haveFilterEffect())
return { };
return { info, WTFMove(helper) };
}
bool RenderLayer::hasFilterThatIsPainting(GraphicsContext& context, PaintLayerFlags paintFlags) const
{
return !!filterPainter(context, paintFlags).first;
}
std::unique_ptr<FilterEffectRendererHelper> RenderLayer::setupFilters(GraphicsContext& context, LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, const LayoutSize& offsetFromRoot, LayoutRect& rootRelativeBounds, bool& rootRelativeBoundsComputed)
{
auto painter = filterPainter(context, paintFlags);
if (!painter.first)
return nullptr;
auto& filterInfo = *painter.first;
auto& filterPainter = *painter.second;
LayoutRect filterRepaintRect = filterInfo.dirtySourceRect();
filterRepaintRect.move(offsetFromRoot);
if (!rootRelativeBoundsComputed) {
rootRelativeBounds = calculateLayerBounds(paintingInfo.rootLayer, offsetFromRoot, 0);
rootRelativeBoundsComputed = true;
}
if (!filterPainter.prepareFilterEffect(*this, enclosingIntRect(rootRelativeBounds), enclosingIntRect(paintingInfo.paintDirtyRect), enclosingIntRect(filterRepaintRect)))
return nullptr;
filterInfo.resetDirtySourceRect();
if (!filterPainter.beginFilterEffect())
return nullptr;
paintingInfo.paintDirtyRect = filterPainter.repaintRect();
paintingInfo.clipToDirtyRect = !filterInfo.renderer()->hasFilterThatMovesPixels();
paintingInfo.requireSecurityOriginAccessForWidgets = filterInfo.renderer()->hasFilterThatShouldBeRestrictedBySecurityOrigin();
return WTFMove(painter.second);
}
void RenderLayer::applyFilters(FilterEffectRendererHelper* filterPainter, GraphicsContext& originalContext, const LayerPaintingInfo& paintingInfo, const LayerFragments& layerFragments)
{
ASSERT(filterPainter->hasStartedFilterEffect());
ClipRect backgroundRect = layerFragments.isEmpty() ? ClipRect() : layerFragments[0].backgroundRect;
clipToRect(originalContext, paintingInfo, backgroundRect);
filterPainter->applyFilterEffect(originalContext);
restoreClip(originalContext, paintingInfo, backgroundRect);
}
static inline bool compareZIndex(RenderLayer* first, RenderLayer* second)
{
return first->zIndex() < second->zIndex();
}
void RenderLayer::paintFixedLayersInNamedFlows(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags)
{
if (!isRootLayer())
return;
if (!renderer().view().hasRenderNamedFlowThreads())
return;
renderer().view().flowThreadController().updateNamedFlowsLayerListsIfNeeded();
Vector<RenderLayer*> fixedLayers;
renderer().view().flowThreadController().collectFixedPositionedLayers(fixedLayers);
for (auto* fixedLayer : fixedLayers)
fixedLayer->paintLayer(context, paintingInfo, paintFlags);
}
void RenderLayer::paintLayerContents(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags)
{
ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant());
PaintLayerFlags localPaintFlags = paintFlags & ~(PaintLayerAppliedTransform);
bool haveTransparency = localPaintFlags & PaintLayerHaveTransparency;
bool isSelfPaintingLayer = this->isSelfPaintingLayer();
bool isPaintingOverlayScrollbars = paintFlags & PaintLayerPaintingOverlayScrollbars;
bool isPaintingScrollingContent = paintFlags & PaintLayerPaintingCompositingScrollingPhase;
bool isPaintingCompositedForeground = paintFlags & PaintLayerPaintingCompositingForegroundPhase;
bool isPaintingCompositedBackground = paintFlags & PaintLayerPaintingCompositingBackgroundPhase;
bool isPaintingOverflowContents = paintFlags & PaintLayerPaintingOverflowContents;
bool shouldPaintOutline = isSelfPaintingLayer && !isPaintingOverlayScrollbars
&& ((isPaintingScrollingContent && isPaintingCompositedBackground)
|| (!isPaintingScrollingContent && isPaintingCompositedForeground));
bool shouldPaintContent = m_hasVisibleContent && isSelfPaintingLayer && !isPaintingOverlayScrollbars;
if (localPaintFlags & PaintLayerPaintingRootBackgroundOnly && !renderer().isRenderView() && !renderer().isDocumentElementRenderer())
return;
updateLayerListsIfNeeded();
if (paintingInfo.rootLayer->isOutOfFlowRenderFlowThread()
&& renderer().fixedPositionedWithNamedFlowContainingBlock())
return;
LayoutSize offsetFromRoot = offsetFromAncestor(paintingInfo.rootLayer);
LayoutRect rootRelativeBounds;
bool rootRelativeBoundsComputed = false;
bool didQuantizeFonts = true;
bool needToAdjustSubpixelQuantization = setupFontSubpixelQuantization(context, didQuantizeFonts);
LayoutSize columnAwareOffsetFromRoot = offsetFromRoot;
if (renderer().flowThreadContainingBlock() && (renderer().hasClipPath() || hasFilterThatIsPainting(context, paintFlags)))
columnAwareOffsetFromRoot = toLayoutSize(convertToLayerCoords(paintingInfo.rootLayer, LayoutPoint(), AdjustForColumns));
bool hasClipPath = false;
if (shouldApplyClipPath(paintingInfo.paintBehavior, localPaintFlags))
hasClipPath = setupClipPath(context, paintingInfo, columnAwareOffsetFromRoot, rootRelativeBounds, rootRelativeBoundsComputed);
bool selectionAndBackgroundsOnly = paintingInfo.paintBehavior & PaintBehaviorSelectionAndBackgroundsOnly;
bool selectionOnly = paintingInfo.paintBehavior & PaintBehaviorSelectionOnly;
LayerFragments layerFragments;
RenderObject* subtreePaintRootForRenderer = nullptr;
{ LayerPaintingInfo localPaintingInfo(paintingInfo);
std::unique_ptr<FilterEffectRendererHelper> filterPainter = setupFilters(context, localPaintingInfo, paintFlags, columnAwareOffsetFromRoot, rootRelativeBounds, rootRelativeBoundsComputed);
GraphicsContext* filterContext = filterPainter ? filterPainter->filterContext() : nullptr;
if (filterContext && haveTransparency) {
beginTransparencyLayers(context, localPaintingInfo, paintingInfo.paintDirtyRect);
}
GraphicsContext& currentContext = filterContext ? *filterContext : context;
if (localPaintingInfo.subtreePaintRoot && !renderer().isDescendantOf(localPaintingInfo.subtreePaintRoot))
subtreePaintRootForRenderer = localPaintingInfo.subtreePaintRoot;
if (localPaintingInfo.overlapTestRequests && isSelfPaintingLayer)
performOverlapTests(*localPaintingInfo.overlapTestRequests, localPaintingInfo.rootLayer, this);
PaintBehavior paintBehavior = PaintBehaviorNormal;
if (localPaintFlags & PaintLayerPaintingSkipRootBackground)
paintBehavior |= PaintBehaviorSkipRootBackground;
else if (localPaintFlags & PaintLayerPaintingRootBackgroundOnly)
paintBehavior |= PaintBehaviorRootBackgroundOnly;
if (paintingInfo.paintBehavior & PaintBehaviorFlattenCompositingLayers)
paintBehavior |= PaintBehaviorFlattenCompositingLayers;
if (paintingInfo.paintBehavior & PaintBehaviorSnapshotting)
paintBehavior |= PaintBehaviorSnapshotting;
if (paintingInfo.paintBehavior & PaintBehaviorTileFirstPaint)
paintBehavior |= PaintBehaviorTileFirstPaint;
if (paintingInfo.paintBehavior & PaintBehaviorExcludeSelection)
paintBehavior |= PaintBehaviorExcludeSelection;
LayoutRect paintDirtyRect = localPaintingInfo.paintDirtyRect;
if (shouldPaintContent || shouldPaintOutline || isPaintingOverlayScrollbars) {
if (!localPaintingInfo.clipToDirtyRect && renderer().hasOverflowClip()) {
localPaintingInfo.clipToDirtyRect = true;
paintDirtyRect = clipRectRelativeToAncestor(localPaintingInfo.rootLayer, offsetFromRoot, LayoutRect::infiniteRect());
}
collectFragments(layerFragments, localPaintingInfo.rootLayer, paintDirtyRect, ExcludeCompositedPaginatedLayers,
(localPaintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize,
(isPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip, offsetFromRoot);
updatePaintingInfoForFragments(layerFragments, localPaintingInfo, localPaintFlags, shouldPaintContent, offsetFromRoot);
}
if (isPaintingCompositedBackground) {
if (shouldPaintContent && !selectionOnly) {
paintBackgroundForFragments(layerFragments, currentContext, context, paintingInfo.paintDirtyRect, haveTransparency,
localPaintingInfo, paintBehavior, subtreePaintRootForRenderer);
}
}
if ((isPaintingScrollingContent && isPaintingOverflowContents) || (!isPaintingScrollingContent && isPaintingCompositedBackground))
paintList(negZOrderList(), currentContext, localPaintingInfo, localPaintFlags);
if (isPaintingCompositedForeground) {
if (shouldPaintContent) {
paintForegroundForFragments(layerFragments, currentContext, context, paintingInfo.paintDirtyRect, haveTransparency,
localPaintingInfo, paintBehavior, subtreePaintRootForRenderer);
}
}
if (shouldPaintOutline)
paintOutlineForFragments(layerFragments, currentContext, localPaintingInfo, paintBehavior, subtreePaintRootForRenderer);
if (isPaintingCompositedForeground) {
paintList(m_normalFlowList.get(), currentContext, localPaintingInfo, localPaintFlags);
paintList(posZOrderList(), currentContext, localPaintingInfo, localPaintFlags);
paintFixedLayersInNamedFlows(currentContext, localPaintingInfo, localPaintFlags);
if (shouldPaintContent)
paintFlowThreadIfRegionForFragments(layerFragments, currentContext, localPaintingInfo, localPaintFlags);
}
if (isPaintingOverlayScrollbars && hasScrollbars())
paintOverflowControlsForFragments(layerFragments, currentContext, localPaintingInfo);
if (filterContext) {
LayerFragments layerFragments;
collectFragments(layerFragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, ExcludeCompositedPaginatedLayers,
(localPaintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize,
(isPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip, offsetFromRoot);
updatePaintingInfoForFragments(layerFragments, paintingInfo, localPaintFlags, shouldPaintContent, offsetFromRoot);
applyFilters(filterPainter.get(), context, paintingInfo, layerFragments);
filterPainter = nullptr;
}
}
if (shouldPaintContent && !(selectionOnly || selectionAndBackgroundsOnly)) {
PaintBehavior paintBehavior = PaintBehaviorNormal;
if (paintingInfo.paintBehavior & PaintBehaviorFlattenCompositingLayers)
paintBehavior |= PaintBehaviorFlattenCompositingLayers;
if (paintingInfo.paintBehavior & PaintBehaviorSnapshotting)
paintBehavior |= PaintBehaviorSnapshotting;
if (paintingInfo.paintBehavior & PaintBehaviorTileFirstPaint)
paintBehavior |= PaintBehaviorTileFirstPaint;
if (shouldPaintMask(paintingInfo.paintBehavior, localPaintFlags)) {
paintMaskForFragments(layerFragments, context, paintingInfo, paintBehavior, subtreePaintRootForRenderer);
}
if (!(paintFlags & PaintLayerPaintingCompositingMaskPhase) && (paintFlags & PaintLayerPaintingCompositingClipPathPhase)) {
paintChildClippingMaskForFragments(layerFragments, context, paintingInfo, paintBehavior, subtreePaintRootForRenderer);
}
if ((localPaintFlags & PaintLayerPaintingChildClippingMaskPhase)) {
paintChildClippingMaskForFragments(layerFragments, context, paintingInfo, paintBehavior, subtreePaintRootForRenderer);
}
}
if (haveTransparency && m_usedTransparency && !m_paintingInsideReflection) {
context.endTransparencyLayer();
context.restore();
m_usedTransparency = false;
}
if (needToAdjustSubpixelQuantization)
context.setShouldSubpixelQuantizeFonts(didQuantizeFonts);
if (hasClipPath)
context.restore();
}
void RenderLayer::paintLayerByApplyingTransform(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags, const LayoutSize& translationOffset)
{
float deviceScaleFactor = renderer().document().deviceScaleFactor();
LayoutSize offsetFromParent = offsetFromAncestor(paintingInfo.rootLayer);
offsetFromParent += translationOffset;
TransformationMatrix transform(renderableTransform(paintingInfo.paintBehavior));
LayoutSize offsetForThisLayer = offsetFromParent + paintingInfo.subpixelOffset;
FloatSize devicePixelSnappedOffsetForThisLayer = toFloatSize(roundPointToDevicePixels(toLayoutPoint(offsetForThisLayer), deviceScaleFactor));
transform.translateRight(devicePixelSnappedOffsetForThisLayer.width(), devicePixelSnappedOffsetForThisLayer.height());
AffineTransform oldTransfrom = context.getCTM();
context.concatCTM(transform.toAffineTransform());
LayoutSize adjustedSubpixelOffset = offsetForThisLayer - LayoutSize(devicePixelSnappedOffsetForThisLayer);
LayerPaintingInfo transformedPaintingInfo(paintingInfo);
transformedPaintingInfo.rootLayer = this;
transformedPaintingInfo.paintDirtyRect = LayoutRect(encloseRectToDevicePixels(transform.inverse().value_or(AffineTransform()).mapRect(paintingInfo.paintDirtyRect), deviceScaleFactor));
transformedPaintingInfo.subpixelOffset = adjustedSubpixelOffset;
paintLayerContentsAndReflection(context, transformedPaintingInfo, paintFlags);
context.setCTM(oldTransfrom);
}
void RenderLayer::paintList(Vector<RenderLayer*>* list, GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags)
{
if (!list)
return;
if (!hasSelfPaintingLayerDescendant())
return;
#if !ASSERT_DISABLED
LayerListMutationDetector mutationChecker(this);
#endif
for (auto* childLayer : *list) {
if (childLayer->isFlowThreadCollectingGraphicsLayersUnderRegions())
continue;
childLayer->paintLayer(context, paintingInfo, paintFlags);
}
}
RenderLayer* RenderLayer::enclosingPaginationLayerInSubtree(const RenderLayer* rootLayer, PaginationInclusionMode mode) const
{
RenderLayer* paginationLayer = enclosingPaginationLayer(mode);
if (!paginationLayer || rootLayer == paginationLayer)
return paginationLayer;
for (const RenderLayer* layer = this; layer; layer = layer->parent()) {
if (layer == rootLayer)
return nullptr;
if (layer == paginationLayer)
return paginationLayer;
}
ASSERT_NOT_REACHED();
return nullptr;
}
void RenderLayer::collectFragments(LayerFragments& fragments, const RenderLayer* rootLayer, const LayoutRect& dirtyRect, PaginationInclusionMode inclusionMode,
ClipRectsType clipRectsType, OverlayScrollbarSizeRelevancy inOverlayScrollbarSizeRelevancy, ShouldRespectOverflowClip respectOverflowClip, const LayoutSize& offsetFromRoot,
const LayoutRect* layerBoundingBox, ShouldApplyRootOffsetToFragments applyRootOffsetToFragments)
{
RenderLayer* paginationLayer = enclosingPaginationLayerInSubtree(rootLayer, inclusionMode);
if (!paginationLayer || hasTransform()) {
LayerFragment fragment;
ClipRectsContext clipRectsContext(rootLayer, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip);
calculateRects(clipRectsContext, dirtyRect, fragment.layerBounds, fragment.backgroundRect, fragment.foregroundRect, offsetFromRoot);
fragments.append(fragment);
return;
}
LayoutSize offsetWithinPaginatedLayer = offsetFromAncestor(paginationLayer);
ClipRectsContext paginationClipRectsContext(paginationLayer, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip);
LayoutRect layerBoundsInFlowThread;
ClipRect backgroundRectInFlowThread;
ClipRect foregroundRectInFlowThread;
calculateRects(paginationClipRectsContext, LayoutRect::infiniteRect(), layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread,
offsetWithinPaginatedLayer);
LayoutRect layerBoundingBoxInFlowThread = layerBoundingBox ? *layerBoundingBox : boundingBox(paginationLayer, offsetWithinPaginatedLayer);
layerBoundingBoxInFlowThread.intersect(backgroundRectInFlowThread.rect());
auto& enclosingFlowThread = downcast<RenderFlowThread>(paginationLayer->renderer());
RenderLayer* parentPaginationLayer = paginationLayer->parent()->enclosingPaginationLayerInSubtree(rootLayer, inclusionMode);
LayerFragments ancestorFragments;
if (parentPaginationLayer) {
LayoutRect layerFragmentBoundingBoxInParentPaginationLayer = enclosingFlowThread.fragmentsBoundingBox(layerBoundingBoxInFlowThread);
LayoutSize offsetWithinParentPaginatedLayer = paginationLayer->offsetFromAncestor(parentPaginationLayer);
layerFragmentBoundingBoxInParentPaginationLayer.move(offsetWithinParentPaginatedLayer);
parentPaginationLayer->collectFragments(ancestorFragments, rootLayer, dirtyRect, inclusionMode, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip,
offsetFromAncestor(rootLayer), &layerFragmentBoundingBoxInParentPaginationLayer, ApplyRootOffsetToFragments);
if (ancestorFragments.isEmpty())
return;
for (auto& ancestorFragment : ancestorFragments) {
LayoutRect dirtyRectInFlowThread(dirtyRect);
dirtyRectInFlowThread.move(-offsetWithinParentPaginatedLayer - ancestorFragment.paginationOffset);
size_t oldSize = fragments.size();
enclosingFlowThread.collectLayerFragments(fragments, layerBoundingBoxInFlowThread, dirtyRectInFlowThread);
size_t newSize = fragments.size();
if (oldSize == newSize)
continue;
for (size_t i = oldSize; i < newSize; ++i) {
LayerFragment& fragment = fragments.at(i);
fragment.setRects(layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread, &layerBoundingBoxInFlowThread);
fragment.moveBy(toLayoutPoint(ancestorFragment.paginationOffset + fragment.paginationOffset + offsetWithinParentPaginatedLayer));
fragment.intersect(ancestorFragment.paginationClip);
fragment.intersect(fragment.paginationClip);
if (applyRootOffsetToFragments == ApplyRootOffsetToFragments)
fragment.paginationOffset = fragment.paginationOffset + offsetWithinParentPaginatedLayer;
}
}
return;
}
LayoutSize offsetOfPaginationLayerFromRoot = enclosingPaginationLayer(inclusionMode)->offsetFromAncestor(rootLayer);
LayoutRect dirtyRectInFlowThread(dirtyRect);
dirtyRectInFlowThread.move(-offsetOfPaginationLayerFromRoot);
enclosingFlowThread.collectLayerFragments(fragments, layerBoundingBoxInFlowThread, dirtyRectInFlowThread);
if (fragments.isEmpty())
return;
ClipRect ancestorClipRect = dirtyRect;
if (paginationLayer->parent()) {
ClipRectsContext clipRectsContext(rootLayer, clipRectsType, inOverlayScrollbarSizeRelevancy, respectOverflowClip);
ancestorClipRect = paginationLayer->backgroundClipRect(clipRectsContext);
ancestorClipRect.intersect(dirtyRect);
}
for (auto& fragment : fragments) {
fragment.setRects(layerBoundsInFlowThread, backgroundRectInFlowThread, foregroundRectInFlowThread, &layerBoundingBoxInFlowThread);
fragment.moveBy(toLayoutPoint(fragment.paginationOffset + offsetOfPaginationLayerFromRoot));
fragment.intersect(ancestorClipRect);
fragment.intersect(fragment.paginationClip);
if (applyRootOffsetToFragments == ApplyRootOffsetToFragments)
fragment.paginationOffset = fragment.paginationOffset + offsetOfPaginationLayerFromRoot;
}
}
void RenderLayer::updatePaintingInfoForFragments(LayerFragments& fragments, const LayerPaintingInfo& localPaintingInfo, PaintLayerFlags localPaintFlags,
bool shouldPaintContent, const LayoutSize& offsetFromRoot)
{
for (auto& fragment : fragments) {
fragment.shouldPaintContent = shouldPaintContent;
if (this != localPaintingInfo.rootLayer || !(localPaintFlags & PaintLayerPaintingOverflowContents)) {
LayoutSize newOffsetFromRoot = offsetFromRoot + fragment.paginationOffset;
fragment.shouldPaintContent &= intersectsDamageRect(fragment.layerBounds, fragment.backgroundRect.rect(), localPaintingInfo.rootLayer, newOffsetFromRoot, fragment.hasBoundingBox ? &fragment.boundingBox : 0);
}
}
}
void RenderLayer::paintTransformedLayerIntoFragments(GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags)
{
LayerFragments enclosingPaginationFragments;
LayoutSize offsetOfPaginationLayerFromRoot;
RenderLayer* paginatedLayer = enclosingPaginationLayer(ExcludeCompositedPaginatedLayers);
LayoutRect transformedExtent = transparencyClipBox(*this, paginatedLayer, PaintingTransparencyClipBox, RootOfTransparencyClipBox, paintingInfo.paintBehavior);
paginatedLayer->collectFragments(enclosingPaginationFragments, paintingInfo.rootLayer, paintingInfo.paintDirtyRect, ExcludeCompositedPaginatedLayers,
(paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects, IgnoreOverlayScrollbarSize,
(paintFlags & PaintLayerPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip, offsetOfPaginationLayerFromRoot, &transformedExtent);
for (const auto& fragment : enclosingPaginationFragments) {
LayoutRect clipRect = fragment.backgroundRect.rect();
if (parent() != paginatedLayer) {
offsetOfPaginationLayerFromRoot = toLayoutSize(paginatedLayer->convertToLayerCoords(paintingInfo.rootLayer, toLayoutPoint(offsetOfPaginationLayerFromRoot)));
ClipRectsContext clipRectsContext(paginatedLayer, (paintFlags & PaintLayerTemporaryClipRects) ? TemporaryClipRects : PaintingClipRects,
IgnoreOverlayScrollbarSize, (paintFlags & PaintLayerPaintingOverflowContents) ? IgnoreOverflowClip : RespectOverflowClip);
LayoutRect parentClipRect = backgroundClipRect(clipRectsContext).rect();
parentClipRect.move(fragment.paginationOffset + offsetOfPaginationLayerFromRoot);
clipRect.intersect(parentClipRect);
}
parent()->clipToRect(context, paintingInfo, clipRect);
paintLayerByApplyingTransform(context, paintingInfo, paintFlags, fragment.paginationOffset);
parent()->restoreClip(context, paintingInfo, clipRect);
}
}
void RenderLayer::paintBackgroundForFragments(const LayerFragments& layerFragments, GraphicsContext& context, GraphicsContext& contextForTransparencyLayer,
const LayoutRect& transparencyPaintDirtyRect, bool haveTransparency, const LayerPaintingInfo& localPaintingInfo, PaintBehavior paintBehavior,
RenderObject* subtreePaintRootForRenderer)
{
for (const auto& fragment : layerFragments) {
if (!fragment.shouldPaintContent)
continue;
if (haveTransparency)
beginTransparencyLayers(contextForTransparencyLayer, localPaintingInfo, transparencyPaintDirtyRect);
if (localPaintingInfo.clipToDirtyRect) {
clipToRect(context, localPaintingInfo, fragment.backgroundRect, DoNotIncludeSelfForBorderRadius); }
PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhaseBlockBackground, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer());
renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset));
if (localPaintingInfo.clipToDirtyRect)
restoreClip(context, localPaintingInfo, fragment.backgroundRect);
}
}
void RenderLayer::paintForegroundForFragments(const LayerFragments& layerFragments, GraphicsContext& context, GraphicsContext& contextForTransparencyLayer,
const LayoutRect& transparencyPaintDirtyRect, bool haveTransparency, const LayerPaintingInfo& localPaintingInfo, PaintBehavior paintBehavior,
RenderObject* subtreePaintRootForRenderer)
{
if (haveTransparency) {
for (const auto& fragment : layerFragments) {
if (fragment.shouldPaintContent && !fragment.foregroundRect.isEmpty()) {
beginTransparencyLayers(contextForTransparencyLayer, localPaintingInfo, transparencyPaintDirtyRect);
break;
}
}
}
PaintBehavior localPaintBehavior;
if (localPaintingInfo.paintBehavior & PaintBehaviorForceBlackText)
localPaintBehavior = PaintBehaviorForceBlackText;
else if (localPaintingInfo.paintBehavior & PaintBehaviorForceWhiteText)
localPaintBehavior = PaintBehaviorForceWhiteText;
else
localPaintBehavior = paintBehavior;
if (localPaintingInfo.paintBehavior & PaintBehaviorExcludeSelection)
localPaintBehavior |= PaintBehaviorExcludeSelection;
if (localPaintingInfo.paintBehavior & PaintBehaviorSnapshotting)
localPaintBehavior |= PaintBehaviorSnapshotting;
if (localPaintingInfo.paintBehavior & PaintBehaviorTileFirstPaint)
localPaintBehavior |= PaintBehaviorTileFirstPaint;
bool shouldClip = localPaintingInfo.clipToDirtyRect && layerFragments.size() == 1 && layerFragments[0].shouldPaintContent && !layerFragments[0].foregroundRect.isEmpty();
ClipRect clippedRect;
if (shouldClip) {
clippedRect = layerFragments[0].foregroundRect;
clipToRect(context, localPaintingInfo, clippedRect);
}
bool selectionOnly = localPaintingInfo.paintBehavior & (PaintBehaviorSelectionAndBackgroundsOnly | PaintBehaviorSelectionOnly);
paintForegroundForFragmentsWithPhase(selectionOnly ? PaintPhaseSelection : PaintPhaseChildBlockBackgrounds, layerFragments,
context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer);
if (!selectionOnly) {
paintForegroundForFragmentsWithPhase(PaintPhaseFloat, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer);
paintForegroundForFragmentsWithPhase(PaintPhaseForeground, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer);
paintForegroundForFragmentsWithPhase(PaintPhaseChildOutlines, layerFragments, context, localPaintingInfo, localPaintBehavior, subtreePaintRootForRenderer);
}
if (shouldClip)
restoreClip(context, localPaintingInfo, clippedRect);
}
void RenderLayer::paintForegroundForFragmentsWithPhase(PaintPhase phase, const LayerFragments& layerFragments, GraphicsContext& context,
const LayerPaintingInfo& localPaintingInfo, PaintBehavior paintBehavior, RenderObject* subtreePaintRootForRenderer)
{
bool shouldClip = localPaintingInfo.clipToDirtyRect && layerFragments.size() > 1;
for (const auto& fragment : layerFragments) {
if (!fragment.shouldPaintContent || fragment.foregroundRect.isEmpty())
continue;
if (shouldClip)
clipToRect(context, localPaintingInfo, fragment.foregroundRect);
PaintInfo paintInfo(context, fragment.foregroundRect.rect(), phase, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer(), localPaintingInfo.requireSecurityOriginAccessForWidgets);
if (phase == PaintPhaseForeground)
paintInfo.overlapTestRequests = localPaintingInfo.overlapTestRequests;
renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset));
if (shouldClip)
restoreClip(context, localPaintingInfo, fragment.foregroundRect);
}
}
void RenderLayer::paintOutlineForFragments(const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo,
PaintBehavior paintBehavior, RenderObject* subtreePaintRootForRenderer)
{
for (const auto& fragment : layerFragments) {
if (fragment.backgroundRect.isEmpty())
continue;
PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhaseSelfOutline, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer());
clipToRect(context, localPaintingInfo, fragment.backgroundRect, DoNotIncludeSelfForBorderRadius);
renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset));
restoreClip(context, localPaintingInfo, fragment.backgroundRect);
}
}
void RenderLayer::paintMaskForFragments(const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo,
PaintBehavior paintBehavior, RenderObject* subtreePaintRootForRenderer)
{
for (const auto& fragment : layerFragments) {
if (!fragment.shouldPaintContent)
continue;
if (localPaintingInfo.clipToDirtyRect)
clipToRect(context, localPaintingInfo, fragment.backgroundRect, DoNotIncludeSelfForBorderRadius);
PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhaseMask, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer());
renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset));
if (localPaintingInfo.clipToDirtyRect)
restoreClip(context, localPaintingInfo, fragment.backgroundRect);
}
}
void RenderLayer::paintChildClippingMaskForFragments(const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo, PaintBehavior paintBehavior, RenderObject* subtreePaintRootForRenderer)
{
for (const auto& fragment : layerFragments) {
if (!fragment.shouldPaintContent)
continue;
if (localPaintingInfo.clipToDirtyRect)
clipToRect(context, localPaintingInfo, fragment.foregroundRect, IncludeSelfForBorderRadius);
PaintInfo paintInfo(context, fragment.backgroundRect.rect(), PaintPhaseClippingMask, paintBehavior, subtreePaintRootForRenderer, nullptr, nullptr, &localPaintingInfo.rootLayer->renderer());
renderer().paint(paintInfo, toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset));
if (localPaintingInfo.clipToDirtyRect)
restoreClip(context, localPaintingInfo, fragment.foregroundRect);
}
}
void RenderLayer::paintOverflowControlsForFragments(const LayerFragments& layerFragments, GraphicsContext& context, const LayerPaintingInfo& localPaintingInfo)
{
for (const auto& fragment : layerFragments) {
if (fragment.backgroundRect.isEmpty())
continue;
clipToRect(context, localPaintingInfo, fragment.backgroundRect);
paintOverflowControls(context, roundedIntPoint(toLayoutPoint(fragment.layerBounds.location() - renderBoxLocation() + localPaintingInfo.subpixelOffset)),
snappedIntRect(fragment.backgroundRect.rect()), true);
restoreClip(context, localPaintingInfo, fragment.backgroundRect);
}
}
bool RenderLayer::hitTest(const HitTestRequest& request, HitTestResult& result)
{
return hitTest(request, result.hitTestLocation(), result);
}
bool RenderLayer::hitTest(const HitTestRequest& request, const HitTestLocation& hitTestLocation, HitTestResult& result)
{
ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant());
ASSERT(!renderer().view().needsLayout());
updateLayerListsIfNeeded();
LayoutRect hitTestArea = isOutOfFlowRenderFlowThread() ? downcast<RenderFlowThread>(renderer()).visualOverflowRect() : renderer().view().documentRect();
if (!request.ignoreClipping())
hitTestArea.intersect(renderer().view().frameView().visibleContentRect(LegacyIOSDocumentVisibleRect));
RenderLayer* insideLayer = hitTestLayer(this, nullptr, request, result, hitTestArea, hitTestLocation, false);
if (!insideLayer) {
if (!request.isChildFrameHitTest() && (request.active() || request.release()) && isRootLayer()) {
renderer().updateHitTestResult(result, downcast<RenderView>(renderer()).flipForWritingMode(hitTestLocation.point()));
insideLayer = this;
}
}
Node* node = result.innerNode();
if (node && !result.URLElement())
result.setURLElement(node->enclosingLinkEventParentOrSelf());
return insideLayer;
}
Element* RenderLayer::enclosingElement() const
{
for (RenderElement* r = &renderer(); r; r = r->parent()) {
if (Element* e = r->element())
return e;
}
return nullptr;
}
RenderLayer* RenderLayer::enclosingFlowThreadAncestor() const
{
RenderLayer* curr = parent();
for (; curr && !curr->isRenderFlowThread(); curr = curr->parent()) {
if (curr->isStackingContainer() && curr->isComposited()) {
return nullptr;
}
}
return curr;
}
bool RenderLayer::isFlowThreadCollectingGraphicsLayersUnderRegions() const
{
return is<RenderFlowThread>(renderer()) && downcast<RenderFlowThread>(renderer()).collectsGraphicsLayersUnderRegions();
}
static double computeZOffset(const HitTestingTransformState& transformState)
{
if (transformState.m_accumulatedTransform.isAffine())
return 0;
FloatPoint targetPoint = transformState.mappedPoint();
FloatPoint3D backmappedPoint = transformState.m_accumulatedTransform.mapPoint(FloatPoint3D(targetPoint));
return backmappedPoint.z();
}
Ref<HitTestingTransformState> RenderLayer::createLocalTransformState(RenderLayer* rootLayer, RenderLayer* containerLayer,
const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation,
const HitTestingTransformState* containerTransformState,
const LayoutSize& translationOffset) const
{
RefPtr<HitTestingTransformState> transformState;
LayoutSize offset;
if (containerTransformState) {
transformState = HitTestingTransformState::create(*containerTransformState);
offset = offsetFromAncestor(containerLayer);
} else {
transformState = HitTestingTransformState::create(hitTestLocation.transformedPoint(), hitTestLocation.transformedRect(), FloatQuad(hitTestRect));
offset = offsetFromAncestor(rootLayer);
}
offset += translationOffset;
RenderObject* containerRenderer = containerLayer ? &containerLayer->renderer() : nullptr;
if (renderer().shouldUseTransformFromContainer(containerRenderer)) {
TransformationMatrix containerTransform;
renderer().getTransformFromContainer(containerRenderer, offset, containerTransform);
transformState->applyTransform(containerTransform, HitTestingTransformState::AccumulateTransform);
} else {
transformState->translate(offset.width(), offset.height(), HitTestingTransformState::AccumulateTransform);
}
return transformState.releaseNonNull();
}
static bool isHitCandidate(const RenderLayer* hitLayer, bool canDepthSort, double* zOffset, const HitTestingTransformState* transformState)
{
if (!hitLayer)
return false;
if (hitLayer->renderer().isRenderNamedFlowFragment())
return false;
if (canDepthSort)
return true;
if (zOffset) {
ASSERT(transformState);
double childZOffset = computeZOffset(*transformState);
if (childZOffset > *zOffset) {
*zOffset = childZOffset;
return true;
}
return false;
}
return true;
}
RenderLayer* RenderLayer::hitTestFixedLayersInNamedFlows(RenderLayer* ,
const HitTestRequest& request, HitTestResult& result,
const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation,
const HitTestingTransformState* transformState,
double* zOffsetForDescendants, double* zOffset,
const HitTestingTransformState* unflattenedTransformState,
bool depthSortDescendants)
{
if (!isRootLayer())
return nullptr;
if (!renderer().view().hasRenderNamedFlowThreads())
return nullptr;
Vector<RenderLayer*> fixedLayers;
renderer().view().flowThreadController().collectFixedPositionedLayers(fixedLayers);
RenderLayer* resultLayer = nullptr;
for (int i = fixedLayers.size() - 1; i >= 0; --i) {
RenderLayer* fixedLayer = fixedLayers.at(i);
HitTestResult tempResult(result.hitTestLocation());
RenderLayer* hitLayer = fixedLayer->hitTestLayer(fixedLayer->renderer().flowThreadContainingBlock()->layer(), nullptr, request, tempResult,
hitTestRect, hitTestLocation, false, transformState, zOffsetForDescendants);
if (result.isRectBasedTest())
result.append(tempResult);
if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState)) {
resultLayer = hitLayer;
if (!result.isRectBasedTest())
result = tempResult;
if (!depthSortDescendants)
break;
}
}
return resultLayer;
}
RenderLayer* RenderLayer::hitTestLayer(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result,
const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, bool appliedTransform,
const HitTestingTransformState* transformState, double* zOffset)
{
if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant())
return nullptr;
RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment();
if (renderer().fixedPositionedWithNamedFlowContainingBlock() && namedFlowFragment)
return nullptr;
if (namedFlowFragment) {
ASSERT(namedFlowFragment->isValid());
RenderFlowThread* flowThread = namedFlowFragment->flowThread();
if (!flowThread->objectShouldFragmentInFlowRegion(&renderer(), namedFlowFragment))
return nullptr;
}
if (transform() && !appliedTransform) {
if (enclosingPaginationLayer(IncludeCompositedPaginatedLayers))
return hitTestTransformedLayerInFragments(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation, transformState, zOffset);
if (parent()) {
ClipRectsContext clipRectsContext(rootLayer, RootRelativeClipRects, IncludeOverlayScrollbarSize);
ClipRect clipRect = backgroundClipRect(clipRectsContext);
if (!clipRect.intersects(hitTestLocation))
return nullptr;
}
return hitTestLayerByApplyingTransform(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation, transformState, zOffset);
}
updateCompositingAndLayerListsIfNeeded();
update3DTransformedDescendantStatus();
RefPtr<HitTestingTransformState> localTransformState;
if (appliedTransform) {
ASSERT(transformState);
localTransformState = const_cast<HitTestingTransformState*>(transformState);
} else if (transformState || has3DTransformedDescendant() || preserves3D()) {
localTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestLocation, transformState);
}
if (localTransformState && renderer().style().backfaceVisibility() == BackfaceVisibilityHidden) {
std::optional<TransformationMatrix> invertedMatrix = localTransformState->m_accumulatedTransform.inverse();
if (invertedMatrix && invertedMatrix.value().m33() < 0)
return nullptr;
}
RefPtr<HitTestingTransformState> unflattenedTransformState = localTransformState;
if (localTransformState && !preserves3D()) {
unflattenedTransformState = HitTestingTransformState::create(*localTransformState);
localTransformState->flatten();
}
double localZOffset = -std::numeric_limits<double>::infinity();
double* zOffsetForDescendantsPtr = nullptr;
double* zOffsetForContentsPtr = nullptr;
bool depthSortDescendants = false;
if (preserves3D()) {
depthSortDescendants = true;
zOffsetForDescendantsPtr = zOffset ? zOffset : &localZOffset;
zOffsetForContentsPtr = zOffset ? zOffset : &localZOffset;
} else if (zOffset) {
zOffsetForDescendantsPtr = nullptr;
zOffsetForContentsPtr = zOffset;
}
RenderLayer* candidateLayer = nullptr;
#if !ASSERT_DISABLED
LayerListMutationDetector mutationChecker(this);
#endif
RenderLayer* hitLayer = hitTestFixedLayersInNamedFlows(rootLayer, request, result, hitTestRect, hitTestLocation,
localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants);
if (hitLayer) {
if (!depthSortDescendants)
return hitLayer;
candidateLayer = hitLayer;
}
hitLayer = hitTestList(posZOrderList(), rootLayer, request, result, hitTestRect, hitTestLocation,
localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants);
if (hitLayer) {
if (!depthSortDescendants)
return hitLayer;
candidateLayer = hitLayer;
}
hitLayer = hitTestList(m_normalFlowList.get(), rootLayer, request, result, hitTestRect, hitTestLocation,
localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants);
if (hitLayer) {
if (!depthSortDescendants)
return hitLayer;
candidateLayer = hitLayer;
}
LayerFragments layerFragments;
collectFragments(layerFragments, rootLayer, hitTestRect, IncludeCompositedPaginatedLayers, RootRelativeClipRects, IncludeOverlayScrollbarSize, RespectOverflowClip,
offsetFromAncestor(rootLayer));
if (canResize() && hitTestResizerInFragments(layerFragments, hitTestLocation)) {
renderer().updateHitTestResult(result, hitTestLocation.point());
return this;
}
hitLayer = hitTestFlowThreadIfRegionForFragments(layerFragments, rootLayer, request, result, hitTestRect, hitTestLocation,
localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants);
if (hitLayer) {
if (!depthSortDescendants)
return hitLayer;
candidateLayer = hitLayer;
}
if (isSelfPaintingLayer()) {
HitTestResult tempResult(result.hitTestLocation());
bool insideFragmentForegroundRect = false;
if (hitTestContentsForFragments(layerFragments, request, tempResult, hitTestLocation, HitTestDescendants, insideFragmentForegroundRect)
&& isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) {
if (result.isRectBasedTest())
result.append(tempResult);
else
result = tempResult;
if (!depthSortDescendants)
return this;
candidateLayer = this;
} else if (insideFragmentForegroundRect && result.isRectBasedTest())
result.append(tempResult);
}
hitLayer = hitTestList(negZOrderList(), rootLayer, request, result, hitTestRect, hitTestLocation,
localTransformState.get(), zOffsetForDescendantsPtr, zOffset, unflattenedTransformState.get(), depthSortDescendants);
if (hitLayer) {
if (!depthSortDescendants)
return hitLayer;
candidateLayer = hitLayer;
}
if (candidateLayer)
return candidateLayer;
if (isSelfPaintingLayer()) {
HitTestResult tempResult(result.hitTestLocation());
bool insideFragmentBackgroundRect = false;
if (hitTestContentsForFragments(layerFragments, request, tempResult, hitTestLocation, HitTestSelf, insideFragmentBackgroundRect)
&& isHitCandidate(this, false, zOffsetForContentsPtr, unflattenedTransformState.get())) {
if (result.isRectBasedTest())
result.append(tempResult);
else
result = tempResult;
return this;
}
if (insideFragmentBackgroundRect && result.isRectBasedTest())
result.append(tempResult);
}
return nullptr;
}
bool RenderLayer::hitTestContentsForFragments(const LayerFragments& layerFragments, const HitTestRequest& request, HitTestResult& result,
const HitTestLocation& hitTestLocation, HitTestFilter hitTestFilter, bool& insideClipRect) const
{
if (layerFragments.isEmpty())
return false;
for (int i = layerFragments.size() - 1; i >= 0; --i) {
const LayerFragment& fragment = layerFragments.at(i);
if ((hitTestFilter == HitTestSelf && !fragment.backgroundRect.intersects(hitTestLocation))
|| (hitTestFilter == HitTestDescendants && !fragment.foregroundRect.intersects(hitTestLocation)))
continue;
insideClipRect = true;
if (hitTestContents(request, result, fragment.layerBounds, hitTestLocation, hitTestFilter))
return true;
}
return false;
}
bool RenderLayer::hitTestResizerInFragments(const LayerFragments& layerFragments, const HitTestLocation& hitTestLocation) const
{
if (layerFragments.isEmpty())
return false;
for (int i = layerFragments.size() - 1; i >= 0; --i) {
const LayerFragment& fragment = layerFragments.at(i);
if (fragment.backgroundRect.intersects(hitTestLocation) && resizerCornerRect(*this, snappedIntRect(fragment.layerBounds)).contains(hitTestLocation.roundedPoint()))
return true;
}
return false;
}
RenderLayer* RenderLayer::hitTestTransformedLayerInFragments(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result,
const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset)
{
LayerFragments enclosingPaginationFragments;
LayoutSize offsetOfPaginationLayerFromRoot;
RenderLayer* paginatedLayer = enclosingPaginationLayer(IncludeCompositedPaginatedLayers);
LayoutRect transformedExtent = transparencyClipBox(*this, paginatedLayer, HitTestingTransparencyClipBox, RootOfTransparencyClipBox);
paginatedLayer->collectFragments(enclosingPaginationFragments, rootLayer, hitTestRect, IncludeCompositedPaginatedLayers,
RootRelativeClipRects, IncludeOverlayScrollbarSize, RespectOverflowClip, offsetOfPaginationLayerFromRoot, &transformedExtent);
for (int i = enclosingPaginationFragments.size() - 1; i >= 0; --i) {
const LayerFragment& fragment = enclosingPaginationFragments.at(i);
LayoutRect clipRect = fragment.backgroundRect.rect();
if (parent() != paginatedLayer) {
offsetOfPaginationLayerFromRoot = toLayoutSize(paginatedLayer->convertToLayerCoords(rootLayer, toLayoutPoint(offsetOfPaginationLayerFromRoot)));
ClipRectsContext clipRectsContext(paginatedLayer, RootRelativeClipRects, IncludeOverlayScrollbarSize);
LayoutRect parentClipRect = backgroundClipRect(clipRectsContext).rect();
parentClipRect.move(fragment.paginationOffset + offsetOfPaginationLayerFromRoot);
clipRect.intersect(parentClipRect);
}
if (!hitTestLocation.intersects(clipRect))
continue;
RenderLayer* hitLayer = hitTestLayerByApplyingTransform(rootLayer, containerLayer, request, result, hitTestRect, hitTestLocation,
transformState, zOffset, fragment.paginationOffset);
if (hitLayer)
return hitLayer;
}
return nullptr;
}
RenderLayer* RenderLayer::hitTestLayerByApplyingTransform(RenderLayer* rootLayer, RenderLayer* containerLayer, const HitTestRequest& request, HitTestResult& result,
const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation, const HitTestingTransformState* transformState, double* zOffset,
const LayoutSize& translationOffset)
{
Ref<HitTestingTransformState> newTransformState = createLocalTransformState(rootLayer, containerLayer, hitTestRect, hitTestLocation, transformState, translationOffset);
if (!newTransformState->m_accumulatedTransform.isInvertible())
return nullptr;
FloatPoint localPoint = newTransformState->mappedPoint();
FloatQuad localPointQuad = newTransformState->mappedQuad();
LayoutRect localHitTestRect = newTransformState->boundsOfMappedArea();
HitTestLocation newHitTestLocation;
if (hitTestLocation.isRectBasedTest())
newHitTestLocation = HitTestLocation(localPoint, localPointQuad);
else
newHitTestLocation = HitTestLocation(localPoint);
return hitTestLayer(this, containerLayer, request, result, localHitTestRect, newHitTestLocation, true, newTransformState.ptr(), zOffset);
}
bool RenderLayer::hitTestContents(const HitTestRequest& request, HitTestResult& result, const LayoutRect& layerBounds, const HitTestLocation& hitTestLocation, HitTestFilter hitTestFilter) const
{
ASSERT(isSelfPaintingLayer() || hasSelfPaintingLayerDescendant());
if (!renderer().hitTest(request, result, hitTestLocation, toLayoutPoint(layerBounds.location() - renderBoxLocation()), hitTestFilter)) {
ASSERT(!result.innerNode() || (result.isRectBasedTest() && result.rectBasedTestResult().size()));
return false;
}
if (!result.innerNode() || !result.innerNonSharedNode()) {
if (isOutOfFlowRenderFlowThread()) {
return false;
}
Element* e = enclosingElement();
if (!result.innerNode())
result.setInnerNode(e);
if (!result.innerNonSharedNode())
result.setInnerNonSharedNode(e);
}
return true;
}
RenderLayer* RenderLayer::hitTestList(Vector<RenderLayer*>* list, RenderLayer* rootLayer,
const HitTestRequest& request, HitTestResult& result,
const LayoutRect& hitTestRect, const HitTestLocation& hitTestLocation,
const HitTestingTransformState* transformState,
double* zOffsetForDescendants, double* zOffset,
const HitTestingTransformState* unflattenedTransformState,
bool depthSortDescendants)
{
if (!list)
return nullptr;
if (!hasSelfPaintingLayerDescendant())
return nullptr;
RenderLayer* resultLayer = nullptr;
for (size_t i = list->size(); i > 0; --i) {
RenderLayer* childLayer = list->at(i - 1);
if (childLayer->isFlowThreadCollectingGraphicsLayersUnderRegions())
continue;
RenderLayer* hitLayer = nullptr;
HitTestResult tempResult(result.hitTestLocation());
hitLayer = childLayer->hitTestLayer(rootLayer, this, request, tempResult, hitTestRect, hitTestLocation, false, transformState, zOffsetForDescendants);
if (result.isRectBasedTest())
result.append(tempResult);
if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState)) {
resultLayer = hitLayer;
if (!result.isRectBasedTest())
result = tempResult;
if (!depthSortDescendants)
break;
}
}
return resultLayer;
}
Ref<ClipRects> RenderLayer::updateClipRects(const ClipRectsContext& clipRectsContext)
{
ClipRectsType clipRectsType = clipRectsContext.clipRectsType;
ASSERT(clipRectsType < NumCachedClipRectsTypes);
if (m_clipRectsCache) {
if (auto* clipRects = m_clipRectsCache->getClipRects(clipRectsType, clipRectsContext.respectOverflowClip)) {
ASSERT(clipRectsContext.rootLayer == m_clipRectsCache->m_clipRectsRoot[clipRectsType]);
ASSERT(m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] == clipRectsContext.overlayScrollbarSizeRelevancy);
#ifdef CHECK_CACHED_CLIP_RECTS
ClipRectsContext tempContext(clipRectsContext);
tempContext.clipRectsType = TemporaryClipRects;
Ref<ClipRects> tempClipRects = ClipRects::create();
calculateClipRects(tempContext, tempClipRects);
ASSERT(tempClipRects.get() == *clipRects);
#endif
return *clipRects; }
}
if (!m_clipRectsCache)
m_clipRectsCache = std::make_unique<ClipRectsCache>();
#ifndef NDEBUG
m_clipRectsCache->m_clipRectsRoot[clipRectsType] = clipRectsContext.rootLayer;
m_clipRectsCache->m_scrollbarRelevancy[clipRectsType] = clipRectsContext.overlayScrollbarSizeRelevancy;
#endif
RefPtr<ClipRects> parentClipRects;
if (auto* parentLayer = (clipRectsContext.rootLayer != this ? parent() : nullptr))
parentClipRects = parentLayer->updateClipRects(clipRectsContext);
auto clipRects = ClipRects::create();
calculateClipRects(clipRectsContext, clipRects);
if (parentClipRects && *parentClipRects == clipRects) {
m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, parentClipRects.copyRef());
return parentClipRects.releaseNonNull();
}
m_clipRectsCache->setClipRects(clipRectsType, clipRectsContext.respectOverflowClip, clipRects.copyRef());
return clipRects;
}
bool RenderLayer::mapLayerClipRectsToFragmentationLayer(ClipRects& clipRects) const
{
RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment();
if (!namedFlowFragment)
return false;
ASSERT(namedFlowFragment->parent() && namedFlowFragment->parent()->isRenderNamedFlowFragmentContainer());
ClipRectsContext targetClipRectsContext(&namedFlowFragment->fragmentContainerLayer(), TemporaryClipRects);
namedFlowFragment->fragmentContainerLayer().calculateClipRects(targetClipRectsContext, clipRects);
LayoutRect flowThreadPortionRect = namedFlowFragment->flowThreadPortionRect();
LayoutPoint portionLocation = flowThreadPortionRect.location();
LayoutRect regionContentBox = namedFlowFragment->fragmentContainer().contentBoxRect();
LayoutSize moveOffset = portionLocation - regionContentBox.location() + toLayoutSize(namedFlowFragment->fragmentContainer().scrollPosition());
ClipRect newOverflowClipRect = clipRects.overflowClipRect();
newOverflowClipRect.move(moveOffset);
clipRects.setOverflowClipRect(newOverflowClipRect);
ClipRect newFixedClipRect = clipRects.fixedClipRect();
newFixedClipRect.move(moveOffset);
clipRects.setFixedClipRect(newFixedClipRect);
ClipRect newPosClipRect = clipRects.posClipRect();
newPosClipRect.move(moveOffset);
clipRects.setPosClipRect(newPosClipRect);
return true;
}
ClipRects* RenderLayer::clipRects(const ClipRectsContext& context) const
{
ASSERT(context.clipRectsType < NumCachedClipRectsTypes);
if (!m_clipRectsCache)
return nullptr;
return m_clipRectsCache->getClipRects(context.clipRectsType, context.respectOverflowClip);
}
void RenderLayer::calculateClipRects(const ClipRectsContext& clipRectsContext, ClipRects& clipRects) const
{
if (!parent()) {
clipRects.reset();
return;
}
ClipRectsType clipRectsType = clipRectsContext.clipRectsType;
bool useCached = clipRectsType != TemporaryClipRects;
if (renderer().isRenderNamedFlowThread() && mapLayerClipRectsToFragmentationLayer(clipRects))
return;
RenderLayer* parentLayer = clipRectsContext.rootLayer != this ? parent() : nullptr;
if (parentLayer) {
if (useCached && parentLayer->clipRects(clipRectsContext))
clipRects = *parentLayer->clipRects(clipRectsContext);
else {
ClipRectsContext parentContext(clipRectsContext);
parentContext.overlayScrollbarSizeRelevancy = IgnoreOverlayScrollbarSize; parentLayer->calculateClipRects(parentContext, clipRects);
}
} else
clipRects.reset();
if (renderer().style().position() == FixedPosition) {
clipRects.setPosClipRect(clipRects.fixedClipRect());
clipRects.setOverflowClipRect(clipRects.fixedClipRect());
clipRects.setFixed(true);
} else if (renderer().style().hasInFlowPosition())
clipRects.setPosClipRect(clipRects.overflowClipRect());
else if (renderer().style().position() == AbsolutePosition)
clipRects.setOverflowClipRect(clipRects.posClipRect());
#if PLATFORM(IOS)
if (renderer().hasClipOrOverflowClip() && (clipRectsContext.respectOverflowClip == RespectOverflowClip || this != clipRectsContext.rootLayer)) {
#else
if ((renderer().hasOverflowClip() && (clipRectsContext.respectOverflowClip == RespectOverflowClip || this != clipRectsContext.rootLayer)) || renderer().hasClip()) {
#endif
LayoutPoint offset(renderer().localToContainerPoint(FloatPoint(), &clipRectsContext.rootLayer->renderer()));
if (clipRects.fixed() && &clipRectsContext.rootLayer->renderer() == &renderer().view())
offset -= toLayoutSize(renderer().view().frameView().scrollPositionForFixedPosition());
if (renderer().hasOverflowClip()) {
ClipRect newOverflowClip = downcast<RenderBox>(renderer()).overflowClipRectForChildLayers(offset, currentRenderNamedFlowFragment(), clipRectsContext.overlayScrollbarSizeRelevancy);
newOverflowClip.setAffectedByRadius(renderer().style().hasBorderRadius());
clipRects.setOverflowClipRect(intersection(newOverflowClip, clipRects.overflowClipRect()));
if (renderer().isPositioned())
clipRects.setPosClipRect(intersection(newOverflowClip, clipRects.posClipRect()));
}
if (renderer().hasClip()) {
LayoutRect newPosClip = downcast<RenderBox>(renderer()).clipRect(offset, currentRenderNamedFlowFragment());
clipRects.setPosClipRect(intersection(newPosClip, clipRects.posClipRect()));
clipRects.setOverflowClipRect(intersection(newPosClip, clipRects.overflowClipRect()));
clipRects.setFixedClipRect(intersection(newPosClip, clipRects.fixedClipRect()));
}
}
}
Ref<ClipRects> RenderLayer::parentClipRects(const ClipRectsContext& clipRectsContext) const
{
ASSERT(parent());
if (renderer().isRenderNamedFlowThread()) {
auto parentClipRects = ClipRects::create();
if (mapLayerClipRectsToFragmentationLayer(parentClipRects))
return parentClipRects;
}
if (clipRectsContext.clipRectsType == TemporaryClipRects) {
auto parentClipRects = ClipRects::create();
parent()->calculateClipRects(clipRectsContext, parentClipRects);
return parentClipRects;
}
return parent()->updateClipRects(clipRectsContext);
}
static inline ClipRect backgroundClipRectForPosition(const ClipRects& parentRects, EPosition position)
{
if (position == FixedPosition)
return parentRects.fixedClipRect();
if (position == AbsolutePosition)
return parentRects.posClipRect();
return parentRects.overflowClipRect();
}
ClipRect RenderLayer::backgroundClipRect(const ClipRectsContext& clipRectsContext) const
{
ASSERT(parent());
auto computeParentRects = [this, &clipRectsContext] () {
if (clipRectsContext.clipRectsType != TemporaryClipRects
&& parent()->enclosingPaginationLayer(IncludeCompositedPaginatedLayers) != enclosingPaginationLayer(IncludeCompositedPaginatedLayers)) {
ClipRectsContext tempContext(clipRectsContext);
tempContext.clipRectsType = TemporaryClipRects;
return parentClipRects(tempContext);
}
return parentClipRects(clipRectsContext);
};
auto parentRects = computeParentRects();
ClipRect backgroundClipRect = backgroundClipRectForPosition(parentRects, renderer().style().position());
RenderView& view = renderer().view();
if (parentRects->fixed() && &clipRectsContext.rootLayer->renderer() == &view && !backgroundClipRect.isInfinite())
backgroundClipRect.moveBy(view.frameView().scrollPositionForFixedPosition());
return backgroundClipRect;
}
void RenderLayer::calculateRects(const ClipRectsContext& clipRectsContext, const LayoutRect& paintDirtyRect, LayoutRect& layerBounds,
ClipRect& backgroundRect, ClipRect& foregroundRect, const LayoutSize& offsetFromRoot) const
{
if (clipRectsContext.rootLayer != this && parent()) {
backgroundRect = backgroundClipRect(clipRectsContext);
backgroundRect.intersect(paintDirtyRect);
} else
backgroundRect = paintDirtyRect;
LayoutSize offsetFromRootLocal = offsetFromRoot;
RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment();
if (clipRectsContext.rootLayer->isOutOfFlowRenderFlowThread() && !namedFlowFragment) {
LayoutPoint absPos = LayoutPoint(renderer().view().localToAbsolute(FloatPoint(), IsFixed));
offsetFromRootLocal += toLayoutSize(absPos);
}
layerBounds = LayoutRect(toLayoutPoint(offsetFromRootLocal), size());
foregroundRect = backgroundRect;
RenderFlowThread* flowThread = namedFlowFragment ? namedFlowFragment->flowThread() : nullptr;
if (isSelfPaintingLayer() && flowThread && !renderer().isInFlowRenderFlowThread()) {
ASSERT(namedFlowFragment->isValid());
const RenderBoxModelObject& boxModelObject = downcast<RenderBoxModelObject>(renderer());
LayoutRect layerBoundsWithVisualOverflow = namedFlowFragment->visualOverflowRectForBox(boxModelObject);
if (&boxModelObject == flowThread && flowThread->style().isFlippedBlocksWritingMode()) {
if (flowThread->style().isHorizontalWritingMode())
layerBoundsWithVisualOverflow.moveBy(LayoutPoint(0, flowThread->height()));
else
layerBoundsWithVisualOverflow.moveBy(LayoutPoint(flowThread->width(), 0));
} else {
RenderBlock* rendererContainingBlock = is<RenderBlock>(boxModelObject.enclosingBox()) ? &downcast<RenderBlock>(boxModelObject.enclosingBox()) : nullptr;
if (rendererContainingBlock)
rendererContainingBlock->flipForWritingMode(layerBoundsWithVisualOverflow);
}
layerBoundsWithVisualOverflow.move(offsetFromRootLocal);
backgroundRect.intersect(layerBoundsWithVisualOverflow);
foregroundRect = backgroundRect;
}
if (renderer().hasClipOrOverflowClip()) {
if (renderer().hasOverflowClip() && (this != clipRectsContext.rootLayer || clipRectsContext.respectOverflowClip == RespectOverflowClip)) {
foregroundRect.intersect(downcast<RenderBox>(renderer()).overflowClipRect(toLayoutPoint(offsetFromRootLocal), namedFlowFragment, clipRectsContext.overlayScrollbarSizeRelevancy));
if (renderer().style().hasBorderRadius())
foregroundRect.setAffectedByRadius(true);
}
if (renderer().hasClip()) {
LayoutRect newPosClip = downcast<RenderBox>(renderer()).clipRect(toLayoutPoint(offsetFromRootLocal), namedFlowFragment);
backgroundRect.intersect(newPosClip);
foregroundRect.intersect(newPosClip);
}
if (renderBox()->hasVisualOverflow()) {
LayoutRect layerBoundsWithVisualOverflow = namedFlowFragment ? namedFlowFragment->visualOverflowRectForBox(*renderBox()) : renderBox()->visualOverflowRect();
renderBox()->flipForWritingMode(layerBoundsWithVisualOverflow); layerBoundsWithVisualOverflow.move(offsetFromRootLocal);
if (this != clipRectsContext.rootLayer || clipRectsContext.respectOverflowClip == RespectOverflowClip)
backgroundRect.intersect(layerBoundsWithVisualOverflow);
} else {
LayoutRect bounds = renderBox()->borderBoxRectInRegion(namedFlowFragment);
if (namedFlowFragment)
bounds = namedFlowFragment->rectFlowPortionForBox(renderBox(), bounds);
bounds.move(offsetFromRootLocal);
if (this != clipRectsContext.rootLayer || clipRectsContext.respectOverflowClip == RespectOverflowClip)
backgroundRect.intersect(bounds);
if (flowThread && flowThread->isRenderNamedFlowThread()) {
if (flowThread->style().isHorizontalWritingMode())
layerBounds.shiftMaxXEdgeTo(std::max(layerBounds.maxX(), backgroundRect.rect().maxX()));
else
layerBounds.shiftMaxYEdgeTo(std::max(layerBounds.maxY(), backgroundRect.rect().maxY()));
}
}
}
}
LayoutRect RenderLayer::childrenClipRect() const
{
RenderLayer* clippingRootLayer = clippingRootForPainting();
LayoutRect layerBounds;
ClipRect backgroundRect;
ClipRect foregroundRect;
ClipRectsContext clipRectsContext(clippingRootLayer, TemporaryClipRects);
calculateRects(clipRectsContext, renderer().view().unscaledDocumentRect(), layerBounds, backgroundRect, foregroundRect, offsetFromAncestor(clipRectsContext.rootLayer));
return clippingRootLayer->renderer().localToAbsoluteQuad(FloatQuad(foregroundRect.rect())).enclosingBoundingBox();
}
LayoutRect RenderLayer::clipRectRelativeToAncestor(RenderLayer* ancestor, LayoutSize offsetFromAncestor, const LayoutRect& constrainingRect) const
{
LayoutRect layerBounds;
ClipRect backgroundRect;
ClipRect foregroundRect;
auto clipRectType = !m_enclosingPaginationLayer || m_enclosingPaginationLayer == ancestor ? PaintingClipRects : TemporaryClipRects;
ClipRectsContext clipRectsContext(ancestor, clipRectType);
calculateRects(clipRectsContext, constrainingRect, layerBounds, backgroundRect, foregroundRect, offsetFromAncestor);
return backgroundRect.rect();
}
LayoutRect RenderLayer::selfClipRect() const
{
RenderLayer* clippingRootLayer = clippingRootForPainting();
LayoutRect clipRect = clipRectRelativeToAncestor(clippingRootLayer, offsetFromAncestor(clippingRootLayer), renderer().view().documentRect());
return clippingRootLayer->renderer().localToAbsoluteQuad(FloatQuad(clipRect)).enclosingBoundingBox();
}
LayoutRect RenderLayer::localClipRect(bool& clipExceedsBounds) const
{
clipExceedsBounds = false;
RenderLayer* clippingRootLayer = clippingRootForPainting();
LayoutSize offsetFromRoot = offsetFromAncestor(clippingRootLayer);
LayoutRect clipRect = clipRectRelativeToAncestor(clippingRootLayer, offsetFromRoot, LayoutRect::infiniteRect());
if (clipRect.isInfinite())
return clipRect;
if (renderer().hasClip()) {
LayoutRect cssClipRect = downcast<RenderBox>(renderer()).clipRect(toLayoutPoint(offsetFromRoot), currentRenderNamedFlowFragment());
clipExceedsBounds = !clipRect.contains(cssClipRect);
}
clipRect.move(-offsetFromRoot);
return clipRect;
}
void RenderLayer::addBlockSelectionGapsBounds(const LayoutRect& bounds)
{
m_blockSelectionGapsBounds.unite(enclosingIntRect(bounds));
}
void RenderLayer::clearBlockSelectionGapsBounds()
{
m_blockSelectionGapsBounds = IntRect();
for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
child->clearBlockSelectionGapsBounds();
}
void RenderLayer::repaintBlockSelectionGaps()
{
for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
child->repaintBlockSelectionGaps();
if (m_blockSelectionGapsBounds.isEmpty())
return;
LayoutRect rect = m_blockSelectionGapsBounds;
rect.moveBy(-scrollPosition());
if (renderer().hasOverflowClip() && !usesCompositedScrolling())
rect.intersect(downcast<RenderBox>(renderer()).overflowClipRect(LayoutPoint(), nullptr)); if (renderer().hasClip())
rect.intersect(downcast<RenderBox>(renderer()).clipRect(LayoutPoint(), nullptr)); if (!rect.isEmpty())
renderer().repaintRectangle(rect);
}
bool RenderLayer::intersectsDamageRect(const LayoutRect& layerBounds, const LayoutRect& damageRect, const RenderLayer* rootLayer, const LayoutSize& offsetFromRoot, const LayoutRect* cachedBoundingBox) const
{
if (isRootLayer() || renderer().isDocumentElementRenderer())
return true;
if (damageRect.isInfinite())
return true;
if (damageRect.isEmpty())
return false;
if (!renderer().isRenderInline() && layerBounds.intersects(damageRect))
return true;
RenderNamedFlowFragment* namedFlowFragment = currentRenderNamedFlowFragment();
if (namedFlowFragment && renderer().flowThreadContainingBlock()) {
LayoutRect adjustedBounds = layerBounds;
adjustedBounds.moveBy(namedFlowFragment->visualOverflowRectForBox(downcast<RenderBoxModelObject>(renderer())).location());
if (adjustedBounds.intersects(damageRect))
return true;
}
if (cachedBoundingBox)
return cachedBoundingBox->intersects(damageRect);
return boundingBox(rootLayer, offsetFromRoot).intersects(damageRect);
}
LayoutRect RenderLayer::localBoundingBox(CalculateLayerBoundsFlags flags) const
{
LayoutRect result;
if (renderer().isInline() && is<RenderInline>(renderer()))
result = downcast<RenderInline>(renderer()).linesVisualOverflowBoundingBox();
else if (is<RenderTableRow>(renderer())) {
auto& tableRow = downcast<RenderTableRow>(renderer());
for (RenderTableCell* cell = tableRow.firstCell(); cell; cell = cell->nextCell()) {
LayoutRect bbox = cell->borderBoxRect();
result.unite(bbox);
LayoutRect overflowRect = tableRow.visualOverflowRect();
if (bbox != overflowRect)
result.unite(overflowRect);
}
} else {
RenderBox* box = renderBox();
ASSERT(box);
if (!(flags & DontConstrainForMask) && box->hasMask()) {
result = box->maskClipRect(LayoutPoint());
box->flipForWritingMode(result); } else {
LayoutRect bbox = box->borderBoxRect();
result = bbox;
LayoutRect overflowRect = box->visualOverflowRect();
if (bbox != overflowRect)
result.unite(overflowRect);
}
}
return result;
}
LayoutRect RenderLayer::boundingBox(const RenderLayer* ancestorLayer, const LayoutSize& offsetFromRoot, CalculateLayerBoundsFlags flags) const
{
LayoutRect result = localBoundingBox(flags);
if (renderer().view().frameView().hasFlippedBlockRenderers()) {
if (renderer().isBox())
renderBox()->flipForWritingMode(result);
else
renderer().containingBlock()->flipForWritingMode(result);
}
PaginationInclusionMode inclusionMode = ExcludeCompositedPaginatedLayers;
if (flags & UseFragmentBoxesIncludingCompositing)
inclusionMode = IncludeCompositedPaginatedLayers;
const RenderLayer* paginationLayer = nullptr;
if (flags & UseFragmentBoxesExcludingCompositing || flags & UseFragmentBoxesIncludingCompositing)
paginationLayer = enclosingPaginationLayerInSubtree(ancestorLayer, inclusionMode);
const RenderLayer* childLayer = this;
bool isPaginated = paginationLayer;
while (paginationLayer) {
result.move(childLayer->offsetFromAncestor(paginationLayer));
auto& enclosingFlowThread = downcast<RenderFlowThread>(paginationLayer->renderer());
result = enclosingFlowThread.fragmentsBoundingBox(result);
childLayer = paginationLayer;
paginationLayer = paginationLayer->parent()->enclosingPaginationLayerInSubtree(ancestorLayer, inclusionMode);
}
if (isPaginated) {
result.move(childLayer->offsetFromAncestor(ancestorLayer));
return result;
}
result.move(offsetFromRoot);
return result;
}
bool RenderLayer::getOverlapBoundsIncludingChildrenAccountingForTransformAnimations(LayoutRect& bounds) const
{
CalculateLayerBoundsFlags boundsFlags = DefaultCalculateLayerBoundsFlags & ~IncludeSelfTransform;
bounds = calculateLayerBounds(this, LayoutSize(), boundsFlags);
LayoutRect animatedBounds = bounds;
if (renderer().animation().computeExtentOfAnimation(renderer(), animatedBounds)) {
bounds = animatedBounds;
return true;
}
return false;
}
IntRect RenderLayer::absoluteBoundingBox() const
{
const RenderLayer* rootLayer = root();
return snappedIntRect(boundingBox(rootLayer, offsetFromAncestor(rootLayer)));
}
FloatRect RenderLayer::absoluteBoundingBoxForPainting() const
{
const RenderLayer* rootLayer = root();
return snapRectToDevicePixels(boundingBox(rootLayer, offsetFromAncestor(rootLayer)), renderer().document().deviceScaleFactor());
}
LayoutRect RenderLayer::calculateLayerBounds(const RenderLayer* ancestorLayer, const LayoutSize& offsetFromRoot, CalculateLayerBoundsFlags flags) const
{
if (!isSelfPaintingLayer())
return LayoutRect();
if ((flags & ExcludeHiddenDescendants) && this != ancestorLayer && !hasVisibleContent() && !hasVisibleDescendant())
return LayoutRect();
if (isRootLayer()) {
return renderer().view().unscaledDocumentRect();
}
LayoutRect boundingBoxRect = localBoundingBox(flags);
if (renderer().view().frameView().hasFlippedBlockRenderers()) {
if (is<RenderBox>(renderer()))
downcast<RenderBox>(renderer()).flipForWritingMode(boundingBoxRect);
else
renderer().containingBlock()->flipForWritingMode(boundingBoxRect);
}
if (renderer().isDocumentElementRenderer()) {
const FrameView& frameView = renderer().view().frameView();
boundingBoxRect.setWidth(std::max(boundingBoxRect.width(), frameView.contentsWidth() - boundingBoxRect.x()));
boundingBoxRect.setHeight(std::max(boundingBoxRect.height(), frameView.contentsHeight() - boundingBoxRect.y()));
}
LayoutRect unionBounds = boundingBoxRect;
if (flags & UseLocalClipRectIfPossible) {
bool clipExceedsBounds = false;
LayoutRect localClipRect = this->localClipRect(clipExceedsBounds);
if (!localClipRect.isInfinite() && !clipExceedsBounds) {
if ((flags & IncludeSelfTransform) && paintsWithTransform(PaintBehaviorNormal))
localClipRect = transform()->mapRect(localClipRect);
localClipRect.move(offsetFromAncestor(ancestorLayer));
return localClipRect;
}
}
CalculateLayerBoundsFlags descendantFlags = DefaultCalculateLayerBoundsFlags | (flags & ExcludeHiddenDescendants) | (flags & IncludeCompositedDescendants);
const_cast<RenderLayer*>(this)->updateLayerListsIfNeeded();
if (RenderLayer* reflection = reflectionLayer()) {
if (!reflection->isComposited()) {
LayoutRect childUnionBounds = reflection->calculateLayerBounds(this, reflection->offsetFromAncestor(this), descendantFlags);
unionBounds.unite(childUnionBounds);
}
}
ASSERT(isStackingContainer() || (!posZOrderList() || !posZOrderList()->size()));
#if !ASSERT_DISABLED
LayerListMutationDetector mutationChecker(const_cast<RenderLayer*>(this));
#endif
auto computeLayersUnion = [this, &unionBounds, flags, descendantFlags] (const RenderLayer& childLayer) {
if (!(flags & IncludeCompositedDescendants) && childLayer.isComposited())
return;
LayoutRect childBounds = childLayer.calculateLayerBounds(this, childLayer.offsetFromAncestor(this), descendantFlags);
unionBounds.checkedUnite(childBounds);
};
if (Vector<RenderLayer*>* negZOrderList = this->negZOrderList()) {
for (auto* childLayer : *negZOrderList)
computeLayersUnion(*childLayer);
}
if (Vector<RenderLayer*>* posZOrderList = this->posZOrderList()) {
for (auto* childLayer : *posZOrderList) {
if (childLayer->isFlowThreadCollectingGraphicsLayersUnderRegions())
continue;
computeLayersUnion(*childLayer);
}
}
if (Vector<RenderLayer*>* normalFlowList = this->normalFlowList()) {
for (auto* childLayer : *normalFlowList) {
ASSERT(!childLayer->isFlowThreadCollectingGraphicsLayersUnderRegions());
computeLayersUnion(*childLayer);
}
}
if (flags & IncludeLayerFilterOutsets)
renderer().style().filterOutsets().expandRect(unionBounds);
if ((flags & IncludeSelfTransform) && paintsWithTransform(PaintBehaviorNormal)) {
TransformationMatrix* affineTrans = transform();
boundingBoxRect = affineTrans->mapRect(boundingBoxRect);
unionBounds = affineTrans->mapRect(unionBounds);
}
unionBounds.move(offsetFromRoot);
return unionBounds;
}
void RenderLayer::clearClipRectsIncludingDescendants(ClipRectsType typeToClear)
{
if (!m_clipRectsCache)
return;
clearClipRects(typeToClear);
for (RenderLayer* l = firstChild(); l; l = l->nextSibling())
l->clearClipRectsIncludingDescendants(typeToClear);
}
void RenderLayer::clearClipRects(ClipRectsType typeToClear)
{
if (typeToClear == AllClipRectTypes)
m_clipRectsCache = nullptr;
else {
ASSERT(typeToClear < NumCachedClipRectsTypes);
m_clipRectsCache->setClipRects(typeToClear, RespectOverflowClip, nullptr);
m_clipRectsCache->setClipRects(typeToClear, IgnoreOverflowClip, nullptr);
}
}
RenderLayerBacking* RenderLayer::ensureBacking()
{
if (!m_backing) {
m_backing = std::make_unique<RenderLayerBacking>(*this);
compositor().layerBecameComposited(*this);
updateOrRemoveFilterEffectRenderer();
}
return m_backing.get();
}
void RenderLayer::clearBacking(bool layerBeingDestroyed)
{
if (m_backing && !renderer().renderTreeBeingDestroyed())
compositor().layerBecameNonComposited(*this);
m_backing = nullptr;
if (!layerBeingDestroyed)
updateOrRemoveFilterEffectRenderer();
}
bool RenderLayer::hasCompositedMask() const
{
return m_backing && m_backing->hasMaskLayer();
}
GraphicsLayer* RenderLayer::layerForScrolling() const
{
return m_backing ? m_backing->scrollingContentsLayer() : nullptr;
}
GraphicsLayer* RenderLayer::layerForHorizontalScrollbar() const
{
return m_backing ? m_backing->layerForHorizontalScrollbar() : nullptr;
}
GraphicsLayer* RenderLayer::layerForVerticalScrollbar() const
{
return m_backing ? m_backing->layerForVerticalScrollbar() : nullptr;
}
GraphicsLayer* RenderLayer::layerForScrollCorner() const
{
return m_backing ? m_backing->layerForScrollCorner() : nullptr;
}
bool RenderLayer::paintsWithTransform(PaintBehavior paintBehavior) const
{
bool paintsToWindow = !isComposited() || backing()->paintsIntoWindow();
return transform() && ((paintBehavior & PaintBehaviorFlattenCompositingLayers) || paintsToWindow);
}
bool RenderLayer::shouldPaintMask(PaintBehavior paintBehavior, PaintLayerFlags paintFlags) const
{
if (!renderer().hasMask())
return false;
bool paintsToWindow = !isComposited() || backing()->paintsIntoWindow();
if (paintsToWindow || (paintBehavior & PaintBehaviorFlattenCompositingLayers))
return true;
return (paintFlags & PaintLayerPaintingCompositingMaskPhase);
}
bool RenderLayer::shouldApplyClipPath(PaintBehavior paintBehavior, PaintLayerFlags paintFlags) const
{
if (!renderer().hasClipPath())
return false;
bool paintsToWindow = !isComposited() || backing()->paintsIntoWindow();
if (paintsToWindow || (paintBehavior & PaintBehaviorFlattenCompositingLayers))
return true;
return (paintFlags & PaintLayerPaintingCompositingClipPathPhase);
}
bool RenderLayer::backgroundIsKnownToBeOpaqueInRect(const LayoutRect& localRect) const
{
if (!isSelfPaintingLayer() && !hasSelfPaintingLayerDescendant())
return false;
if (paintsWithTransparency(PaintBehaviorNormal))
return false;
if (renderer().isDocumentElementRenderer()) {
return false;
}
if (renderer().style().visibility() != VISIBLE)
return false;
if (paintsWithFilters() && renderer().style().filter().hasFilterThatAffectsOpacity())
return false;
if (paintsWithTransform(PaintBehaviorNormal))
return false;
if (m_zOrderListsDirty || m_normalFlowListDirty)
return false;
if (renderer().isTablePart())
return false;
if (renderer().backgroundIsKnownToBeOpaqueInRect(localRect))
return true;
if (renderer().hasOverflowClip())
return false;
return listBackgroundIsKnownToBeOpaqueInRect(posZOrderList(), localRect)
|| listBackgroundIsKnownToBeOpaqueInRect(negZOrderList(), localRect)
|| listBackgroundIsKnownToBeOpaqueInRect(normalFlowList(), localRect);
}
bool RenderLayer::listBackgroundIsKnownToBeOpaqueInRect(const Vector<RenderLayer*>* list, const LayoutRect& localRect) const
{
if (!list || list->isEmpty())
return false;
for (auto iter = list->rbegin(); iter != list->rend(); ++iter) {
const RenderLayer* childLayer = *iter;
if (childLayer->isComposited())
continue;
if (!childLayer->canUseConvertToLayerCoords())
continue;
LayoutRect childLocalRect(localRect);
childLocalRect.move(-childLayer->offsetFromAncestor(this));
if (childLayer->backgroundIsKnownToBeOpaqueInRect(childLocalRect))
return true;
}
return false;
}
void RenderLayer::setParent(RenderLayer* parent)
{
if (parent == m_parent)
return;
if (m_parent && !renderer().renderTreeBeingDestroyed())
compositor().layerWillBeRemoved(*m_parent, *this);
m_parent = parent;
if (m_parent && !renderer().renderTreeBeingDestroyed())
compositor().layerWasAdded(*m_parent, *this);
}
void RenderLayer::dirtyZOrderLists()
{
ASSERT(m_layerListMutationAllowed);
ASSERT(isStackingContainer());
if (m_posZOrderList)
m_posZOrderList->clear();
if (m_negZOrderList)
m_negZOrderList->clear();
m_zOrderListsDirty = true;
if (!renderer().renderTreeBeingDestroyed()) {
if (isFlowThreadCollectingGraphicsLayersUnderRegions())
downcast<RenderFlowThread>(renderer()).setNeedsLayerToRegionMappingsUpdate();
compositor().setCompositingLayersNeedRebuild();
if (acceleratedCompositingForOverflowScrollEnabled())
compositor().setShouldReevaluateCompositingAfterLayout();
}
}
void RenderLayer::dirtyStackingContainerZOrderLists()
{
RenderLayer* sc = stackingContainer();
if (sc)
sc->dirtyZOrderLists();
}
void RenderLayer::dirtyNormalFlowList()
{
ASSERT(m_layerListMutationAllowed);
if (m_normalFlowList)
m_normalFlowList->clear();
m_normalFlowListDirty = true;
if (!renderer().renderTreeBeingDestroyed()) {
if (isFlowThreadCollectingGraphicsLayersUnderRegions())
downcast<RenderFlowThread>(renderer()).setNeedsLayerToRegionMappingsUpdate();
compositor().setCompositingLayersNeedRebuild();
if (acceleratedCompositingForOverflowScrollEnabled())
compositor().setShouldReevaluateCompositingAfterLayout();
}
}
void RenderLayer::rebuildZOrderLists()
{
ASSERT(m_layerListMutationAllowed);
ASSERT(isDirtyStackingContainer());
rebuildZOrderLists(StopAtStackingContainers, m_posZOrderList, m_negZOrderList);
m_zOrderListsDirty = false;
}
void RenderLayer::rebuildZOrderLists(CollectLayersBehavior behavior, std::unique_ptr<Vector<RenderLayer*>>& posZOrderList, std::unique_ptr<Vector<RenderLayer*>>& negZOrderList)
{
bool includeHiddenLayers = compositor().inCompositingMode();
for (RenderLayer* child = firstChild(); child; child = child->nextSibling())
if (!m_reflection || reflectionLayer() != child)
child->collectLayers(includeHiddenLayers, behavior, posZOrderList, negZOrderList);
if (posZOrderList)
std::stable_sort(posZOrderList->begin(), posZOrderList->end(), compareZIndex);
if (negZOrderList)
std::stable_sort(negZOrderList->begin(), negZOrderList->end(), compareZIndex);
}
void RenderLayer::updateNormalFlowList()
{
if (!m_normalFlowListDirty)
return;
ASSERT(m_layerListMutationAllowed);
for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
if (child->isNormalFlowOnly() && (!m_reflection || reflectionLayer() != child)) {
if (!m_normalFlowList)
m_normalFlowList = std::make_unique<Vector<RenderLayer*>>();
m_normalFlowList->append(child);
}
}
m_normalFlowListDirty = false;
}
void RenderLayer::collectLayers(bool includeHiddenLayers, CollectLayersBehavior behavior, std::unique_ptr<Vector<RenderLayer*>>& posBuffer, std::unique_ptr<Vector<RenderLayer*>>& negBuffer)
{
updateDescendantDependentFlags();
bool isStacking = behavior == StopAtStackingContexts ? isStackingContext() : isStackingContainer();
bool includeHiddenLayer = includeHiddenLayers || (m_hasVisibleContent || (m_hasVisibleDescendant && isStacking));
if (includeHiddenLayer && !isNormalFlowOnly()) {
std::unique_ptr<Vector<RenderLayer*>>& buffer = (zIndex() >= 0) ? posBuffer : negBuffer;
if (!buffer)
buffer = std::make_unique<Vector<RenderLayer*>>();
buffer->append(this);
}
if ((includeHiddenLayers || m_hasVisibleDescendant) && !isStacking) {
for (RenderLayer* child = firstChild(); child; child = child->nextSibling()) {
if (!m_reflection || reflectionLayer() != child)
child->collectLayers(includeHiddenLayers, behavior, posBuffer, negBuffer);
}
}
}
void RenderLayer::updateLayerListsIfNeeded()
{
bool shouldUpdateDescendantsAreContiguousInStackingOrder = (m_zOrderListsDirty || m_normalFlowListDirty) && isStackingContext();
updateZOrderLists();
updateNormalFlowList();
if (RenderLayer* reflectionLayer = this->reflectionLayer()) {
reflectionLayer->updateZOrderLists();
reflectionLayer->updateNormalFlowList();
}
if (shouldUpdateDescendantsAreContiguousInStackingOrder) {
updateDescendantsAreContiguousInStackingOrder();
updateZOrderLists();
updateNormalFlowList();
}
}
void RenderLayer::updateDescendantsLayerListsIfNeeded(bool recursive)
{
Vector<RenderLayer*> layersToUpdate;
if (isStackingContainer()) {
if (Vector<RenderLayer*>* list = negZOrderList()) {
for (auto* childLayer : *list)
layersToUpdate.append(childLayer);
}
}
if (Vector<RenderLayer*>* list = normalFlowList()) {
for (auto* childLayer : *list)
layersToUpdate.append(childLayer);
}
if (isStackingContainer()) {
if (Vector<RenderLayer*>* list = posZOrderList()) {
for (auto* childLayer : *list)
layersToUpdate.append(childLayer);
}
}
for (auto* childLayer : layersToUpdate) {
childLayer->updateLayerListsIfNeeded();
if (recursive)
childLayer->updateDescendantsLayerListsIfNeeded(true);
}
}
void RenderLayer::updateCompositingAndLayerListsIfNeeded()
{
if (compositor().inCompositingMode()) {
if (isDirtyStackingContainer() || m_normalFlowListDirty)
compositor().updateCompositingLayers(CompositingUpdateOnHitTest, this);
return;
}
updateLayerListsIfNeeded();
}
void RenderLayer::repaintIncludingDescendants()
{
renderer().repaint();
for (RenderLayer* current = firstChild(); current; current = current->nextSibling())
current->repaintIncludingDescendants();
if (renderer().isRenderNamedFlowFragmentContainer()) {
RenderNamedFlowFragment& region = *downcast<RenderBlockFlow>(renderer()).renderNamedFlowFragment();
if (region.isValid())
region.flowThread()->layer()->repaintIncludingDescendants();
}
}
void RenderLayer::setBackingNeedsRepaint(GraphicsLayer::ShouldClipToLayer shouldClip)
{
ASSERT(isComposited());
if (backing()->paintsIntoWindow()) {
renderer().view().repaintViewRectangle(absoluteBoundingBox());
} else
backing()->setContentsNeedDisplay(shouldClip);
}
void RenderLayer::setBackingNeedsRepaintInRect(const LayoutRect& r, GraphicsLayer::ShouldClipToLayer shouldClip)
{
ASSERT(isComposited());
if (!isComposited() || backing()->paintsIntoWindow()) {
LayoutRect absRect(r);
absRect.move(offsetFromAncestor(root()));
renderer().view().repaintViewRectangle(absRect);
} else
backing()->setContentsNeedDisplayInRect(r, shouldClip);
}
void RenderLayer::repaintIncludingNonCompositingDescendants(RenderLayerModelObject* repaintContainer)
{
renderer().repaintUsingContainer(repaintContainer, renderer().clippedOverflowRectForRepaint(repaintContainer));
for (RenderLayer* curr = firstChild(); curr; curr = curr->nextSibling()) {
if (!curr->isComposited())
curr->repaintIncludingNonCompositingDescendants(repaintContainer);
}
}
static bool createsStackingContext(const RenderLayer& layer)
{
auto& renderer = layer.renderer();
return renderer.hasTransformRelatedProperty()
|| renderer.hasClipPath()
|| renderer.hasFilter()
|| renderer.hasMask()
|| renderer.hasBackdropFilter()
#if ENABLE(CSS_COMPOSITING)
|| renderer.hasBlendMode()
#endif
|| renderer.isTransparent()
|| renderer.isPositioned()
|| renderer.style().hasFlowFrom()
|| renderer.hasReflection()
|| renderer.style().hasIsolation()
|| layer.needsCompositedScrolling()
#if PLATFORM(IOS)
|| layer.hasAcceleratedTouchScrolling()
#endif
|| (renderer.style().willChange() && renderer.style().willChange()->canCreateStackingContext());
}
bool RenderLayer::shouldBeNormalFlowOnly() const
{
if (createsStackingContext(*this))
return false;
return renderer().hasOverflowClip()
|| renderer().isCanvas()
|| renderer().isVideo()
|| renderer().isEmbeddedObject()
|| renderer().isRenderIFrame()
|| (renderer().style().specifiesColumns() && !isRootLayer())
|| renderer().isInFlowRenderFlowThread();
}
bool RenderLayer::shouldBeSelfPaintingLayer() const
{
if (!isNormalFlowOnly())
return true;
return hasOverlayScrollbars()
|| needsCompositedScrolling()
|| renderer().isTableRow()
|| renderer().isCanvas()
|| renderer().isVideo()
|| renderer().isEmbeddedObject()
|| renderer().isRenderIFrame()
|| renderer().isInFlowRenderFlowThread();
}
void RenderLayer::updateSelfPaintingLayer()
{
bool isSelfPaintingLayer = shouldBeSelfPaintingLayer();
if (m_isSelfPaintingLayer == isSelfPaintingLayer)
return;
m_isSelfPaintingLayer = isSelfPaintingLayer;
if (!parent())
return;
if (isSelfPaintingLayer)
parent()->setAncestorChainHasSelfPaintingLayerDescendant();
else
parent()->dirtyAncestorChainHasSelfPaintingLayerDescendantStatus();
}
static bool hasVisibleBoxDecorationsOrBackground(const RenderElement& renderer)
{
return renderer.hasVisibleBoxDecorations() || renderer.style().hasOutline();
}
static bool styleHasSmoothingTextMode(const RenderStyle& style)
{
FontSmoothingMode smoothingMode = style.fontDescription().fontSmoothing();
return smoothingMode == AutoSmoothing || smoothingMode == SubpixelAntialiased;
}
static const unsigned maxRendererTraversalCount = 200;
static void determineNonLayerDescendantsPaintedContent(const RenderElement& renderer, unsigned& renderersTraversed, RenderLayer::PaintedContentRequest& request)
{
for (const auto& child : childrenOfType<RenderObject>(renderer)) {
if (++renderersTraversed > maxRendererTraversalCount) {
request.makeStatesUndetermined();
return;
}
if (is<RenderText>(child)) {
const auto& renderText = downcast<RenderText>(child);
if (renderText.linesBoundingBox().isEmpty())
continue;
if (renderer.style().userSelect() != SELECT_NONE)
request.setHasPaintedContent();
if (!renderText.text()->containsOnlyWhitespace()) {
request.setHasPaintedContent();
if (request.needToDetermineSubpixelAntialiasedTextState() && styleHasSmoothingTextMode(child.style()))
request.setHasSubpixelAntialiasedText();
}
if (request.isSatisfied())
return;
}
if (!is<RenderElement>(child))
continue;
const RenderElement& renderElementChild = downcast<RenderElement>(child);
if (is<RenderLayerModelObject>(renderElementChild) && downcast<RenderLayerModelObject>(renderElementChild).hasSelfPaintingLayer())
continue;
if (hasVisibleBoxDecorationsOrBackground(renderElementChild)) {
request.setHasPaintedContent();
if (request.isSatisfied())
return;
}
if (is<RenderReplaced>(renderElementChild)) {
request.setHasPaintedContent();
if (is<RenderImage>(renderElementChild) && request.needToDetermineSubpixelAntialiasedTextState()) {
auto& imageRenderer = downcast<RenderImage>(renderElementChild);
if ((imageRenderer.isShowingAltText() || imageRenderer.hasNonBitmapImage()) && styleHasSmoothingTextMode(child.style()))
request.setHasSubpixelAntialiasedText();
}
if (request.isSatisfied())
return;
}
determineNonLayerDescendantsPaintedContent(renderElementChild, renderersTraversed, request);
if (request.isSatisfied())
return;
}
}
bool RenderLayer::hasNonEmptyChildRenderers(PaintedContentRequest& request) const
{
unsigned renderersTraversed = 0;
determineNonLayerDescendantsPaintedContent(renderer(), renderersTraversed, request);
return request.probablyHasPaintedContent();
}
bool RenderLayer::hasVisibleBoxDecorationsOrBackground() const
{
return WebCore::hasVisibleBoxDecorationsOrBackground(renderer());
}
bool RenderLayer::hasVisibleBoxDecorations() const
{
if (!hasVisibleContent())
return false;
return hasVisibleBoxDecorationsOrBackground() || hasOverflowControls();
}
bool RenderLayer::isVisuallyNonEmpty(PaintedContentRequest* request) const
{
ASSERT(!m_visibleDescendantStatusDirty);
if (!hasVisibleContent() || !renderer().style().opacity())
return false;
if (renderer().isRenderReplaced() || hasOverflowControls()) {
if (request)
request->setHasPaintedContent();
return true;
}
if (hasVisibleBoxDecorationsOrBackground()) {
if (request)
request->setHasPaintedContent();
return true;
}
PaintedContentRequest localRequest;
if (!request)
request = &localRequest;
return hasNonEmptyChildRenderers(*request);
}
void RenderLayer::updateStackingContextsAfterStyleChange(const RenderStyle* oldStyle)
{
if (!oldStyle)
return;
bool wasStackingContext = isStackingContext(oldStyle);
bool isStackingContext = this->isStackingContext();
if (isStackingContext != wasStackingContext) {
dirtyStackingContainerZOrderLists();
if (isStackingContext)
dirtyZOrderLists();
else
clearZOrderLists();
#if ENABLE(CSS_COMPOSITING)
if (parent()) {
if (isStackingContext) {
if (!hasNotIsolatedBlendingDescendantsStatusDirty() && hasNotIsolatedBlendingDescendants())
parent()->dirtyAncestorChainHasBlendingDescendants();
} else {
if (hasNotIsolatedBlendingDescendantsStatusDirty())
parent()->dirtyAncestorChainHasBlendingDescendants();
else if (hasNotIsolatedBlendingDescendants())
parent()->updateAncestorChainHasBlendingDescendants();
}
}
#endif
return;
}
if (oldStyle->zIndex() != renderer().style().zIndex() || oldStyle->visibility() != renderer().style().visibility()) {
dirtyStackingContainerZOrderLists();
if (isStackingContext)
dirtyZOrderLists();
}
}
void RenderLayer::updateScrollbarsAfterStyleChange(const RenderStyle* oldStyle)
{
RenderBox* box = renderBox();
if (!box)
return;
if (box->style().appearance() == ListboxPart)
return;
EOverflow overflowX = box->style().overflowX();
EOverflow overflowY = box->style().overflowY();
bool needsHorizontalScrollbar = box->hasOverflowClip() && ((hasHorizontalScrollbar() && styleDefinesAutomaticScrollbar(box->style(), HorizontalScrollbar)) || styleRequiresScrollbar(box->style(), HorizontalScrollbar));
bool needsVerticalScrollbar = box->hasOverflowClip() && ((hasVerticalScrollbar() && styleDefinesAutomaticScrollbar(box->style(), VerticalScrollbar)) || styleRequiresScrollbar(box->style(), VerticalScrollbar));
setHasHorizontalScrollbar(needsHorizontalScrollbar);
setHasVerticalScrollbar(needsVerticalScrollbar);
if (m_hBar && needsHorizontalScrollbar && oldStyle && oldStyle->overflowX() == OSCROLL && overflowX != OSCROLL)
m_hBar->setEnabled(true);
if (m_vBar && needsVerticalScrollbar && oldStyle && oldStyle->overflowY() == OSCROLL && overflowY != OSCROLL)
m_vBar->setEnabled(true);
if (!m_scrollDimensionsDirty)
updateScrollableAreaSet(hasScrollableHorizontalOverflow() || hasScrollableVerticalOverflow());
}
void RenderLayer::setAncestorChainHasOutOfFlowPositionedDescendant(RenderBlock* containingBlock)
{
for (RenderLayer* layer = this; layer; layer = layer->parent()) {
if (!layer->m_hasOutOfFlowPositionedDescendantDirty && layer->hasOutOfFlowPositionedDescendant())
break;
layer->m_hasOutOfFlowPositionedDescendantDirty = false;
layer->m_hasOutOfFlowPositionedDescendant = true;
layer->updateNeedsCompositedScrolling();
if (&layer->renderer() == containingBlock)
break;
}
}
void RenderLayer::dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus()
{
m_hasOutOfFlowPositionedDescendantDirty = true;
if (parent())
parent()->dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus();
}
void RenderLayer::updateOutOfFlowPositioned(const RenderStyle* oldStyle)
{
bool wasOutOfFlowPositioned = oldStyle && (oldStyle->position() == AbsolutePosition || oldStyle->position() == FixedPosition);
if (parent() && (renderer().isOutOfFlowPositioned() != wasOutOfFlowPositioned)) {
parent()->dirtyAncestorChainHasOutOfFlowPositionedDescendantStatus();
if (!renderer().renderTreeBeingDestroyed() && acceleratedCompositingForOverflowScrollEnabled())
compositor().setShouldReevaluateCompositingAfterLayout();
}
}
void RenderLayer::styleChanged(StyleDifference diff, const RenderStyle* oldStyle)
{
bool isNormalFlowOnly = shouldBeNormalFlowOnly();
if (isNormalFlowOnly != m_isNormalFlowOnly) {
m_isNormalFlowOnly = isNormalFlowOnly;
RenderLayer* p = parent();
if (p)
p->dirtyNormalFlowList();
dirtyStackingContainerZOrderLists();
}
if (renderer().isHTMLMarquee() && renderer().style().marqueeBehavior() != MNONE && renderer().isBox()) {
if (!m_marquee)
m_marquee = std::make_unique<RenderMarquee>(this);
m_marquee->updateMarqueeStyle();
}
else if (m_marquee) {
m_marquee = nullptr;
}
updateScrollbarsAfterStyleChange(oldStyle);
updateStackingContextsAfterStyleChange(oldStyle);
updateSelfPaintingLayer();
updateOutOfFlowPositioned(oldStyle);
if (!hasReflection() && m_reflection)
removeReflection();
else if (hasReflection()) {
if (!m_reflection)
createReflection();
else
m_reflection->setStyle(createReflectionStyle());
}
if (m_hBar)
m_hBar->styleChanged();
if (m_vBar)
m_vBar->styleChanged();
updateScrollCornerStyle();
updateResizerStyle();
updateDescendantDependentFlags();
updateTransform();
#if ENABLE(CSS_COMPOSITING)
updateBlendMode();
#endif
updateOrRemoveFilterClients();
updateNeedsCompositedScrolling();
compositor().layerStyleChanged(diff, *this, oldStyle);
updateOrRemoveFilterEffectRenderer();
#if PLATFORM(IOS) && ENABLE(TOUCH_EVENTS)
if (diff == StyleDifferenceRecompositeLayer || diff >= StyleDifferenceLayoutPositionedMovementOnly)
renderer().document().setTouchEventRegionsNeedUpdate();
#else
UNUSED_PARAM(diff);
#endif
}
void RenderLayer::updateScrollableAreaSet(bool hasOverflow)
{
FrameView& frameView = renderer().view().frameView();
bool isVisibleToHitTest = renderer().visibleToHitTesting();
if (HTMLFrameOwnerElement* owner = frameView.frame().ownerElement())
isVisibleToHitTest &= owner->renderer() && owner->renderer()->visibleToHitTesting();
bool isScrollable = hasOverflow && isVisibleToHitTest;
bool addedOrRemoved = false;
ASSERT(m_registeredScrollableArea == frameView.containsScrollableArea(this));
if (isScrollable) {
if (!m_registeredScrollableArea) {
addedOrRemoved = frameView.addScrollableArea(this);
m_registeredScrollableArea = true;
}
} else if (m_registeredScrollableArea) {
addedOrRemoved = frameView.removeScrollableArea(this);
m_registeredScrollableArea = false;
}
if (addedOrRemoved)
updateNeedsCompositedScrolling();
#if ENABLE(IOS_TOUCH_EVENTS)
if (addedOrRemoved) {
if (isScrollable && !hasAcceleratedTouchScrolling())
registerAsTouchEventListenerForScrolling();
else {
unregisterAsTouchEventListenerForScrolling();
}
}
#endif
}
void RenderLayer::updateScrollCornerStyle()
{
RenderElement* actualRenderer = rendererForScrollbar(renderer());
auto corner = renderer().hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(SCROLLBAR_CORNER), &actualRenderer->style()) : nullptr;
if (!corner) {
m_scrollCorner = nullptr;
return;
}
if (!m_scrollCorner) {
m_scrollCorner = createRenderer<RenderScrollbarPart>(renderer().document(), WTFMove(*corner));
m_scrollCorner->setParent(&renderer());
m_scrollCorner->initializeStyle();
} else
m_scrollCorner->setStyle(WTFMove(*corner));
}
void RenderLayer::updateResizerStyle()
{
RenderElement* actualRenderer = rendererForScrollbar(renderer());
auto resizer = renderer().hasOverflowClip() ? actualRenderer->getUncachedPseudoStyle(PseudoStyleRequest(RESIZER), &actualRenderer->style()) : nullptr;
if (!resizer) {
m_resizer = nullptr;
return;
}
if (!m_resizer) {
m_resizer = createRenderer<RenderScrollbarPart>(renderer().document(), WTFMove(*resizer));
m_resizer->setParent(&renderer());
m_resizer->initializeStyle();
} else
m_resizer->setStyle(WTFMove(*resizer));
}
RenderLayer* RenderLayer::reflectionLayer() const
{
return m_reflection ? m_reflection->layer() : nullptr;
}
void RenderLayer::createReflection()
{
ASSERT(!m_reflection);
m_reflection = createRenderer<RenderReplica>(renderer().document(), createReflectionStyle());
m_reflection->setParent(&renderer()); m_reflection->initializeStyle();
}
void RenderLayer::removeReflection()
{
if (!m_reflection->renderTreeBeingDestroyed())
m_reflection->removeLayers(this);
m_reflection->setParent(nullptr);
m_reflection = nullptr;
}
RenderStyle RenderLayer::createReflectionStyle()
{
auto newStyle = RenderStyle::create();
newStyle.inheritFrom(renderer().style());
TransformOperations transform;
switch (renderer().style().boxReflect()->direction()) {
case ReflectionBelow:
transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), Length(100., Percent), TransformOperation::TRANSLATE));
transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), renderer().style().boxReflect()->offset(), TransformOperation::TRANSLATE));
transform.operations().append(ScaleTransformOperation::create(1.0, -1.0, ScaleTransformOperation::SCALE));
break;
case ReflectionAbove:
transform.operations().append(ScaleTransformOperation::create(1.0, -1.0, ScaleTransformOperation::SCALE));
transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), Length(100., Percent), TransformOperation::TRANSLATE));
transform.operations().append(TranslateTransformOperation::create(Length(0, Fixed), renderer().style().boxReflect()->offset(), TransformOperation::TRANSLATE));
break;
case ReflectionRight:
transform.operations().append(TranslateTransformOperation::create(Length(100., Percent), Length(0, Fixed), TransformOperation::TRANSLATE));
transform.operations().append(TranslateTransformOperation::create(renderer().style().boxReflect()->offset(), Length(0, Fixed), TransformOperation::TRANSLATE));
transform.operations().append(ScaleTransformOperation::create(-1.0, 1.0, ScaleTransformOperation::SCALE));
break;
case ReflectionLeft:
transform.operations().append(ScaleTransformOperation::create(-1.0, 1.0, ScaleTransformOperation::SCALE));
transform.operations().append(TranslateTransformOperation::create(Length(100., Percent), Length(0, Fixed), TransformOperation::TRANSLATE));
transform.operations().append(TranslateTransformOperation::create(renderer().style().boxReflect()->offset(), Length(0, Fixed), TransformOperation::TRANSLATE));
break;
}
newStyle.setTransform(transform);
newStyle.setMaskBoxImage(renderer().style().boxReflect()->mask());
newStyle.setZIndex(0);
return newStyle;
}
void RenderLayer::updateOrRemoveFilterClients()
{
if (!hasFilter()) {
FilterInfo::remove(*this);
return;
}
if (renderer().style().filter().hasReferenceFilter() && !renderer().isSVGRoot())
FilterInfo::get(*this).updateReferenceFilterClients(renderer().style().filter());
else if (FilterInfo* filterInfo = FilterInfo::getIfExists(*this))
filterInfo->removeReferenceFilterClients();
}
void RenderLayer::updateOrRemoveFilterEffectRenderer()
{
if (!paintsWithFilters()) {
if (FilterInfo* filterInfo = FilterInfo::getIfExists(*this))
filterInfo->setRenderer(nullptr);
if (!renderer().style().filter().hasReferenceFilter())
return;
}
FilterInfo& filterInfo = FilterInfo::get(*this);
if (!filterInfo.renderer()) {
RefPtr<FilterEffectRenderer> filterRenderer = FilterEffectRenderer::create();
filterRenderer->setFilterScale(page().deviceScaleFactor());
filterRenderer->setRenderingMode(renderer().settings().acceleratedFiltersEnabled() ? Accelerated : Unaccelerated);
filterInfo.setRenderer(WTFMove(filterRenderer));
renderer().view().setHasSoftwareFilters(true);
} else if (filterInfo.renderer()->filterScale() != page().deviceScaleFactor()) {
filterInfo.renderer()->setFilterScale(page().deviceScaleFactor());
filterInfo.renderer()->clearIntermediateResults();
}
if (!filterInfo.renderer()->build(renderer(), renderer().style().filter(), FilterProperty))
filterInfo.setRenderer(nullptr);
}
void RenderLayer::filterNeedsRepaint()
{
if (Element* element = enclosingElement())
element->invalidateStyleAndLayerComposition();
renderer().repaint();
}
void RenderLayer::paintNamedFlowThreadInsideRegion(GraphicsContext& context, RenderNamedFlowFragment* region, LayoutRect paintDirtyRect, LayoutPoint paintOffset, PaintBehavior paintBehavior, PaintLayerFlags paintFlags)
{
LayoutRect regionContentBox = downcast<RenderBox>(region->layerOwner()).contentBoxRect();
CurrentRenderRegionMaintainer regionMaintainer(*region);
region->setRegionObjectsRegionStyle();
LayoutSize moveOffset = region->flowThreadPortionLocation() - (paintOffset + regionContentBox.location()) + toLayoutSize(region->fragmentContainer().scrollPosition());
FloatPoint adjustedPaintOffset = roundPointToDevicePixels(toLayoutPoint(moveOffset), renderer().document().deviceScaleFactor());
context.save();
context.translate(-adjustedPaintOffset.x(), -adjustedPaintOffset.y());
LayoutSize subpixelOffset = moveOffset - toLayoutSize(LayoutPoint(adjustedPaintOffset));
paintDirtyRect.move(moveOffset);
paint(context, paintDirtyRect, LayoutSize(-subpixelOffset.width(), -subpixelOffset.height()), paintBehavior, nullptr, paintFlags | PaintLayerTemporaryClipRects);
region->restoreRegionObjectsOriginalStyle();
context.restore();
}
void RenderLayer::paintFlowThreadIfRegionForFragments(const LayerFragments& fragments, GraphicsContext& context, const LayerPaintingInfo& paintingInfo, PaintLayerFlags paintFlags)
{
if (!renderer().isRenderNamedFlowFragmentContainer())
return;
RenderBlockFlow& renderNamedFlowFragmentContainer = downcast<RenderBlockFlow>(renderer());
RenderNamedFlowFragment* flowFragment = renderNamedFlowFragmentContainer.renderNamedFlowFragment();
if (!flowFragment->isValid())
return;
RenderNamedFlowThread* flowThread = flowFragment->namedFlowThread();
RenderLayer* flowThreadLayer = flowThread->layer();
LayoutRect regionClipRect = LayoutRect::infiniteRect();
if (flowFragment->shouldClipFlowThreadContent()) {
regionClipRect = renderNamedFlowFragmentContainer.paddingBoxRect();
if (!isComposited())
regionClipRect.move(offsetFromAncestor(paintingInfo.rootLayer));
}
for (const auto& fragment : fragments) {
ClipRect clipRect = fragment.foregroundRect;
if (flowFragment->shouldClipFlowThreadContent())
clipRect.intersect(regionClipRect);
bool shouldClip = !clipRect.isInfinite();
if (shouldClip)
clipToRect(context, paintingInfo, clipRect);
flowThreadLayer->paintNamedFlowThreadInsideRegion(context, flowFragment, paintingInfo.paintDirtyRect, fragment.layerBounds.location() + paintingInfo.subpixelOffset,
paintingInfo.paintBehavior, paintFlags);
if (shouldClip)
restoreClip(context, paintingInfo, clipRect);
}
}
RenderLayer* RenderLayer::hitTestFlowThreadIfRegionForFragments(const LayerFragments& fragments, RenderLayer*, const HitTestRequest& request, HitTestResult& result, const LayoutRect& hitTestRect,
const HitTestLocation& hitTestLocation,
const HitTestingTransformState* transformState,
double* zOffsetForDescendants, double* zOffset,
const HitTestingTransformState* unflattenedTransformState, bool depthSortDescendants)
{
if (!renderer().isRenderNamedFlowFragmentContainer())
return nullptr;
RenderNamedFlowFragment& region = *downcast<RenderBlockFlow>(renderer()).renderNamedFlowFragment();
if (!region.isValid())
return nullptr;
RenderFlowThread* flowThread = region.flowThread();
LayoutPoint portionLocation = region.flowThreadPortionRect().location();
if (flowThread->style().isFlippedBlocksWritingMode()) {
if (flowThread->style().isHorizontalWritingMode())
portionLocation.setY(flowThread->height() - (portionLocation.y() + region.contentHeight()));
else
portionLocation.setX(flowThread->width() - (portionLocation.x() + region.contentWidth()));
}
LayoutRect regionContentBox = downcast<RenderBlockFlow>(renderer()).contentBoxRect();
RenderLayer* resultLayer = nullptr;
for (int i = fragments.size() - 1; i >= 0; --i) {
const LayerFragment& fragment = fragments.at(i);
if (!fragment.backgroundRect.intersects(hitTestLocation))
continue;
LayoutSize hitTestOffset = portionLocation - (fragment.layerBounds.location() + regionContentBox.location()) + toLayoutSize(region.fragmentContainer().scrollPosition());
HitTestRequest newRequest(request.type() | HitTestRequest::IgnoreClipping | HitTestRequest::DisallowUserAgentShadowContent);
HitTestLocation newHitTestLocation(hitTestLocation, hitTestOffset);
LayoutRect hitTestRectInFlowThread = hitTestRect;
hitTestRectInFlowThread.move(hitTestOffset);
hitTestRectInFlowThread.expand(LayoutSize(fabs((double)hitTestOffset.width()), fabs((double)hitTestOffset.height())));
CurrentRenderRegionMaintainer regionMaintainer(region);
HitTestResult tempResult(result.hitTestLocation());
RenderLayer* hitLayer = flowThread->layer()->hitTestLayer(flowThread->layer(), nullptr, newRequest, tempResult, hitTestRectInFlowThread, newHitTestLocation, false, transformState, zOffsetForDescendants);
if (result.isRectBasedTest())
result.append(tempResult);
if (isHitCandidate(hitLayer, depthSortDescendants, zOffset, unflattenedTransformState)) {
resultLayer = hitLayer;
if (!result.isRectBasedTest())
result = tempResult;
if (!depthSortDescendants)
break;
}
}
return resultLayer;
}
RenderNamedFlowFragment* RenderLayer::currentRenderNamedFlowFragment() const
{
return renderer().currentRenderNamedFlowFragment();
}
}
#if ENABLE(TREE_DEBUGGING)
void showLayerTree(const WebCore::RenderLayer* layer)
{
if (!layer)
return;
WTF::String output = externalRepresentation(&layer->renderer().frame(), WebCore::RenderAsTextShowAllLayers | WebCore::RenderAsTextShowLayerNesting | WebCore::RenderAsTextShowCompositedLayers | WebCore::RenderAsTextShowAddresses | WebCore::RenderAsTextShowIDAndClass | WebCore::RenderAsTextDontUpdateLayout | WebCore::RenderAsTextShowLayoutState | WebCore::RenderAsTextShowOverflow | WebCore::RenderAsTextShowSVGGeometry | WebCore::RenderAsTextShowLayerFragments);
fprintf(stderr, "\n%s\n", output.utf8().data());
}
void showLayerTree(const WebCore::RenderObject* renderer)
{
if (!renderer)
return;
showLayerTree(renderer->enclosingLayer());
}
#endif