void PaintLayerClipper::calculateClipRects(const ClipRectsContext& context, ClipRects& clipRects) const
{
    bool rootLayerScrolls = m_layoutObject.document().settings() && m_layoutObject.document().settings()->rootLayerScrolls();
    if (!m_layoutObject.layer()->parent() && !rootLayerScrolls) {
        // The root layer's clip rect is always infinite.
        clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect()));
        return;
    }

    bool isClippingRoot = m_layoutObject.layer() == context.rootLayer;

    // For transformed layers, the root layer was shifted to be us, so there is no need to
    // examine the parent. We want to cache clip rects with us as the root.
    PaintLayer* parentLayer = !isClippingRoot ? m_layoutObject.layer()->parent() : 0;
    // Ensure that our parent's clip has been calculated so that we can examine the values.
    if (parentLayer) {
        // FIXME: Why don't we just call getClipRects here?
        if (context.usesCache() && parentLayer->clipper().cachedClipRects(context)) {
            clipRects = *parentLayer->clipper().cachedClipRects(context);
        } else {
            parentLayer->clipper().calculateClipRects(context, clipRects);
        }
    } else {
        clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect()));
    }

    adjustClipRectsForChildren(m_layoutObject, clipRects);

    if ((m_layoutObject.hasOverflowClip() && shouldRespectOverflowClip(context)) || m_layoutObject.hasClip()) {
        // This offset cannot use convertToLayerCoords, because sometimes our rootLayer may be across
        // some transformed layer boundary, for example, in the PaintLayerCompositor overlapMap, where
        // clipRects are needed in view space.
        applyClipRects(context, m_layoutObject, roundedLayoutPoint(m_layoutObject.localToContainerPoint(FloatPoint(), context.rootLayer->layoutObject())), clipRects);
    }
}
Beispiel #2
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 bool shouldRepaintSubsequence(PaintLayer& paintLayer, const PaintLayerPaintingInfo& paintingInfo, ShouldRespectOverflowClip respectOverflowClip, const LayoutSize& subpixelAccumulation)
{
    bool needsRepaint = false;

    // Repaint subsequence if the layer is marked for needing repaint.
    if (paintLayer.needsRepaint())
        needsRepaint = true;

    // Repaint if layer's clip changes.
    ClipRects& clipRects = paintLayer.clipper().paintingClipRects(paintingInfo.rootLayer, respectOverflowClip, subpixelAccumulation);
    ClipRects* previousClipRects = paintLayer.previousPaintingClipRects();
    if (!needsRepaint && &clipRects != previousClipRects && (!previousClipRects || clipRects != *previousClipRects))
        needsRepaint = true;
    paintLayer.setPreviousPaintingClipRects(clipRects);

    // Repaint if previously the layer might be clipped by paintDirtyRect and paintDirtyRect changes.
    if (!needsRepaint && paintLayer.previousPaintResult() == PaintLayerPainter::MayBeClippedByPaintDirtyRect && paintLayer.previousPaintDirtyRect() != paintingInfo.paintDirtyRect)
        needsRepaint = true;
    paintLayer.setPreviousPaintDirtyRect(paintingInfo.paintDirtyRect);

    // Repaint if scroll offset accumulation changes.
    if (!needsRepaint && paintingInfo.scrollOffsetAccumulation != paintLayer.previousScrollOffsetAccumulationForPainting())
        needsRepaint = true;
    paintLayer.setPreviousScrollOffsetAccumulationForPainting(paintingInfo.scrollOffsetAccumulation);

    return needsRepaint;
}
void PaintLayerClipper::clearClipRectsIncludingDescendants()
{
    m_cache = nullptr;

    for (PaintLayer* layer = m_layoutObject.layer()->firstChild(); layer; layer = layer->nextSibling()) {
        layer->clipper().clearClipRectsIncludingDescendants();
    }
}
void PaintLayerClipper::clearClipRectsIncludingDescendants(ClipRectsCacheSlot cacheSlot)
{
    if (m_cache)
        m_cache->clear(cacheSlot);

    for (PaintLayer* layer = m_layoutObject.layer()->firstChild(); layer; layer = layer->nextSibling()) {
        layer->clipper().clearClipRectsIncludingDescendants(cacheSlot);
    }
}
Beispiel #6
0
void PaintLayerClipper::clearClipRectsIncludingDescendants() {
  if (m_geometryMapper)
    m_geometryMapper.reset(new GeometryMapper);
  m_layer.clearClipRectsCache();

  for (PaintLayer* layer = m_layer.firstChild(); layer;
       layer = layer->nextSibling()) {
    layer->clipper().clearClipRectsIncludingDescendants();
  }
}
Beispiel #7
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 #8
0
TEST_F(PaintLayerClipperTest, LayoutSVGRoot) {
  setBodyInnerHTML(
      "<!DOCTYPE html>"
      "<svg id=target width=200 height=300 style='position: relative'>"
      "  <rect width=400 height=500 fill='blue'/>"
      "</svg>");

  Element* target = document().getElementById("target");
  PaintLayer* targetPaintLayer =
      toLayoutBoxModelObject(target->layoutObject())->layer();
  ClipRectsContext context(document().layoutView()->layer(), UncachedClipRects);
  LayoutRect layerBounds;
  ClipRect backgroundRect, foregroundRect;
  targetPaintLayer->clipper().calculateRects(
      context, LayoutRect(LayoutRect::infiniteIntRect()), layerBounds,
      backgroundRect, foregroundRect);
  EXPECT_EQ(LayoutRect(LayoutRect::infiniteIntRect()), backgroundRect.rect());
  EXPECT_EQ(LayoutRect(LayoutRect::infiniteIntRect()), foregroundRect.rect());
  EXPECT_EQ(LayoutRect(8, 8, 200, 300), layerBounds);
}
Beispiel #9
0
TEST_F(PaintLayerClipperTest, LayoutSVGRootChild) {
  setBodyInnerHTML(
      "<svg width=200 height=300 style='position: relative'>"
      "  <foreignObject width=400 height=500>"
      "    <div id=target xmlns='http://www.w3.org/1999/xhtml' "
      "style='position: relative'></div>"
      "  </foreignObject>"
      "</svg>");

  Element* target = document().getElementById("target");
  PaintLayer* targetPaintLayer =
      toLayoutBoxModelObject(target->layoutObject())->layer();
  ClipRectsContext context(document().layoutView()->layer(), UncachedClipRects);
  LayoutRect layerBounds;
  ClipRect backgroundRect, foregroundRect;
  targetPaintLayer->clipper().calculateRects(
      context, LayoutRect(LayoutRect::infiniteIntRect()), layerBounds,
      backgroundRect, foregroundRect);
  EXPECT_EQ(LayoutRect(8, 8, 200, 300), backgroundRect.rect());
  EXPECT_EQ(LayoutRect(8, 8, 200, 300), foregroundRect.rect());
  EXPECT_EQ(LayoutRect(8, 8, 400, 0), layerBounds);
}
Beispiel #10
0
void PaintLayerClipper::calculateClipRects(const ClipRectsContext& context,
                                           ClipRects& clipRects) const {
  const LayoutBoxModelObject& layoutObject = *m_layer.layoutObject();
  if (!m_layer.parent() &&
      !RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
    // The root layer's clip rect is always infinite.
    clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect()));
    return;
  }

  bool isClippingRoot = &m_layer == context.rootLayer;

  // For transformed layers, the root layer was shifted to be us, so there is no
  // need to examine the parent. We want to cache clip rects with us as the
  // root.
  PaintLayer* parentLayer = !isClippingRoot ? m_layer.parent() : nullptr;
  // Ensure that our parent's clip has been calculated so that we can examine
  // the values.
  if (parentLayer) {
    parentLayer->clipper().getOrCalculateClipRects(context, clipRects);
  } else {
    clipRects.reset(LayoutRect(LayoutRect::infiniteIntRect()));
  }

  adjustClipRectsForChildren(layoutObject, clipRects);

  if (shouldClipOverflow(context) || layoutObject.hasClip() ||
      (layoutObject.isSVGRoot() &&
       toLayoutSVGRoot(&layoutObject)->shouldApplyViewportClip())) {
    // This offset cannot use convertToLayerCoords, because sometimes our
    // rootLayer may be across some transformed layer boundary, for example, in
    // the PaintLayerCompositor overlapMap, where clipRects are needed in view
    // space.
    applyClipRects(context, layoutObject,
                   roundedLayoutPoint(layoutObject.localToAncestorPoint(
                       FloatPoint(), context.rootLayer->layoutObject())),
                   clipRects);
  }
}