TEST_F(PaintPropertyTreeBuilderTest, PositionAndScroll) { loadTestData("position-and-scroll.html"); Element* scroller = document().getElementById("scroller"); scroller->scrollTo(0, 100); FrameView* frameView = document().view(); frameView->updateAllLifecyclePhases(); ObjectPaintProperties* scrollerProperties = scroller->layoutObject()->objectPaintProperties(); EXPECT_EQ(TransformationMatrix().translate(0, -100), scrollerProperties->scrollTranslation()->matrix()); EXPECT_EQ(frameView->scrollTranslation(), scrollerProperties->scrollTranslation()->parent()); // The relative-positioned element should have accumulated box offset (exclude scrolling), // and should be affected by ancestor scroll transforms. Element* relPos = document().getElementById("rel-pos"); ObjectPaintProperties* relPosProperties = relPos->layoutObject()->objectPaintProperties(); EXPECT_EQ(TransformationMatrix().translate(680, 1120), relPosProperties->paintOffsetTranslation()->matrix()); EXPECT_EQ(scrollerProperties->scrollTranslation(), relPosProperties->paintOffsetTranslation()->parent()); // The absolute-positioned element should not be affected by non-positioned scroller at all. Element* absPos = document().getElementById("abs-pos"); ObjectPaintProperties* absPosProperties = absPos->layoutObject()->objectPaintProperties(); EXPECT_EQ(TransformationMatrix().translate(123, 456), absPosProperties->paintOffsetTranslation()->matrix()); EXPECT_EQ(frameView->scrollTranslation(), absPosProperties->paintOffsetTranslation()->parent()); }
const TransformPaintPropertyNode* frameScrollTranslation() { FrameView* frameView = document().view(); if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) { return frameView->layoutView()->paintProperties()->scrollTranslation(); } return frameView->scrollTranslation(); }
TEST_F(PaintPropertyTreeBuilderTest, FrameScrollingTraditional) { setBodyInnerHTML("<style> body { height: 10000px; } </style>"); document().domWindow()->scrollTo(0, 100); FrameView* frameView = document().view(); frameView->updateAllLifecyclePhases(); EXPECT_EQ(TransformationMatrix(), frameView->preTranslation()->matrix()); EXPECT_EQ(nullptr, frameView->preTranslation()->parent()); EXPECT_EQ(TransformationMatrix().translate(0, -100), frameView->scrollTranslation()->matrix()); EXPECT_EQ(frameView->preTranslation(), frameView->scrollTranslation()->parent()); LayoutView* layoutView = document().layoutView(); ObjectPaintProperties* layoutViewProperties = layoutView->objectPaintProperties(); EXPECT_EQ(nullptr, layoutViewProperties); }
TEST_F(PaintPropertyTreeBuilderTest, BorderRadiusClip) { setBodyInnerHTML( "<style>" " body {" " margin: 0px;" " }" " #div {" " border-radius: 12px 34px 56px 78px;" " border-top: 45px solid;" " border-right: 50px solid;" " border-bottom: 55px solid;" " border-left: 60px solid;" " width: 500px;" " height: 400px;" " overflow: scroll;" " }" "</style>" "<div id='div'></div>"); FrameView* frameView = document().view(); LayoutObject& div = *document().getElementById("div")->layoutObject(); ObjectPaintProperties* divProperties = div.objectPaintProperties(); EXPECT_EQ(frameView->scrollTranslation(), divProperties->overflowClip()->localTransformSpace()); // The overflow clip rect includes only the padding box. // padding box = border box(500+60+50, 400+45+55) - border outset(60+50, 45+55) - scrollbars(15, 15) EXPECT_EQ(FloatRoundedRect(60, 45, 500, 400), divProperties->overflowClip()->clipRect()); const ClipPaintPropertyNode* borderRadiusClip = divProperties->overflowClip()->parent(); EXPECT_EQ(frameView->scrollTranslation(), borderRadiusClip->localTransformSpace()); // The border radius clip is the area enclosed by inner border edge, including the scrollbars. // As the border-radius is specified in outer radius, the inner radius is calculated by: // inner radius = max(outer radius - border width, 0) // In the case that two adjacent borders have different width, the inner radius of the corner // may transition from one value to the other. i.e. being an ellipse. EXPECT_EQ( FloatRoundedRect( FloatRect(60, 45, 500, 400), // = border box(610, 500) - border outset(110, 100) FloatSize(0, 0), // (top left) = max((12, 12) - (60, 45), (0, 0)) FloatSize(0, 0), // (top right) = max((34, 34) - (50, 45), (0, 0)) FloatSize(18, 23), // (bottom left) = max((78, 78) - (60, 55), (0, 0)) FloatSize(6, 1)), // (bottom right) = max((56, 56) - (50, 55), (0, 0)) borderRadiusClip->clipRect()); EXPECT_EQ(frameView->contentClip(), borderRadiusClip->parent()); }
void updateFrameViewScrollTranslation( FrameView& frameView, PassRefPtr<const TransformPaintPropertyNode> parent, const TransformationMatrix& matrix, const FloatPoint3D& origin) { DCHECK(!RuntimeEnabledFeatures::rootLayerScrollingEnabled()); if (auto* existingScrollTranslation = frameView.scrollTranslation()) { existingScrollTranslation->update(std::move(parent), matrix, origin); } else { frameView.setScrollTranslation( TransformPaintPropertyNode::create(std::move(parent), matrix, origin)); } }
TEST_F(PaintPropertyTreeBuilderTest, ControlClip) { setBodyInnerHTML( "<style>" " body {" " margin: 0;" " }" " input {" " border-width: 5px;" " padding: 0;" " }" "</style>" "<input id='button' type='button' style='width:345px; height:123px' value='some text'/>"); FrameView* frameView = document().view(); LayoutObject& button = *document().getElementById("button")->layoutObject(); ObjectPaintProperties* buttonProperties = button.objectPaintProperties(); EXPECT_EQ(frameView->scrollTranslation(), buttonProperties->overflowClip()->localTransformSpace()); EXPECT_EQ(FloatRoundedRect(5, 5, 335, 113), buttonProperties->overflowClip()->clipRect()); EXPECT_EQ(frameView->contentClip(), buttonProperties->overflowClip()->parent()); }
void PaintPropertyTreeBuilder::buildTreeNodes( FrameView& frameView, PaintPropertyTreeBuilderContext& context) { if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) { LayoutView* layoutView = frameView.layoutView(); if (!layoutView) return; TransformationMatrix frameTranslate; frameTranslate.translate(frameView.x() + layoutView->location().x() + context.current.paintOffset.x(), frameView.y() + layoutView->location().y() + context.current.paintOffset.y()); context.current.transform = layoutView->getMutableForPainting() .ensurePaintProperties() .updatePaintOffsetTranslation(context.current.transform, frameTranslate, FloatPoint3D()); context.current.paintOffset = LayoutPoint(); context.current.renderingContextID = 0; context.current.shouldFlattenInheritedTransform = true; context.absolutePosition = context.current; context.containerForAbsolutePosition = nullptr; // This will get set in updateOutOfFlowContext(). context.fixedPosition = context.current; return; } TransformationMatrix frameTranslate; frameTranslate.translate(frameView.x() + context.current.paintOffset.x(), frameView.y() + context.current.paintOffset.y()); context.current.transform = updateFrameViewPreTranslation( frameView, context.current.transform, frameTranslate, FloatPoint3D()); FloatRoundedRect contentClip( IntRect(IntPoint(), frameView.visibleContentSize())); context.current.clip = updateFrameViewContentClip( frameView, context.current.clip, frameView.preTranslation(), contentClip); // Record the fixed properties before any scrolling occurs. const auto* fixedTransformNode = context.current.transform; auto* fixedScrollNode = context.current.scroll; ScrollOffset scrollOffset = frameView.scrollOffset(); if (frameView.isScrollable() || !scrollOffset.isZero()) { TransformationMatrix frameScroll; frameScroll.translate(-scrollOffset.width(), -scrollOffset.height()); context.current.transform = updateFrameViewScrollTranslation( frameView, frameView.preTranslation(), frameScroll, FloatPoint3D()); IntSize scrollClip = frameView.visibleContentSize(); IntSize scrollBounds = frameView.contentsSize(); bool userScrollableHorizontal = frameView.userInputScrollable(HorizontalScrollbar); bool userScrollableVertical = frameView.userInputScrollable(VerticalScrollbar); context.current.scroll = updateFrameViewScroll( frameView, context.current.scroll, frameView.scrollTranslation(), scrollClip, scrollBounds, userScrollableHorizontal, userScrollableVertical); } else { // Ensure pre-existing properties are cleared when there is no scrolling. frameView.setScrollTranslation(nullptr); frameView.setScroll(nullptr); } // Initialize the context for current, absolute and fixed position cases. // They are the same, except that scroll translation does not apply to // fixed position descendants. context.current.paintOffset = LayoutPoint(); context.current.renderingContextID = 0; context.current.shouldFlattenInheritedTransform = true; context.absolutePosition = context.current; context.containerForAbsolutePosition = nullptr; context.fixedPosition = context.current; context.fixedPosition.transform = fixedTransformNode; context.fixedPosition.scroll = fixedScrollNode; std::unique_ptr<PropertyTreeState> contentsState( new PropertyTreeState(context.current.transform, context.current.clip, context.currentEffect, context.current.scroll)); frameView.setTotalPropertyTreeStateForContents(std::move(contentsState)); }
void PaintPropertyTreeBuilder::updateFramePropertiesAndContext( FrameView& frameView, PaintPropertyTreeBuilderContext& context) { if (RuntimeEnabledFeatures::rootLayerScrollingEnabled()) { // With root layer scrolling, the LayoutView (a LayoutObject) properties are // updated like other objects (see updatePropertiesAndContextForSelf and // updatePropertiesAndContextForChildren) instead of needing LayoutView- // specific property updates here. context.current.paintOffset.moveBy(frameView.location()); context.current.renderingContextID = 0; context.current.shouldFlattenInheritedTransform = true; context.absolutePosition = context.current; context.containerForAbsolutePosition = nullptr; context.fixedPosition = context.current; return; } TransformationMatrix frameTranslate; frameTranslate.translate(frameView.x() + context.current.paintOffset.x(), frameView.y() + context.current.paintOffset.y()); updateFrameViewPreTranslation(frameView, context.current.transform, frameTranslate, FloatPoint3D()); FloatRoundedRect contentClip( IntRect(IntPoint(), frameView.visibleContentSize())); updateFrameViewContentClip(frameView, context.current.clip, frameView.preTranslation(), contentClip); ScrollOffset scrollOffset = frameView.scrollOffset(); if (frameView.isScrollable() || !scrollOffset.isZero()) { TransformationMatrix frameScroll; frameScroll.translate(-scrollOffset.width(), -scrollOffset.height()); updateFrameViewScrollTranslation(frameView, frameView.preTranslation(), frameScroll, FloatPoint3D()); IntSize scrollClip = frameView.visibleContentSize(); IntSize scrollBounds = frameView.contentsSize(); bool userScrollableHorizontal = frameView.userInputScrollable(HorizontalScrollbar); bool userScrollableVertical = frameView.userInputScrollable(VerticalScrollbar); updateFrameViewScroll(frameView, context.current.scroll, frameView.scrollTranslation(), scrollClip, scrollBounds, userScrollableHorizontal, userScrollableVertical); } else { // Ensure pre-existing properties are cleared when there is no scrolling. frameView.setScrollTranslation(nullptr); frameView.setScroll(nullptr); } // Initialize the context for current, absolute and fixed position cases. // They are the same, except that scroll translation does not apply to // fixed position descendants. const auto* fixedTransformNode = frameView.preTranslation() ? frameView.preTranslation() : context.current.transform; auto* fixedScrollNode = context.current.scroll; DCHECK(frameView.preTranslation()); context.current.transform = frameView.preTranslation(); DCHECK(frameView.contentClip()); context.current.clip = frameView.contentClip(); if (const auto* scrollTranslation = frameView.scrollTranslation()) context.current.transform = scrollTranslation; if (auto* scroll = frameView.scroll()) context.current.scroll = scroll; context.current.paintOffset = LayoutPoint(); context.current.renderingContextID = 0; context.current.shouldFlattenInheritedTransform = true; context.absolutePosition = context.current; context.containerForAbsolutePosition = nullptr; context.fixedPosition = context.current; context.fixedPosition.transform = fixedTransformNode; context.fixedPosition.scroll = fixedScrollNode; std::unique_ptr<PropertyTreeState> contentsState( new PropertyTreeState(context.current.transform, context.current.clip, context.currentEffect, context.current.scroll)); frameView.setTotalPropertyTreeStateForContents(std::move(contentsState)); }