Skip to content

Commit

Permalink
Fix PaintInvalidationState mapping for absolute whose container is ab…
Browse files Browse the repository at this point in the history
…ove paintInvalidationContainer

BUG=591199
TEST=VisualRectMappingTest.ContainerOfAbsoluteAbovePaintInvalidationContainer

Review URL: https://codereview.chromium.org/1857373002

Cr-Commit-Position: refs/heads/master@{#385501}
  • Loading branch information
wangxianzhu authored and Commit bot committed Apr 6, 2016
1 parent 23af05c commit 733202f
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 21 deletions.
55 changes: 36 additions & 19 deletions third_party/WebKit/Source/core/layout/PaintInvalidationState.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -134,19 +134,10 @@ PaintInvalidationState::PaintInvalidationState(const PaintInvalidationState& par

ASSERT(parentState.m_didUpdateForChildren);

EPosition position = currentObject.styleRef().position();

if (currentObject.isPaintInvalidationContainer()) {
m_paintInvalidationContainer = toLayoutBoxModelObject(&currentObject);
if (currentObject.styleRef().isStackingContext()) {
if (currentObject.styleRef().isStackingContext())
m_paintInvalidationContainerForStackedContents = toLayoutBoxModelObject(&currentObject);
// Adjust cached offsets for absolute-position to be relative to this new paintInvalidationContainer.
if (m_cachedOffsetsForAbsolutePositionEnabled && m_cachedOffsetsEnabled) {
m_paintOffsetForAbsolutePosition -= m_paintOffset;
if (m_clippedForAbsolutePosition)
m_clipRectForAbsolutePosition.move(-m_paintOffset);
}
}
} else if (currentObject.isLayoutView()) {
// m_paintInvalidationContainerForStackedContents is only for stacked descendants in its own frame,
// because it doesn't establish stacking context for stacked contents in sub-frames.
Expand Down Expand Up @@ -194,31 +185,52 @@ PaintInvalidationState::PaintInvalidationState(const PaintInvalidationState& par
m_forcedSubtreeInvalidationWithinContainer = false;
m_forcedSubtreeInvalidationRectUpdateWithinContainer = false;

if (currentObject == m_paintInvalidationContainerForStackedContents
&& currentObject != m_containerForAbsolutePosition
&& m_cachedOffsetsForAbsolutePositionEnabled
&& m_cachedOffsetsEnabled) {
// The current object is the new paintInvalidationContainer for absolute-position descendants but is not their container.
// Call updateForCurrentObject() before resetting m_paintOffset to get paint offset of the current object
// from the original paintInvalidationContainerForStackingContents, then use this paint offset to adjust
// m_paintOffsetForAbsolutePosition.
updateForCurrentObject(parentState);
m_paintOffsetForAbsolutePosition -= m_paintOffset;
if (m_clippedForAbsolutePosition)
m_clipRectForAbsolutePosition.move(-m_paintOffset);
}

m_clipped = false; // Will be updated in updateForChildren().
m_paintOffset = LayoutSize();
return;
}

updateForCurrentObject(parentState);
}

void PaintInvalidationState::updateForCurrentObject(const PaintInvalidationState& parentState)
{
if (!m_cachedOffsetsEnabled)
return;

if (currentObject.isLayoutView()) {
ASSERT(&parentState.m_currentObject == toLayoutView(currentObject).frame()->ownerLayoutObject());
if (m_currentObject.isLayoutView()) {
ASSERT(&parentState.m_currentObject == toLayoutView(m_currentObject).frame()->ownerLayoutObject());
m_paintOffset += toLayoutBox(parentState.m_currentObject).contentBoxOffset();
// a LayoutView paints with a defined size but a pixel-rounded offset.
m_paintOffset = LayoutSize(roundedIntSize(m_paintOffset));
return;
}

EPosition position = m_currentObject.styleRef().position();

if (position == FixedPosition) {
if (m_paintInvalidationContainer != currentObject.view() && m_paintInvalidationContainer->view() == currentObject.view()) {
if (m_paintInvalidationContainer != m_currentObject.view() && m_paintInvalidationContainer->view() == m_currentObject.view()) {
// TODO(crbug.com/598762): localToAncestorPoint() is incorrect for fixed-position when paintInvalidationContainer
// is under the containing LayoutView.
m_cachedOffsetsEnabled = false;
return;
}
// Use slow path to get the offset of the fixed-position, and enable fast path for descendants.
FloatPoint fixedOffset = currentObject.localToAncestorPoint(FloatPoint(), m_paintInvalidationContainer, TraverseDocumentBoundaries);
FloatPoint fixedOffset = m_currentObject.localToAncestorPoint(FloatPoint(), m_paintInvalidationContainer, TraverseDocumentBoundaries);
m_paintOffset = LayoutSize(fixedOffset.x(), fixedOffset.y());
// In the above way to get paint offset, we can't get accurate clip rect, so just assume no clip.
// Clip on fixed-position is rare, in case that paintInvalidationContainer crosses frame boundary
Expand All @@ -242,11 +254,11 @@ PaintInvalidationState::PaintInvalidationState(const PaintInvalidationState& par
m_paintOffset += toLayoutInline(container).offsetForInFlowPositionedInline(toLayoutBox(m_currentObject));
}

if (currentObject.isBox())
m_paintOffset += toLayoutBox(currentObject).locationOffset();
if (m_currentObject.isBox())
m_paintOffset += toLayoutBox(m_currentObject).locationOffset();

if (currentObject.isInFlowPositioned() && currentObject.hasLayer())
m_paintOffset += toLayoutBoxModelObject(currentObject).layer()->offsetForInFlowPosition();
if (m_currentObject.isInFlowPositioned() && m_currentObject.hasLayer())
m_paintOffset += toLayoutBoxModelObject(m_currentObject).layer()->offsetForInFlowPosition();
}

void PaintInvalidationState::updateForChildren()
Expand Down Expand Up @@ -399,7 +411,7 @@ static void slowMapToVisualRectInAncestorSpace(const LayoutObject& object, const
}
}

void PaintInvalidationState::mapLocalRectToPaintInvalidationBacking(LayoutRect& rect) const
void PaintInvalidationState::mapLocalRectToPaintInvalidationContainer(LayoutRect& rect) const
{
ASSERT(!m_didUpdateForChildren);

Expand All @@ -420,6 +432,11 @@ void PaintInvalidationState::mapLocalRectToPaintInvalidationBacking(LayoutRect&
} else {
slowMapToVisualRectInAncestorSpace(m_currentObject, *m_paintInvalidationContainer, rect);
}
}

void PaintInvalidationState::mapLocalRectToPaintInvalidationBacking(LayoutRect& rect) const
{
mapLocalRectToPaintInvalidationContainer(rect);

if (m_paintInvalidationContainer->layer()->groupedMapping())
PaintLayer::mapRectInPaintInvalidationContainerToBacking(*m_paintInvalidationContainer, rect);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ class CORE_EXPORT PaintInvalidationState {
private:
friend class VisualRectMappingTest;

void mapLocalRectToPaintInvalidationContainer(LayoutRect&) const;

void updateForCurrentObject(const PaintInvalidationState& parentState);
void updateForNormalChildren();

LayoutRect computePaintInvalidationRectInBackingForSVG() const;
Expand Down
79 changes: 77 additions & 2 deletions third_party/WebKit/Source/core/layout/VisualRectMappingTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "core/layout/LayoutTestHelper.h"
#include "core/layout/LayoutView.h"
#include "core/layout/PaintInvalidationState.h"
#include "core/paint/PaintLayer.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace blink {
Expand Down Expand Up @@ -35,10 +36,10 @@ class VisualRectMappingTest : public RenderingTest {

const PaintInvalidationState& paintInvalidationState = *paintInvalidationStates.last();
ASSERT_EQ(paintInvalidationState.m_currentObject, object);
ASSERT_EQ(paintInvalidationState.paintInvalidationContainer(), paintInvalidationContainer);
ASSERT_EQ(&paintInvalidationState.paintInvalidationContainer(), &paintInvalidationContainer);

LayoutRect r = rect;
paintInvalidationState.mapLocalRectToPaintInvalidationBacking(r);
paintInvalidationState.mapLocalRectToPaintInvalidationContainer(r);
EXPECT_EQ(expectedRect, r);
}
};
Expand Down Expand Up @@ -234,6 +235,7 @@ TEST_F(VisualRectMappingTest, ContainerOverflowClip)
EXPECT_EQ(LayoutUnit(0), container->scrollLeft());
container->setScrollTop(LayoutUnit(7));
container->setScrollLeft(LayoutUnit(8));
document().view()->updateAllLifecyclePhases();

LayoutBlock* target = toLayoutBlock(getLayoutObjectByElementId("target"));
LayoutRect targetOverflowRect = target->localOverflowRectForPaintInvalidation();
Expand Down Expand Up @@ -291,6 +293,7 @@ TEST_F(VisualRectMappingTest, ContainerFlippedWritingModeAndOverflowClip)
EXPECT_EQ(LayoutUnit(150), container->scrollLeft());
container->setScrollTop(LayoutUnit(7));
container->setScrollLeft(LayoutUnit(142)); // Scroll to the right by 8 pixels.
document().view()->updateAllLifecyclePhases();

LayoutBlock* target = toLayoutBlock(getLayoutObjectByElementId("target"));
LayoutRect targetOverflowRect = target->localOverflowRectForPaintInvalidation();
Expand Down Expand Up @@ -338,4 +341,76 @@ TEST_F(VisualRectMappingTest, ContainerFlippedWritingModeAndOverflowClip)
checkPaintInvalidationStateRectMapping(rect, containerOverflowRect, *container, layoutView(), layoutView());
}

TEST_F(VisualRectMappingTest, DifferentPaintInvalidaitionContainerForAbsolutePosition)
{
enableCompositing();
document().frame()->settings()->setPreferCompositingToLCDTextEnabled(true);

setBodyInnerHTML(
"<div id='stacking-context' style='opacity: 0.9; background: blue; will-change: transform'>"
" <div id='scroller' style='overflow: scroll; width: 80px; height: 80px'>"
" <div id='absolute' style='position: absolute; top: 111px; left: 222px; width: 50px; height: 50px; background: green'></div>"
" <div id='normal-flow' style='width: 2000px; height: 2000px; background: yellow'></div>"
" </div>"
"</div>");

LayoutBlock* scroller = toLayoutBlock(getLayoutObjectByElementId("scroller"));
scroller->setScrollTop(LayoutUnit(77));
scroller->setScrollLeft(LayoutUnit(88));
document().view()->updateAllLifecyclePhases();

LayoutBlock* normalFlow = toLayoutBlock(getLayoutObjectByElementId("normal-flow"));
EXPECT_EQ(scroller, &normalFlow->containerForPaintInvalidation());

LayoutRect normalFlowOverflowRect = normalFlow->localOverflowRectForPaintInvalidation();
EXPECT_EQ(LayoutRect(0, 0, 2000, 2000), normalFlowOverflowRect);
LayoutRect rect = normalFlowOverflowRect;
EXPECT_TRUE(normalFlow->mapToVisualRectInAncestorSpace(scroller, rect));
EXPECT_EQ(LayoutRect(-88, -77, 2000, 2000), rect);
checkPaintInvalidationStateRectMapping(rect, normalFlowOverflowRect, *normalFlow, layoutView(), *scroller);

LayoutBlock* stackingContext = toLayoutBlock(getLayoutObjectByElementId("stacking-context"));
LayoutBlock* absolute = toLayoutBlock(getLayoutObjectByElementId("absolute"));
EXPECT_EQ(stackingContext, &absolute->containerForPaintInvalidation());
EXPECT_EQ(stackingContext, absolute->container());

LayoutRect absoluteOverflowRect = absolute->localOverflowRectForPaintInvalidation();
EXPECT_EQ(LayoutRect(0, 0, 50, 50), absoluteOverflowRect);
rect = absoluteOverflowRect;
EXPECT_TRUE(absolute->mapToVisualRectInAncestorSpace(stackingContext, rect));
EXPECT_EQ(LayoutRect(222, 111, 50, 50), rect);
checkPaintInvalidationStateRectMapping(rect, absoluteOverflowRect, *absolute, layoutView(), *stackingContext);
}

TEST_F(VisualRectMappingTest, ContainerOfAbsoluteAbovePaintInvalidationContainer)
{
enableCompositing();
document().frame()->settings()->setPreferCompositingToLCDTextEnabled(true);

setBodyInnerHTML(
"<div id='container' style='position: absolute; top: 88px; left: 99px'>"
" <div style='height: 222px'></div>"
// This div makes stacking-context composited.
" <div style='position: absolute; width: 1px; height: 1px; background:yellow; will-change: transform'></div>"
// This stacking context is paintInvalidationContainer of the absolute child, but not a container of it.
" <div id='stacking-context' style='opacity: 0.9'>"
" <div id='absolute' style='position: absolute; top: 50px; left: 50px; width: 50px; height: 50px; background: green'></div>"
" </div>"
"</div>");

LayoutBlock* stackingContext = toLayoutBlock(getLayoutObjectByElementId("stacking-context"));
LayoutBlock* absolute = toLayoutBlock(getLayoutObjectByElementId("absolute"));
LayoutBlock* container = toLayoutBlock(getLayoutObjectByElementId("container"));
EXPECT_EQ(stackingContext, &absolute->containerForPaintInvalidation());
EXPECT_EQ(container, absolute->container());

LayoutRect absoluteOverflowRect = absolute->localOverflowRectForPaintInvalidation();
EXPECT_EQ(LayoutRect(0, 0, 50, 50), absoluteOverflowRect);
LayoutRect rect = absoluteOverflowRect;
EXPECT_TRUE(absolute->mapToVisualRectInAncestorSpace(stackingContext, rect));
// -172 = top(50) - y_offset_of_stacking_context(222)
EXPECT_EQ(LayoutRect(50, -172, 50, 50), rect);
checkPaintInvalidationStateRectMapping(rect, absoluteOverflowRect, *absolute, layoutView(), *stackingContext);
}

} // namespace blink

0 comments on commit 733202f

Please sign in to comment.