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());
}
Example #2
0
 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));
}