void InlineFlowBoxPainter::paintBoxDecorationBackground(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutRect& cullRect)
{
    ASSERT(paintInfo.phase == PaintPhaseForeground);
    if (m_inlineFlowBox.lineLayoutItem().style()->visibility() != VISIBLE)
        return;

    // You can use p::first-line to specify a background. If so, the root line boxes for
    // a line may actually have to paint a background.
    LayoutObject* inlineFlowBoxLayoutObject = LineLayoutAPIShim::layoutObjectFrom(m_inlineFlowBox.lineLayoutItem());
    const ComputedStyle* styleToUse = m_inlineFlowBox.lineLayoutItem().style(m_inlineFlowBox.isFirstLineStyle());
    bool shouldPaintBoxDecorationBackground;
    if (m_inlineFlowBox.parent())
        shouldPaintBoxDecorationBackground = inlineFlowBoxLayoutObject->hasBoxDecorationBackground();
    else
        shouldPaintBoxDecorationBackground = m_inlineFlowBox.isFirstLineStyle() && styleToUse != m_inlineFlowBox.lineLayoutItem().style();

    if (!shouldPaintBoxDecorationBackground)
        return;

    if (DrawingRecorder::useCachedDrawingIfPossible(paintInfo.context, m_inlineFlowBox, DisplayItem::BoxDecorationBackground))
        return;

    DrawingRecorder recorder(paintInfo.context, m_inlineFlowBox, DisplayItem::BoxDecorationBackground, pixelSnappedIntRect(cullRect));

    LayoutRect frameRect = frameRectClampedToLineTopAndBottomIfNeeded();

    // Move x/y to our coordinates.
    LayoutRect localRect(frameRect);
    m_inlineFlowBox.flipForWritingMode(localRect);
    LayoutPoint adjustedPaintOffset = paintOffset + localRect.location();

    LayoutRect adjustedFrameRect = LayoutRect(adjustedPaintOffset, frameRect.size());

    IntRect adjustedClipRect;
    BorderPaintingType borderPaintingType = getBorderPaintType(adjustedFrameRect, adjustedClipRect);

    // Shadow comes first and is behind the background and border.
    if (!m_inlineFlowBox.boxModelObject().boxShadowShouldBeAppliedToBackground(BackgroundBleedNone, &m_inlineFlowBox))
        paintBoxShadow(paintInfo, *styleToUse, Normal, adjustedFrameRect);

    Color backgroundColor = inlineFlowBoxLayoutObject->resolveColor(*styleToUse, CSSPropertyBackgroundColor);
    paintFillLayers(paintInfo, backgroundColor, styleToUse->backgroundLayers(), adjustedFrameRect);
    paintBoxShadow(paintInfo, *styleToUse, Inset, adjustedFrameRect);

    switch (borderPaintingType) {
    case DontPaintBorders:
        break;
    case PaintBordersWithoutClip:
        BoxPainter::paintBorder(*toLayoutBoxModelObject(LineLayoutAPIShim::layoutObjectFrom(m_inlineFlowBox.boxModelObject())), paintInfo, adjustedFrameRect, m_inlineFlowBox.lineLayoutItem().styleRef(m_inlineFlowBox.isFirstLineStyle()), BackgroundBleedNone, m_inlineFlowBox.includeLogicalLeftEdge(), m_inlineFlowBox.includeLogicalRightEdge());
        break;
    case PaintBordersWithClip:
        // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right,
        // but it isn't even clear how this should work at all.
        LayoutRect imageStripPaintRect = paintRectForImageStrip(adjustedPaintOffset, frameRect.size(), LTR);
        GraphicsContextStateSaver stateSaver(paintInfo.context);
        paintInfo.context.clip(adjustedClipRect);
        BoxPainter::paintBorder(*toLayoutBoxModelObject(LineLayoutAPIShim::layoutObjectFrom(m_inlineFlowBox.boxModelObject())), paintInfo, imageStripPaintRect, m_inlineFlowBox.lineLayoutItem().styleRef(m_inlineFlowBox.isFirstLineStyle()));
        break;
    }
}
TEST_F(PaintLayerScrollableAreaTest, OpaqueLayersPromotedOnStyleChange) {
  RuntimeEnabledFeatures::setCompositeOpaqueScrollersEnabled(true);

  setBodyInnerHTML(
      "<style>"
      "#scroller { overflow: scroll; height: 200px; width: 200px; background: "
      "rgba(255,255,255,0.5) local content-box; }"
      "#scrolled { height: 300px; }"
      "</style>"
      "<div id=\"scroller\"><div id=\"scrolled\"></div></div>");
  document().view()->updateAllLifecyclePhases();

  EXPECT_TRUE(RuntimeEnabledFeatures::compositeOpaqueScrollersEnabled());
  Element* scroller = document().getElementById("scroller");
  PaintLayer* paintLayer =
      toLayoutBoxModelObject(scroller->layoutObject())->layer();
  ASSERT_TRUE(paintLayer);
  EXPECT_FALSE(paintLayer->needsCompositedScrolling());

  // Change the background to transparent
  scroller->setAttribute(HTMLNames::styleAttr,
                         "background: white local content-box;");
  document().view()->updateAllLifecyclePhases();
  paintLayer = toLayoutBoxModelObject(scroller->layoutObject())->layer();
  ASSERT_TRUE(paintLayer);
  EXPECT_TRUE(paintLayer->needsCompositedScrolling());
  EXPECT_TRUE(paintLayer->graphicsLayerBacking());
  ASSERT_TRUE(paintLayer->graphicsLayerBackingForScrolling());
  EXPECT_TRUE(paintLayer->graphicsLayerBackingForScrolling()->contentsOpaque());
}
PaintLayer& PaintInvalidationState::enclosingSelfPaintingLayer(const LayoutObject& layoutObject) const
{
    if (layoutObject.hasLayer() && toLayoutBoxModelObject(layoutObject).hasSelfPaintingLayer())
        return *toLayoutBoxModelObject(layoutObject).layer();

    return m_enclosingSelfPaintingLayer;
}
Beispiel #4
0
TEST_P(BoxPaintInvalidatorTest,
       CompositedBackgroundAttachmentLocalGradientResize) {
  enableCompositing();

  Element* target = document().getElementById("target");
  target->setAttribute(HTMLNames::classAttr,
                       "border local-background gradient");
  target->setAttribute(HTMLNames::styleAttr, "will-change: transform");
  target->setInnerHTML(
      "<div id='child' style='width: 500px; height: 500px'></div>",
      ASSERT_NO_EXCEPTION);
  Element* child = document().getElementById("child");
  document().view()->updateAllLifecyclePhases();

  // Resize the content.
  document().view()->setTracksPaintInvalidations(true);
  child->setAttribute(HTMLNames::styleAttr, "width: 500px; height: 1000px");
  document().view()->updateAllLifecyclePhases();
  GraphicsLayer* containerLayer = toLayoutBoxModelObject(target->layoutObject())
                                      ->layer()
                                      ->graphicsLayerBacking();
  GraphicsLayer* contentsLayer = toLayoutBoxModelObject(target->layoutObject())
                                     ->layer()
                                     ->graphicsLayerBackingForScrolling();
  // No invalidation on the container layer.
  EXPECT_FALSE(containerLayer->getRasterInvalidationTracking());
  // Full invalidation of background on contents layer because the gradient
  // background is resized.
  const auto& contentsRasterInvalidations =
      contentsLayer->getRasterInvalidationTracking()
          ->trackedRasterInvalidations;
  ASSERT_EQ(1u, contentsRasterInvalidations.size());
  EXPECT_EQ(IntRect(0, 0, 500, 1000), contentsRasterInvalidations[0].rect);
  EXPECT_EQ(static_cast<const DisplayItemClient*>(target->layoutObject()),
            contentsRasterInvalidations[0].client);
  EXPECT_EQ(PaintInvalidationBackgroundOnScrollingContentsLayer,
            contentsRasterInvalidations[0].reason);
  document().view()->setTracksPaintInvalidations(false);

  // Resize the container.
  document().view()->setTracksPaintInvalidations(true);
  target->setAttribute(HTMLNames::styleAttr,
                       "will-change: transform; height: 200px");
  document().view()->updateAllLifecyclePhases();
  EXPECT_FALSE(contentsLayer->getRasterInvalidationTracking());
  // Full invalidation on the container layer.
  const auto& containerRasterInvalidations =
      containerLayer->getRasterInvalidationTracking()
          ->trackedRasterInvalidations;
  ASSERT_EQ(1u, containerRasterInvalidations.size());
  EXPECT_EQ(IntRect(0, 0, 70, 240), containerRasterInvalidations[0].rect);
  EXPECT_EQ(static_cast<const DisplayItemClient*>(target->layoutObject()),
            containerRasterInvalidations[0].client);
  EXPECT_EQ(PaintInvalidationBorderBoxChange,
            containerRasterInvalidations[0].reason);
  document().view()->setTracksPaintInvalidations(false);
}
// Test that opacity applied to the scroller or an ancestor will cause the
// scrolling contents layer to not be promoted.
TEST_F(PaintLayerScrollableAreaTest, OnlyOpaqueLayersPromoted) {
  ScopedCompositeOpaqueScrollersForTest compositeOpaqueScrollers(true);

  setBodyInnerHTML(
      "<style>"
      "#scroller { overflow: scroll; height: 200px; width: 200px; background: "
      "white local content-box; }"
      "#scrolled { height: 300px; }"
      "</style>"
      "<div id=\"parent\">"
      "  <div id=\"scroller\"><div id=\"scrolled\"></div></div>"
      "</div>");
  document().view()->updateAllLifecyclePhases();

  EXPECT_TRUE(RuntimeEnabledFeatures::compositeOpaqueScrollersEnabled());
  Element* parent = document().getElementById("parent");
  Element* scroller = document().getElementById("scroller");
  PaintLayer* paintLayer =
      toLayoutBoxModelObject(scroller->layoutObject())->layer();
  ASSERT_TRUE(paintLayer);
  EXPECT_TRUE(paintLayer->needsCompositedScrolling());
  EXPECT_TRUE(paintLayer->graphicsLayerBacking());
  ASSERT_TRUE(paintLayer->graphicsLayerBackingForScrolling());
  EXPECT_TRUE(paintLayer->graphicsLayerBackingForScrolling()->contentsOpaque());

  // Change the parent to be partially translucent.
  parent->setAttribute(HTMLNames::styleAttr, "opacity: 0.5;");
  document().view()->updateAllLifecyclePhases();
  paintLayer = toLayoutBoxModelObject(scroller->layoutObject())->layer();
  ASSERT_TRUE(paintLayer);
  EXPECT_FALSE(paintLayer->needsCompositedScrolling());
  EXPECT_FALSE(paintLayer->graphicsLayerBacking());

  // Change the parent to be opaque again.
  parent->setAttribute(HTMLNames::styleAttr, "opacity: 1;");
  document().view()->updateAllLifecyclePhases();
  paintLayer = toLayoutBoxModelObject(scroller->layoutObject())->layer();
  ASSERT_TRUE(paintLayer);
  EXPECT_TRUE(paintLayer->needsCompositedScrolling());
  EXPECT_TRUE(paintLayer->graphicsLayerBacking());
  ASSERT_TRUE(paintLayer->graphicsLayerBackingForScrolling());
  EXPECT_TRUE(paintLayer->graphicsLayerBackingForScrolling()->contentsOpaque());

  // Make the scroller translucent.
  scroller->setAttribute(HTMLNames::styleAttr, "opacity: 0.5");
  document().view()->updateAllLifecyclePhases();
  paintLayer = toLayoutBoxModelObject(scroller->layoutObject())->layer();
  ASSERT_TRUE(paintLayer);
  EXPECT_FALSE(paintLayer->needsCompositedScrolling());
  EXPECT_FALSE(paintLayer->graphicsLayerBacking());
}
static void deriveBorderBoxFromContainerContext(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.isBoxModelObject())
        return;

    const LayoutBoxModelObject& boxModelObject = toLayoutBoxModelObject(object);

    // TODO(trchen): There is some insanity going on with tables. Double check results.
    switch (object.styleRef().position()) {
    case StaticPosition:
        break;
    case RelativePosition:
        context.paintOffset += boxModelObject.offsetForInFlowPosition();
        break;
    case AbsolutePosition:
        context.currentTransform = context.transformForOutOfFlowPositioned;
        context.paintOffset = context.paintOffsetForOutOfFlowPositioned;
        context.currentClip = context.clipForOutOfFlowPositioned;
        break;
    case StickyPosition:
        context.paintOffset += boxModelObject.offsetForInFlowPosition();
        break;
    case FixedPosition:
        context.currentTransform = context.transformForFixedPositioned;
        context.paintOffset = context.paintOffsetForFixedPositioned;
        context.currentClip = context.clipForFixedPositioned;
        break;
    default:
        ASSERT_NOT_REACHED();
    }
    if (boxModelObject.isBox())
        context.paintOffset += toLayoutBox(boxModelObject).locationOffset();
}
Beispiel #7
0
static LayoutBoxModelObject* enclosingBoxModelObject(LayoutObject* object) {
  while (object && !object->isBoxModelObject())
    object = object->parent();
  if (!object)
    return nullptr;
  return toLayoutBoxModelObject(object);
}
void PaintPropertyTreeBuilder::updatePaintOffsetTranslation(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (object.isBoxModelObject()) {
        // TODO(trchen): Eliminate PaintLayer dependency.
        PaintLayer* layer = toLayoutBoxModelObject(object).layer();
        if (!layer || !layer->paintsWithTransform(GlobalPaintNormalPhase))
            return;
    }

    if (context.paintOffset == LayoutPoint())
        return;

    // We should use the same subpixel paint offset values for snapping regardless of whether a
    // transform is present. If there is a transform we round the paint offset but keep around
    // the residual fractional component for the transformed content to paint with.
    // In spv1 this was called "subpixel accumulation". For more information, see
    // PaintLayer::subpixelAccumulation() and PaintLayerPainter::paintFragmentByApplyingTransform.
    IntPoint roundedPaintOffset = roundedIntPoint(context.paintOffset);
    LayoutPoint fractionalPaintOffset = LayoutPoint(context.paintOffset - roundedPaintOffset);

    RefPtr<TransformPaintPropertyNode> paintOffsetTranslation = TransformPaintPropertyNode::create(
        TransformationMatrix().translate(roundedPaintOffset.x(), roundedPaintOffset.y()),
        FloatPoint3D(), context.currentTransform);
    context.currentTransform = paintOffsetTranslation.get();
    context.paintOffset = fractionalPaintOffset;
    object.getMutableForPainting().ensureObjectPaintProperties().setPaintOffsetTranslation(paintOffsetTranslation.release());
}
bool CompositorAnimations::startAnimationOnCompositor(const Element& element, int group, double startTime, double timeOffset, const Timing& timing, const AnimationPlayer& player, const AnimationEffect& effect, Vector<int>& startedAnimationIds, double playerPlaybackRate)
{
    ASSERT(startedAnimationIds.isEmpty());
    ASSERT(isCandidateForAnimationOnCompositor(timing, element, &player, effect, playerPlaybackRate));
    ASSERT(canStartAnimationOnCompositor(element));

    const KeyframeEffectModelBase& keyframeEffect = toKeyframeEffectModelBase(effect);

    DeprecatedPaintLayer* layer = toLayoutBoxModelObject(element.layoutObject())->layer();
    ASSERT(layer);

    Vector<OwnPtr<WebCompositorAnimation>> animations;
    CompositorAnimationsImpl::getAnimationOnCompositor(timing, group, startTime, timeOffset, keyframeEffect, animations, playerPlaybackRate);
    ASSERT(!animations.isEmpty());
    for (auto& animation : animations) {
        int id = animation->id();
        if (RuntimeEnabledFeatures::compositorAnimationTimelinesEnabled()) {
            WebCompositorAnimationPlayer* compositorPlayer = player.compositorPlayer();
            ASSERT(compositorPlayer);
            compositorPlayer->addAnimation(animation.leakPtr());
        } else if (!layer->compositedDeprecatedPaintLayerMapping()->mainGraphicsLayer()->addAnimation(animation.release())) {
            // FIXME: We should know ahead of time whether these animations can be started.
            for (int startedAnimationId : startedAnimationIds)
                cancelAnimationOnCompositor(element, player, startedAnimationId);
            startedAnimationIds.clear();
            return false;
        }
        startedAnimationIds.append(id);
    }
    ASSERT(!startedAnimationIds.isEmpty());
    return true;
}
TEST_F(PaintControllerPaintTestForSlimmingPaintV2, CompositingFold) {
  setBodyInnerHTML(
      "<div id='div' style='width: 200px; height: 200px; opacity: 0.5'>"
      "  <div style='width: 100px; height: 100px; background-color: "
      "blue'></div>"
      "</div>");
  PaintLayer& htmlLayer =
      *toLayoutBoxModelObject(document().documentElement()->layoutObject())
           ->layer();
  LayoutBlock& div = *toLayoutBlock(getLayoutObjectByElementId("div"));
  LayoutObject& subDiv = *div.firstChild();

  EXPECT_DISPLAY_LIST(
      rootPaintController().getDisplayItemList(), 8,
      TestDisplayItem(layoutView(),
                      DisplayItem::kClipFrameToVisibleContentRect),
      TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
      TestDisplayItem(layoutView(), documentBackgroundType),
      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
      // The begin and end compositing display items have been folded into this
      // one.
      TestDisplayItem(subDiv, backgroundType),
      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
      TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence),
      TestDisplayItem(layoutView(),
                      DisplayItem::clipTypeToEndClipType(
                          DisplayItem::kClipFrameToVisibleContentRect)));
}
// static
bool InspectorHighlight::getBoxModel(Node* node, RefPtr<TypeBuilder::DOM::BoxModel>& model)
{
    LayoutObject* layoutObject = node->layoutObject();
    FrameView* view = node->document().view();
    if (!layoutObject || !view)
        return false;

    FloatQuad content, padding, border, margin;
    if (!buildNodeQuads(node, &content, &padding, &border, &margin))
        return false;

    IntRect boundingBox = view->contentsToRootFrame(layoutObject->absoluteBoundingBoxRect());
    LayoutBoxModelObject* modelObject = layoutObject->isBoxModelObject() ? toLayoutBoxModelObject(layoutObject) : nullptr;

    model = TypeBuilder::DOM::BoxModel::create()
        .setContent(buildArrayForQuad(content))
        .setPadding(buildArrayForQuad(padding))
        .setBorder(buildArrayForQuad(border))
        .setMargin(buildArrayForQuad(margin))
        .setWidth(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetWidth(), modelObject) : boundingBox.width())
        .setHeight(modelObject ? adjustForAbsoluteZoom(modelObject->pixelSnappedOffsetHeight(), modelObject) : boundingBox.height());

    Shape::DisplayPaths paths;
    FloatQuad boundsQuad;
    if (const ShapeOutsideInfo* shapeOutsideInfo = shapeOutsideInfoForNode(node, &paths, &boundsQuad)) {
        RefPtr<TypeBuilder::DOM::ShapeOutsideInfo> shapeTypeBuilder = TypeBuilder::DOM::ShapeOutsideInfo::create()
            .setBounds(buildArrayForQuad(boundsQuad))
            .setShape(ShapePathBuilder::buildPath(*view, *layoutObject, *shapeOutsideInfo, paths.shape))
            .setMarginShape(ShapePathBuilder::buildPath(*view, *layoutObject, *shapeOutsideInfo, paths.marginShape));
        model->setShapeOutside(shapeTypeBuilder);
    }

    return true;
}
Beispiel #12
0
TEST_F(PaintLayerClipperTest, NestedContainPaintClip) {
  setBodyInnerHTML(
      "<div style='contain: paint; width: 200px; height: 200px; overflow: "
      "auto'>"
      "  <div id='target' style='contain: paint; height: 400px'>"
      "  </div>"
      "</div>");

  LayoutRect infiniteRect(LayoutRect::infiniteIntRect());
  PaintLayer* layer =
      toLayoutBoxModelObject(getLayoutObjectByElementId("target"))->layer();
  ClipRectsContext context(layer->parent(),
                           PaintingClipRectsIgnoringOverflowClip);
  LayoutRect layerBounds;
  ClipRect backgroundRect, foregroundRect;
  layer->clipper().calculateRects(context, infiniteRect, layerBounds,
                                  backgroundRect, foregroundRect);
  EXPECT_EQ(LayoutRect(0, 0, 200, 400), backgroundRect.rect());
  EXPECT_EQ(LayoutRect(0, 0, 200, 400), foregroundRect.rect());
  EXPECT_EQ(LayoutRect(0, 0, 200, 400), layerBounds);

  ClipRectsContext contextClip(layer->parent(), PaintingClipRects);
  layer->clipper().calculateRects(contextClip, infiniteRect, layerBounds,
                                  backgroundRect, foregroundRect);
  EXPECT_EQ(LayoutRect(0, 0, 200, 200), backgroundRect.rect());
  EXPECT_EQ(LayoutRect(0, 0, 200, 200), foregroundRect.rect());
  EXPECT_EQ(LayoutRect(0, 0, 200, 400), layerBounds);
}
static void addPDFURLRectsForInlineChildrenRecursively(const LayoutObject& layoutObject, const PaintInfo& paintInfo, const LayoutPoint& paintOffset)
{
    for (LayoutObject* child = layoutObject.slowFirstChild(); child; child = child->nextSibling()) {
        if (!child->isLayoutInline() || toLayoutBoxModelObject(child)->hasSelfPaintingLayer())
            continue;
        ObjectPainter(*child).addPDFURLRectIfNeeded(paintInfo, paintOffset);
        addPDFURLRectsForInlineChildrenRecursively(*child, paintInfo, paintOffset);
    }
}
void IntersectionObservation::initializeGeometry(IntersectionGeometry& geometry)
{
    ASSERT(m_target);
    LayoutObject* targetLayoutObject = target()->layoutObject();
    if (targetLayoutObject->isBoxModelObject())
        geometry.targetRect = toLayoutBoxModelObject(targetLayoutObject)->visualOverflowRect();
    else
        geometry.targetRect = toLayoutText(targetLayoutObject)->visualOverflowRect();
    geometry.intersectionRect = geometry.targetRect;
}
void PaintPropertyTreeBuilder::updatePaintOffsetTranslation(
    const LayoutObject& object,
    PaintPropertyTreeBuilderContext& context) {
  bool usesPaintOffsetTranslation = false;
  if (RuntimeEnabledFeatures::rootLayerScrollingEnabled() &&
      object.isLayoutView()) {
    // Root layer scrolling always creates a translation node for LayoutView to
    // ensure fixed and absolute contexts use the correct transform space.
    usesPaintOffsetTranslation = true;
  } else if (object.isBoxModelObject() &&
             context.current.paintOffset != LayoutPoint()) {
    // TODO(trchen): Eliminate PaintLayer dependency.
    PaintLayer* layer = toLayoutBoxModelObject(object).layer();
    if (layer && layer->paintsWithTransform(GlobalPaintNormalPhase))
      usesPaintOffsetTranslation = true;
  }

  // We should use the same subpixel paint offset values for snapping
  // regardless of whether a transform is present. If there is a transform
  // we round the paint offset but keep around the residual fractional
  // component for the transformed content to paint with.  In spv1 this was
  // called "subpixel accumulation". For more information, see
  // PaintLayer::subpixelAccumulation() and
  // PaintLayerPainter::paintFragmentByApplyingTransform.
  IntPoint roundedPaintOffset = roundedIntPoint(context.current.paintOffset);
  LayoutPoint fractionalPaintOffset =
      LayoutPoint(context.current.paintOffset - roundedPaintOffset);

  if (usesPaintOffsetTranslation) {
    object.getMutableForPainting()
        .ensurePaintProperties()
        .updatePaintOffsetTranslation(
            context.current.transform,
            TransformationMatrix().translate(roundedPaintOffset.x(),
                                             roundedPaintOffset.y()),
            FloatPoint3D(), context.current.shouldFlattenInheritedTransform,
            context.current.renderingContextID);
  } else {
    if (auto* properties = object.getMutableForPainting().paintProperties())
      properties->clearPaintOffsetTranslation();
  }

  const auto* properties = object.paintProperties();
  if (properties && properties->paintOffsetTranslation()) {
    context.current.transform = properties->paintOffsetTranslation();
    context.current.paintOffset = fractionalPaintOffset;
    if (RuntimeEnabledFeatures::rootLayerScrollingEnabled() &&
        object.isLayoutView()) {
      context.absolutePosition.transform = properties->paintOffsetTranslation();
      context.fixedPosition.transform = properties->paintOffsetTranslation();
      context.absolutePosition.paintOffset = LayoutPoint();
      context.fixedPosition.paintOffset = LayoutPoint();
    }
  }
}
Beispiel #16
0
TEST_F(CompositedLayerMappingTest, RotatedInterestRect)
{
    setBodyInnerHTML(
        "<div id='target' style='width: 200px; height: 200px; will-change: transform; transform: rotateZ(45deg)'></div>");

    document().view()->updateAllLifecyclePhases();
    Element* element = document().getElementById("target");
    PaintLayer* paintLayer = toLayoutBoxModelObject(element->layoutObject())->layer();
    ASSERT_TRUE(!!paintLayer->graphicsLayerBacking());
    EXPECT_RECT_EQ(IntRect(0, 0, 200, 200), recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
static void deriveBorderBoxFromContainerContext(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.isBoxModelObject())
        return;

    const LayoutBoxModelObject& boxModelObject = toLayoutBoxModelObject(object);

    switch (object.styleRef().position()) {
    case StaticPosition:
        break;
    case RelativePosition:
        context.paintOffset += boxModelObject.offsetForInFlowPosition();
        break;
    case AbsolutePosition: {
        context.currentTransform = context.transformForAbsolutePosition;
        context.paintOffset = context.paintOffsetForAbsolutePosition;

        // Absolutely positioned content in an inline should be positioned relative to the inline.
        const LayoutObject* container = context.containerForAbsolutePosition;
        if (container && container->isInFlowPositioned() && container->isLayoutInline()) {
            DCHECK(object.isBox());
            context.paintOffset += toLayoutInline(container)->offsetForInFlowPositionedInline(toLayoutBox(object));
        }

        context.currentClip = context.clipForAbsolutePosition;
        break;
    }
    case StickyPosition:
        context.paintOffset += boxModelObject.offsetForInFlowPosition();
        break;
    case FixedPosition:
        context.currentTransform = context.transformForFixedPosition;
        context.paintOffset = context.paintOffsetForFixedPosition;
        context.currentClip = context.clipForFixedPosition;
        break;
    default:
        ASSERT_NOT_REACHED();
    }
    if (boxModelObject.isBox() && (!boxModelObject.isSVG() || boxModelObject.isSVGRoot())) {
        // TODO(pdr): Several calls in this function walk back up the tree to calculate containers
        // (e.g., topLeftLocation, offsetForInFlowPosition*). The containing block and other
        // containers can be stored on PaintPropertyTreeBuilderContext instead of recomputing them.
        context.paintOffset.moveBy(toLayoutBox(boxModelObject).topLeftLocation());
        // This is a weird quirk that table cells paint as children of table rows,
        // but their location have the row's location baked-in.
        // Similar adjustment is done in LayoutTableCell::offsetFromContainer().
        if (boxModelObject.isTableCell()) {
            LayoutObject* parentRow = boxModelObject.parent();
            ASSERT(parentRow && parentRow->isTableRow());
            context.paintOffset.moveBy(-toLayoutBox(parentRow)->topLeftLocation());
        }
    }
}
Beispiel #18
0
TEST_F(CompositedLayerMappingTest, TallLayerInterestRect)
{
    setBodyInnerHTML("<div id='target' style='width: 200px; height: 10000px; will-change: transform'></div>");

    document().view()->updateAllLifecyclePhases();
    Element* element = document().getElementById("target");
    PaintLayer* paintLayer = toLayoutBoxModelObject(element->layoutObject())->layer();
    ASSERT_TRUE(paintLayer->graphicsLayerBacking());
    // Screen-space visible content rect is [8, 8, 200, 600]. Mapping back to local, adding 4000px in all directions, then
    // clipping, yields this rect.
    EXPECT_RECT_EQ(IntRect(0, 0, 200, 4592), recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
void PaintLayerStackingNode::rebuildZOrderLists() {
#if DCHECK_IS_ON()
  DCHECK(m_layerListMutationAllowed);
#endif
  DCHECK(isDirtyStackingContext());

  for (PaintLayer* child = layer()->firstChild(); child;
       child = child->nextSibling())
    child->stackingNode()->collectLayers(m_posZOrderList, m_negZOrderList);

  // Sort the two lists.
  if (m_posZOrderList)
    std::stable_sort(m_posZOrderList->begin(), m_posZOrderList->end(),
                     compareZIndex);

  if (m_negZOrderList)
    std::stable_sort(m_negZOrderList->begin(), m_negZOrderList->end(),
                     compareZIndex);

  // Append layers for top layer elements after normal layer collection, to
  // ensure they are on top regardless of z-indexes.  The layoutObjects of top
  // layer elements are children of the view, sorted in top layer stacking
  // order.
  if (layer()->isRootLayer()) {
    LayoutBlockFlow* rootBlock = layoutObject()->view();
    // If the viewport is paginated, everything (including "top-layer" elements)
    // gets redirected to the flow thread. So that's where we have to look, in
    // that case.
    if (LayoutBlockFlow* multiColumnFlowThread =
            rootBlock->multiColumnFlowThread())
      rootBlock = multiColumnFlowThread;
    for (LayoutObject* child = rootBlock->firstChild(); child;
         child = child->nextSibling()) {
      Element* childElement = (child->node() && child->node()->isElementNode())
                                  ? toElement(child->node())
                                  : 0;
      if (childElement && childElement->isInTopLayer()) {
        PaintLayer* layer = toLayoutBoxModelObject(child)->layer();
        // Create the buffer if it doesn't exist yet.
        if (!m_posZOrderList)
          m_posZOrderList = wrapUnique(new Vector<PaintLayerStackingNode*>);
        m_posZOrderList->append(layer->stackingNode());
      }
    }
  }

#if ENABLE(ASSERT)
  updateStackingParentForZOrderLists(this);
#endif

  m_zOrderListsDirty = false;
}
static CompositedLayerMapping* mappingFromElement(Element* element) {
  if (!element)
    return nullptr;
  LayoutObject* layoutObject = element->layoutObject();
  if (!layoutObject || !layoutObject->isBoxModelObject())
    return nullptr;
  PaintLayer* layer = toLayoutBoxModelObject(layoutObject)->layer();
  if (!layer)
    return nullptr;
  if (!layer->hasCompositedLayerMapping())
    return nullptr;
  return layer->compositedLayerMapping();
}
Beispiel #21
0
TEST_F(PaintLayerTest, PaintingExtentReflectionWithTransform) {
  setBodyInnerHTML(
      "<div id='target' style='background-color: blue; position: absolute;"
      "    width: 110px; height: 120px; top: 40px; left: 60px;"
      "    -webkit-box-reflect: below 3px; transform: translateX(30px)'>"
      "</div>");

  PaintLayer* layer =
      toLayoutBoxModelObject(getLayoutObjectByElementId("target"))->layer();
  EXPECT_EQ(
      LayoutRect(90, 40, 110, 243),
      layer->paintingExtent(document().layoutView()->layer(), LayoutSize(), 0));
}
void CompositorAnimations::attachCompositedLayers(const Element& element, const AnimationPlayer& player)
{
    ASSERT(element.layoutObject());

    DeprecatedPaintLayer* layer = toLayoutBoxModelObject(element.layoutObject())->layer();
    ASSERT(layer);

    WebCompositorAnimationPlayer* compositorPlayer = player.compositorPlayer();
    ASSERT(compositorPlayer);

    ASSERT(layer->compositedDeprecatedPaintLayerMapping());
    compositorPlayer->attachLayer(layer->compositedDeprecatedPaintLayerMapping()->mainGraphicsLayer()->platformLayer());
}
Beispiel #23
0
TEST_F(PaintLayerClipperTest, LocalClipRectFixedUnderTransform) {
  setBodyInnerHTML(
      "<div id='transformed'"
      "    style='will-change: transform; width: 100px; height: 100px;"
      "    overflow: hidden'>"
      "  <div id='fixed' "
      "      style='position: fixed; width: 100px; height: 100px;"
      "      top: -50px'>"
      "   </div>"
      "</div>");

  LayoutRect infiniteRect(LayoutRect::infiniteIntRect());
  PaintLayer* transformed =
      toLayoutBoxModelObject(getLayoutObjectByElementId("transformed"))
          ->layer();
  PaintLayer* fixed =
      toLayoutBoxModelObject(getLayoutObjectByElementId("fixed"))->layer();

  EXPECT_EQ(LayoutRect(0, 0, 100, 100),
            transformed->clipper().localClipRect(transformed));
  EXPECT_EQ(LayoutRect(0, 50, 100, 100),
            fixed->clipper().localClipRect(transformed));
}
Beispiel #24
0
TEST_F(CompositedLayerMappingTest, TallLayerWholeDocumentInterestRect)
{
    setBodyInnerHTML("<div id='target' style='width: 200px; height: 10000px; will-change: transform'></div>");

    document().settings()->setMainFrameClipsContent(false);

    document().view()->updateAllLifecyclePhases();
    Element* element = document().getElementById("target");
    PaintLayer* paintLayer = toLayoutBoxModelObject(element->layoutObject())->layer();
    ASSERT_TRUE(paintLayer->graphicsLayerBacking());
    ASSERT_TRUE(paintLayer->compositedLayerMapping());
    // recomputeInterestRect computes the interest rect; computeInterestRect applies the extra setting to paint everything.
    EXPECT_RECT_EQ(IntRect(0, 0, 200, 4592), recomputeInterestRect(paintLayer->graphicsLayerBacking()));
    EXPECT_RECT_EQ(IntRect(0, 0, 200, 10000), computeInterestRect(paintLayer->compositedLayerMapping(), paintLayer->graphicsLayerBacking(), IntRect()));
}
Beispiel #25
0
void FrameCaret::invalidateCaretRect(bool forceInvalidation) {
  if (!m_caretRectDirty)
    return;
  m_caretRectDirty = false;

  DCHECK(caretPositionIsValidForDocument(*m_frame->document()));
  LayoutObject* layoutObject = nullptr;
  LayoutRect newRect;
  PositionWithAffinity currentCaretPosition = caretPosition();
  if (isActive())
    newRect = localCaretRectOfPosition(currentCaretPosition, layoutObject);
  Node* newNode = layoutObject ? layoutObject->node() : nullptr;
  // The current selected node |newNode| could be a child multiple levels below
  // its associated "anchor node" ancestor, so we reference and keep around the
  // anchor node for checking editability.
  // TODO(wkorman): Consider storing previous Position, rather than Node, and
  // making use of EditingUtilies::isEditablePosition() directly.
  Node* newAnchorNode =
      currentCaretPosition.position().parentAnchoredEquivalent().anchorNode();
  if (newNode && newAnchorNode && newNode != newAnchorNode &&
      newAnchorNode->layoutObject() && newAnchorNode->layoutObject()->isBox()) {
    newNode->layoutObject()->mapToVisualRectInAncestorSpace(
        toLayoutBoxModelObject(newAnchorNode->layoutObject()), newRect);
  }
  // It's possible for the timer to be inactive even though we want to
  // invalidate the caret. For example, when running as a layout test the
  // caret blink interval could be zero and thus |m_caretBlinkTimer| will
  // never be started. We provide |forceInvalidation| for use by paint
  // invalidation internals where we need to invalidate the caret regardless
  // of timer state.
  if (!forceInvalidation && !m_caretBlinkTimer.isActive() &&
      newNode == m_previousCaretNode && newRect == m_previousCaretRect &&
      m_caretVisibility == m_previousCaretVisibility)
    return;

  if (m_previousCaretAnchorNode &&
      shouldRepaintCaret(*m_previousCaretAnchorNode)) {
    invalidateLocalCaretRect(m_previousCaretAnchorNode.get(),
                             m_previousCaretRect);
  }
  if (newAnchorNode && shouldRepaintCaret(*newAnchorNode))
    invalidateLocalCaretRect(newAnchorNode, newRect);
  m_previousCaretNode = newNode;
  m_previousCaretAnchorNode = newAnchorNode;
  m_previousCaretRect = newRect;
  m_previousCaretVisibility = m_caretVisibility;
}
TEST_F(PaintControllerPaintTestForSlimmingPaintV2, ChunkIdClientCacheFlag) {
  setBodyInnerHTML(
      "<div id='div' style='width: 200px; height: 200px; opacity: 0.5'>"
      "  <div style='width: 100px; height: 100px; background-color: "
      "blue'></div>"
      "  <div style='width: 100px; height: 100px; background-color: "
      "blue'></div>"
      "</div>");
  PaintLayer& htmlLayer =
      *toLayoutBoxModelObject(document().documentElement()->layoutObject())
           ->layer();
  LayoutBlock& div = *toLayoutBlock(getLayoutObjectByElementId("div"));
  LayoutObject& subDiv = *div.firstChild();
  LayoutObject& subDiv2 = *subDiv.nextSibling();
  EXPECT_DISPLAY_LIST(
      rootPaintController().getDisplayItemList(), 11,
      TestDisplayItem(layoutView(),
                      DisplayItem::kClipFrameToVisibleContentRect),
      TestDisplayItem(*layoutView().layer(), DisplayItem::kSubsequence),
      TestDisplayItem(layoutView(), documentBackgroundType),
      TestDisplayItem(htmlLayer, DisplayItem::kSubsequence),
      TestDisplayItem(div, DisplayItem::kBeginCompositing),
      TestDisplayItem(subDiv, backgroundType),
      TestDisplayItem(subDiv2, backgroundType),
      TestDisplayItem(div, DisplayItem::kEndCompositing),
      TestDisplayItem(htmlLayer, DisplayItem::kEndSubsequence),
      TestDisplayItem(*layoutView().layer(), DisplayItem::kEndSubsequence),
      TestDisplayItem(layoutView(),
                      DisplayItem::clipTypeToEndClipType(
                          DisplayItem::kClipFrameToVisibleContentRect)));

  const PaintChunk& backgroundChunk = rootPaintController().paintChunks()[0];
  EXPECT_TRUE(backgroundChunk.properties.scroll->isRoot());

  const EffectPaintPropertyNode* effectNode = div.paintProperties()->effect();
  EXPECT_EQ(0.5f, effectNode->opacity());
  const PaintChunk& chunk = rootPaintController().paintChunks()[1];
  EXPECT_EQ(*div.layer(), chunk.id->client);
  EXPECT_EQ(effectNode, chunk.properties.effect.get());

  EXPECT_FALSE(div.layer()->isJustCreated());
  // Client used by only paint chunks and non-cachaeable display items but not
  // by any cacheable display items won't be marked as validly cached.
  EXPECT_FALSE(rootPaintController().clientCacheIsValid(*div.layer()));
  EXPECT_FALSE(rootPaintController().clientCacheIsValid(div));
  EXPECT_TRUE(rootPaintController().clientCacheIsValid(subDiv));
}
Beispiel #27
0
TEST_P(CompositedLayerMappingTest, RotatedInterestRectNear90Degrees) {
  setBodyInnerHTML(
      "<div id='target' style='width: 10000px; height: 200px; will-change: "
      "transform; transform: rotateY(89.9999deg)'></div>");

  document().view()->updateAllLifecyclePhases();
  Element* element = document().getElementById("target");
  PaintLayer* paintLayer =
      toLayoutBoxModelObject(element->layoutObject())->layer();
  ASSERT_TRUE(!!paintLayer->graphicsLayerBacking());
  // Because the layer is rotated to almost 90 degrees, floating-point error
  // leads to a reverse-projected rect that is much much larger than the
  // original layer size in certain dimensions. In such cases, we often fall
  // back to the 4000px interest rect padding amount.
  EXPECT_RECT_EQ(IntRect(0, 0, 4000, 200),
                 recomputeInterestRect(paintLayer->graphicsLayerBacking()));
}
static PassRefPtr<TransformPaintPropertyNode> createScrollTranslationIfNeeded(const LayoutObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.isBoxModelObject() || !object.hasOverflowClip())
        return nullptr;

    PaintLayer* layer = toLayoutBoxModelObject(object).layer();
    ASSERT(layer);
    DoubleSize scrollOffset = layer->scrollableArea()->scrollOffset();
    if (scrollOffset.isZero() && !layer->scrollsOverflow())
        return nullptr;

    RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = TransformPaintPropertyNode::create(
        TransformationMatrix().translate(-scrollOffset.width(), -scrollOffset.height()),
        FloatPoint3D(), context.currentTransform);
    context.currentTransform = newTransformNodeForScrollTranslation.get();
    return newTransformNodeForScrollTranslation.release();
}
void IntersectionObservation::clipToRoot(LayoutRect& rect)
{
    // Map and clip rect into root element coordinates.
    // TODO(szager): the writing mode flipping needs a test.
    ASSERT(m_target);
    LayoutObject* rootLayoutObject = m_observer->rootLayoutObject();
    LayoutObject* targetLayoutObject = target()->layoutObject();
    targetLayoutObject->mapToVisibleRectInAncestorSpace(toLayoutBoxModelObject(rootLayoutObject), rect, nullptr);
    if (rootLayoutObject->hasOverflowClip()) {
        LayoutBox* rootLayoutBox = toLayoutBox(rootLayoutObject);
        LayoutRect clipRect(LayoutPoint(), LayoutSize(rootLayoutBox->layer()->size()));
        m_observer->applyRootMargin(clipRect);
        rootLayoutBox->flipForWritingMode(rect);
        rect.intersect(clipRect);
        rootLayoutBox->flipForWritingMode(rect);
    }
}
void MouseRelatedEvent::computeRelativePosition()
{
    Node* targetNode = target() ? target()->toNode() : nullptr;
    if (!targetNode)
        return;

    // Compute coordinates that are based on the target.
    m_layerLocation = m_pageLocation;
    m_offsetLocation = m_pageLocation;

    // Must have an updated layout tree for this math to work correctly.
    targetNode->document().updateLayoutIgnorePendingStylesheets();

    // Adjust offsetLocation to be relative to the target's padding box.
    if (LayoutObject* r = targetNode->layoutObject()) {
        FloatPoint localPos = r->absoluteToLocal(FloatPoint(absoluteLocation()), UseTransforms);

        // Adding this here to address crbug.com/570666. Basically we'd like to
        // find the local coordinates relative to the padding box not the border box.
        if (r->isBoxModelObject()) {
            LayoutBoxModelObject* layoutBox = toLayoutBoxModelObject(r);
            localPos.move(-layoutBox->borderLeft(), -layoutBox->borderTop());
        }

        m_offsetLocation = roundedLayoutPoint(localPos);
        float scaleFactor = 1 / pageZoomFactor(this);
        if (scaleFactor != 1.0f)
            m_offsetLocation.scale(scaleFactor, scaleFactor);
    }

    // Adjust layerLocation to be relative to the layer.
    // FIXME: event.layerX and event.layerY are poorly defined,
    // and probably don't always correspond to PaintLayer offsets.
    // https://bugs.webkit.org/show_bug.cgi?id=21868
    Node* n = targetNode;
    while (n && !n->layoutObject())
        n = n->parentNode();

    if (n) {
        // FIXME: This logic is a wrong implementation of convertToLayerCoords.
        for (PaintLayer* layer = n->layoutObject()->enclosingLayer(); layer; layer = layer->parent())
            m_layerLocation -= toLayoutSize(layer->location());
    }

    m_hasCachedRelativePosition = true;
}