Skip to content

Commit

Permalink
Account for perspective and preserve-3d in mapToVisualRectInAncestorS…
Browse files Browse the repository at this point in the history
…pace.

BUG=590095
CQ_INCLUDE_TRYBOTS=master.tryserver.chromium.linux:linux_layout_tests_slimming_paint_v2

Review-Url: https://codereview.chromium.org/2727093002
Cr-Commit-Position: refs/heads/master@{#454522}
(cherry picked from commit 3a2c466)

Review-Url: https://codereview.chromium.org/2735643002 .
Cr-Commit-Position: refs/branch-heads/3029@{#11}
Cr-Branched-From: 939b32e-refs/heads/master@{#454471}
  • Loading branch information
chrishtr committed Mar 6, 2017
1 parent 268cf0f commit 8013d90
Show file tree
Hide file tree
Showing 19 changed files with 317 additions and 110 deletions.
4 changes: 4 additions & 0 deletions third_party/WebKit/LayoutTests/TestExpectations
Original file line number Diff line number Diff line change
Expand Up @@ -743,6 +743,10 @@ crbug.com/248938 virtual/threaded/animations/dynamic-stylesheet-loading.html [ P
crbug.com/638693 virtual/threaded/animations/display-inline-style-adjust.html [ Pass Crash Failure ]
crbug.com/421283 html/marquee/marquee-scrollamount.html [ Pass Failure ]

crbug.com/590095 virtual/disable-spinvalidation/paint/invalidation/invalidation-with-scale-transform.html [ NeedsRebaseline ]
crbug.com/590095 virtual/disable-spinvalidation/paint/invalidation/transform-layout-repaint.html [ NeedsRebaseline ]
crbug.com/590095 virtual/disable-spinvalidation/paint/invalidation/reflection-repaint-test.html [ NeedsRebaseline ]

# TODO(oshima): Mac Android are currently not supported.
crbug.com/567837 [ Mac Android ] virtual/scalefactor200withzoom/fast/hidpi/static [ Skip ]

Expand Down
121 changes: 76 additions & 45 deletions third_party/WebKit/Source/core/layout/LayoutBox.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1160,28 +1160,32 @@ LayoutRect LayoutBox::clippingRect() const {
}

bool LayoutBox::mapScrollingContentsRectToBoxSpace(
LayoutRect& rect,
TransformState& transformState,
TransformState::TransformAccumulation accumulation,
VisualRectFlags visualRectFlags) const {
if (!hasClipRelatedProperty())
return true;

if (hasOverflowClip()) {
LayoutSize offset = LayoutSize(-scrolledContentOffset());
rect.move(offset);
transformState.move(offset, accumulation);
}

// This won't work fully correctly for fixed-position elements, who should
// receive CSS clip but for whom the current object is not in the containing
// block chain.
LayoutRect clipRect = clippingRect();

transformState.flatten();
LayoutRect rect(transformState.lastPlanarQuad().enclosingBoundingBox());
bool doesIntersect;
if (visualRectFlags & EdgeInclusive) {
doesIntersect = rect.inclusiveIntersect(clipRect);
} else {
rect.intersect(clipRect);
doesIntersect = !rect.isEmpty();
}
transformState.setQuad(FloatQuad(FloatRect(rect)));

return doesIntersect;
}
Expand Down Expand Up @@ -2308,35 +2312,36 @@ LayoutRect LayoutBox::localVisualRect() const {
}

void LayoutBox::inflateVisualRectForFilterUnderContainer(
LayoutRect& rect,
TransformState& transformState,
const LayoutObject& container,
const LayoutBoxModelObject* ancestorToStopAt) const {
transformState.flatten();
// Apply visual overflow caused by reflections and filters defined on objects
// between this object and container (not included) or ancestorToStopAt
// (included).
LayoutSize offsetFromContainer = this->offsetFromContainer(&container);
rect.move(offsetFromContainer);
transformState.move(offsetFromContainer);
for (LayoutObject* parent = this->parent(); parent && parent != container;
parent = parent->parent()) {
if (parent->isBox()) {
// Convert rect into coordinate space of parent to apply parent's
// reflection and filter.
LayoutSize parentOffset = parent->offsetFromAncestorContainer(&container);
rect.move(-parentOffset);
toLayoutBox(parent)->inflateVisualRectForFilter(rect);
rect.move(parentOffset);
transformState.move(-parentOffset);
toLayoutBox(parent)->inflateVisualRectForFilter(transformState);
transformState.move(parentOffset);
}
if (parent == ancestorToStopAt)
break;
}
rect.move(-offsetFromContainer);
transformState.move(-offsetFromContainer);
}

bool LayoutBox::mapToVisualRectInAncestorSpace(
bool LayoutBox::mapToVisualRectInAncestorSpaceInternal(
const LayoutBoxModelObject* ancestor,
LayoutRect& rect,
TransformState& transformState,
VisualRectFlags visualRectFlags) const {
inflateVisualRectForFilter(rect);
inflateVisualRectForFilter(transformState);

if (ancestor == this)
return true;
Expand All @@ -2356,88 +2361,114 @@ bool LayoutBox::mapToVisualRectInAncestorSpace(
if (!container)
return true;

if (skipInfo.filterSkipped())
inflateVisualRectForFilterUnderContainer(rect, *container, ancestor);

// We are now in our parent container's coordinate space. Apply our transform
// to obtain a bounding box in the parent's coordinate space that encloses us.
if (hasLayer() && layer()->transform()) {
// Use enclosingIntRect because we cannot properly compute pixel snapping
// for painted elements within the transform since we don't know the desired
// subpixel accumulation at this point, and the transform may include a
// scale.
rect = LayoutRect(layer()->transform()->mapRect(enclosingIntRect(rect)));
}
LayoutPoint topLeft = rect.location();
LayoutPoint containerOffset;
if (container->isBox()) {
topLeft.moveBy(physicalLocation(toLayoutBox(container)));
containerOffset.moveBy(physicalLocation(toLayoutBox(container)));

// If the row is the ancestor, however, add its offset back in. In effect,
// this passes from the joint <td> / <tr> coordinate space to the parent
// space, then back to <tr> / <td>.
if (tableRowContainer) {
topLeft.moveBy(
containerOffset.moveBy(
-tableRowContainer->physicalLocation(toLayoutBox(container)));
}
} else if (container->isRuby()) {
// TODO(wkorman): Generalize Ruby specialization and/or document more
// clearly. See the accompanying specialization in
// LayoutInline::mapToVisualRectInAncestorSpace.
topLeft.moveBy(physicalLocation());
// LayoutInline::mapToVisualRectInAncestorSpaceInternal.
containerOffset.moveBy(physicalLocation());
} else {
topLeft.moveBy(location());
containerOffset.moveBy(location());
}

const ComputedStyle& styleToUse = styleRef();
EPosition position = styleToUse.position();
if (position == EPosition::kAbsolute && container->isInFlowPositioned() &&
container->isLayoutInline()) {
topLeft +=
toLayoutInline(container)->offsetForInFlowPositionedInline(*this);
containerOffset.move(
toLayoutInline(container)->offsetForInFlowPositionedInline(*this));
} else if (styleToUse.hasInFlowPosition() && layer()) {
// Apply the relative position offset when invalidating a rectangle. The
// layer is translated, but the layout box isn't, so we need to do this to
// get the right dirty rect. Since this is called from
// LayoutObject::setStyle, the relative position flag on the LayoutObject
// has been cleared, so use the one on the style().
topLeft += layer()->offsetForInFlowPosition();
containerOffset.move(layer()->offsetForInFlowPosition());
}

bool preserve3D = container->style()->preserves3D() || style()->preserves3D();

TransformState::TransformAccumulation accumulation =
preserve3D ? TransformState::AccumulateTransform
: TransformState::FlattenTransform;

if (skipInfo.filterSkipped()) {
inflateVisualRectForFilterUnderContainer(transformState, *container,
ancestor);
}

// We are now in our parent container's coordinate space. Apply our transform
// to obtain a bounding box in the parent's coordinate space that encloses us.
if (shouldUseTransformFromContainer(container)) {
TransformationMatrix t;
getTransformFromContainer(container, toLayoutSize(containerOffset), t);
transformState.applyTransform(t, accumulation);

// Use enclosingBoundingBox because we cannot properly compute pixel
// snapping for painted elements within the transform since we don't know
// the desired subpixel accumulation at this point, and the transform may
// include a scale.
if (!preserve3D) {
transformState.flatten();
transformState.setQuad(
FloatQuad(transformState.lastPlanarQuad().enclosingBoundingBox()));
}
} else {
transformState.move(toLayoutSize(containerOffset), accumulation);
}

// FIXME: We ignore the lightweight clipping rect that controls use, since if
// |o| is in mid-layout, its controlClipRect will be wrong. For overflow clip
// we use the values cached by the layer.
rect.setLocation(topLeft);

if (container->isBox() && container != ancestor &&
!toLayoutBox(container)->mapScrollingContentsRectToBoxSpace(
rect, visualRectFlags))
transformState, accumulation, visualRectFlags))
return false;

if (skipInfo.ancestorSkipped()) {
// If the ancestor is below the container, then we need to map the rect into
// ancestor's coordinates.
LayoutSize containerOffset =
ancestor->offsetFromAncestorContainer(container);
rect.move(-containerOffset);
transformState.move(-containerOffset, accumulation);
// If the ancestor is fixed, then the rect is already in its coordinates so
// doesn't need viewport-adjusting.
if (ancestor->style()->position() != EPosition::kFixed &&
container->isLayoutView() && position == EPosition::kFixed)
rect.move(toLayoutView(container)->offsetForFixedPosition(true));
container->isLayoutView() && position == EPosition::kFixed) {
transformState.move(toLayoutView(container)->offsetForFixedPosition(true),
accumulation);
}
return true;
}

if (container->isLayoutView())
return toLayoutView(container)->mapToVisualRectInAncestorSpace(
ancestor, rect, position == EPosition::kFixed ? IsFixed : 0,
return toLayoutView(container)->mapToVisualRectInAncestorSpaceInternal(
ancestor, transformState, position == EPosition::kFixed ? IsFixed : 0,
visualRectFlags);
else
return container->mapToVisualRectInAncestorSpace(ancestor, rect,
visualRectFlags);
return container->mapToVisualRectInAncestorSpaceInternal(
ancestor, transformState, visualRectFlags);
}

void LayoutBox::inflateVisualRectForFilter(LayoutRect& visualRect) const {
if (layer() && layer()->hasFilterInducingProperty())
visualRect = layer()->mapLayoutRectForFilter(visualRect);
void LayoutBox::inflateVisualRectForFilter(
TransformState& transformState) const {
if (!layer() || !layer()->hasFilterInducingProperty())
return;

transformState.flatten();
LayoutRect rect(transformState.lastPlanarQuad().boundingBox());
transformState.setQuad(
FloatQuad(FloatRect(layer()->mapLayoutRectForFilter(rect))));
}

void LayoutBox::updateLogicalWidth() {
Expand Down
22 changes: 13 additions & 9 deletions third_party/WebKit/Source/core/layout/LayoutBox.h
Original file line number Diff line number Diff line change
Expand Up @@ -892,9 +892,9 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject {

bool paintedOutputOfObjectHasNoEffectRegardlessOfSize() const override;
LayoutRect localVisualRect() const override;
bool mapToVisualRectInAncestorSpace(
bool mapToVisualRectInAncestorSpaceInternal(
const LayoutBoxModelObject* ancestor,
LayoutRect&,
TransformState&,
VisualRectFlags = DefaultVisualRectFlags) const override;

LayoutUnit containingBlockLogicalHeightForGetComputedStyle() const;
Expand Down Expand Up @@ -1235,12 +1235,16 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject {
virtual IntSize originAdjustmentForScrollbars() const;
IntSize scrolledContentOffset() const;

// Maps a rect in scrolling contents space to box space and apply overflow
// clip if needed. Returns true if no clipping applied or the rect actually
// intersects the clipping region. If edgeInclusive is true, then this method
// may return true even if the resulting rect has zero area.
// Maps from scrolling contents space to box space and apply overflow
// clip if needed. Returns true if no clipping applied or the flattened quad
// bounds actually intersects the clipping region. If edgeInclusive is true,
// then this method may return true even if the resulting rect has zero area.
//
// When applying offsets and not clips, the TransformAccumulation is
// respected. If there is a clip, the TransformState is flattened first.
bool mapScrollingContentsRectToBoxSpace(
LayoutRect&,
TransformState&,
TransformState::TransformAccumulation,
VisualRectFlags = DefaultVisualRectFlags) const;

virtual bool hasRelativeLogicalWidth() const;
Expand Down Expand Up @@ -1568,9 +1572,9 @@ class CORE_EXPORT LayoutBox : public LayoutBoxModelObject {

void updateBackgroundAttachmentFixedStatusAfterStyleChange();

void inflateVisualRectForFilter(LayoutRect&) const;
void inflateVisualRectForFilter(TransformState&) const;
void inflateVisualRectForFilterUnderContainer(
LayoutRect&,
TransformState&,
const LayoutObject& container,
const LayoutBoxModelObject* ancestorToStopAt) const;

Expand Down
11 changes: 7 additions & 4 deletions third_party/WebKit/Source/core/layout/LayoutFlowThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -85,15 +85,18 @@ void LayoutFlowThread::validateColumnSets() {
generateColumnSetIntervalTree();
}

bool LayoutFlowThread::mapToVisualRectInAncestorSpace(
bool LayoutFlowThread::mapToVisualRectInAncestorSpaceInternal(
const LayoutBoxModelObject* ancestor,
LayoutRect& rect,
TransformState& transformState,
VisualRectFlags visualRectFlags) const {
// A flow thread should never be an invalidation container.
DCHECK(ancestor != this);
transformState.flatten();
LayoutRect rect(transformState.lastPlanarQuad().boundingBox());
rect = fragmentsBoundingBox(rect);
return LayoutBlockFlow::mapToVisualRectInAncestorSpace(ancestor, rect,
visualRectFlags);
transformState.setQuad(FloatQuad(FloatRect(rect)));
return LayoutBlockFlow::mapToVisualRectInAncestorSpaceInternal(
ancestor, transformState, visualRectFlags);
}

void LayoutFlowThread::layout() {
Expand Down
4 changes: 2 additions & 2 deletions third_party/WebKit/Source/core/layout/LayoutFlowThread.h
Original file line number Diff line number Diff line change
Expand Up @@ -115,9 +115,9 @@ class CORE_EXPORT LayoutFlowThread : public LayoutBlockFlow {
return !m_columnSetsInvalidated && !m_multiColumnSetList.isEmpty();
}

bool mapToVisualRectInAncestorSpace(
bool mapToVisualRectInAncestorSpaceInternal(
const LayoutBoxModelObject* ancestor,
LayoutRect&,
TransformState&,
VisualRectFlags = DefaultVisualRectFlags) const override;

LayoutUnit pageLogicalHeightForOffset(LayoutUnit);
Expand Down
25 changes: 18 additions & 7 deletions third_party/WebKit/Source/core/layout/LayoutInline.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1193,9 +1193,9 @@ LayoutRect LayoutInline::visualOverflowRect() const {
return overflowRect;
}

bool LayoutInline::mapToVisualRectInAncestorSpace(
bool LayoutInline::mapToVisualRectInAncestorSpaceInternal(
const LayoutBoxModelObject* ancestor,
LayoutRect& rect,
TransformState& transformState,
VisualRectFlags visualRectFlags) const {
if (ancestor == this)
return true;
Expand All @@ -1205,26 +1205,37 @@ bool LayoutInline::mapToVisualRectInAncestorSpace(
if (!container)
return true;

bool preserve3D = container->style()->preserves3D() || style()->preserves3D();

TransformState::TransformAccumulation accumulation =
preserve3D ? TransformState::AccumulateTransform
: TransformState::FlattenTransform;

if (style()->hasInFlowPosition() && layer()) {
// Apply the in-flow position offset when invalidating a rectangle. The
// layer is translated, but the layout box isn't, so we need to do this to
// get the right dirty rect. Since this is called from LayoutObject::
// setStyle, the relative position flag on the LayoutObject has been
// cleared, so use the one on the style().
rect.move(layer()->offsetForInFlowPosition());
transformState.move(layer()->offsetForInFlowPosition(), accumulation);
}

LayoutBox* containerBox =
container->isBox() ? toLayoutBox(container) : nullptr;
if (containerBox && container != ancestor &&
!containerBox->mapScrollingContentsRectToBoxSpace(rect, visualRectFlags))
!containerBox->mapScrollingContentsRectToBoxSpace(
transformState, accumulation, visualRectFlags))
return false;

// TODO(wkorman): Generalize Ruby specialization and/or document more clearly.
if (containerBox && !isRuby())
if (containerBox && !isRuby()) {
transformState.flatten();
LayoutRect rect(transformState.lastPlanarQuad().boundingBox());
containerBox->flipForWritingMode(rect);
return container->mapToVisualRectInAncestorSpace(ancestor, rect,
visualRectFlags);
transformState.setQuad(FloatQuad(FloatRect(rect)));
}
return container->mapToVisualRectInAncestorSpaceInternal(
ancestor, transformState, visualRectFlags);
}

LayoutSize LayoutInline::offsetFromContainer(
Expand Down
4 changes: 2 additions & 2 deletions third_party/WebKit/Source/core/layout/LayoutInline.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,9 +295,9 @@ class CORE_EXPORT LayoutInline : public LayoutBoxModelObject {
// invalidation.
LayoutRect localVisualRect() const override;

bool mapToVisualRectInAncestorSpace(
bool mapToVisualRectInAncestorSpaceInternal(
const LayoutBoxModelObject* ancestor,
LayoutRect&,
TransformState&,
VisualRectFlags = DefaultVisualRectFlags) const final;

PositionWithAffinity positionForPoint(const LayoutPoint&) final;
Expand Down
Loading

0 comments on commit 8013d90

Please sign in to comment.