// 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;
}
static void deriveBorderBoxFromContainerContext(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context)
{
    // TODO(trchen): There is some insanity going on with tables. Double check results.
    switch (object.styleRef().position()) {
    case StaticPosition:
        break;
    case RelativePosition:
        context.paintOffset += object.offsetForInFlowPosition();
        break;
    case AbsolutePosition:
        context.currentTransform = context.transformForOutOfFlowPositioned;
        context.paintOffset = context.paintOffsetForOutOfFlowPositioned;
        break;
    case StickyPosition:
        context.paintOffset += object.offsetForInFlowPosition();
        break;
    case FixedPosition:
        context.currentTransform = context.transformForFixedPositioned;
        context.paintOffset = context.paintOffsetForFixedPositioned;
        break;
    default:
        ASSERT_NOT_REACHED();
    }
    if (object.isBox())
        context.paintOffset += toLayoutBox(object).locationOffset();
}
Beispiel #3
0
int PrintContext::pageNumberForElement(Element* element, const FloatSize& pageSizeInPixels)
{
    // Make sure the element is not freed during the layout.
    RefPtrWillBeRawPtr<Element> protect(element);
    element->document().updateLayout();

    LayoutBoxModelObject* box = enclosingBoxModelObject(element->layoutObject());
    if (!box)
        return -1;

    LocalFrame* frame = element->document().frame();
    FloatRect pageRect(FloatPoint(0, 0), pageSizeInPixels);
    PrintContext printContext(frame);
    printContext.begin(pageRect.width(), pageRect.height());
    FloatSize scaledPageSize = pageSizeInPixels;
    scaledPageSize.scale(frame->view()->contentsSize().width() / pageRect.width());
    printContext.computePageRectsWithPageSize(scaledPageSize);

    int top = box->pixelSnappedOffsetTop();
    int left = box->pixelSnappedOffsetLeft();
    size_t pageNumber = 0;
    for (; pageNumber < printContext.pageCount(); pageNumber++) {
        const IntRect& page = printContext.pageRect(pageNumber);
        if (page.x() <= left && left < page.maxX() && page.y() <= top && top < page.maxY())
            return pageNumber;
    }
    return -1;
}
void LineBoxListPainter::paint(const LayoutBoxModelObject& layoutObject, const PaintInfo& paintInfo, const LayoutPoint& paintOffset) const
{
    ASSERT(!shouldPaintSelfOutline(paintInfo.phase) && !shouldPaintDescendantOutlines(paintInfo.phase));

    // Only paint during the foreground/selection phases.
    if (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection && paintInfo.phase != PaintPhaseTextClip && paintInfo.phase != PaintPhaseMask)
        return;

    ASSERT(layoutObject.isLayoutBlock() || (layoutObject.isLayoutInline() && layoutObject.hasLayer())); // The only way an inline could paint like this is if it has a layer.

    if (paintInfo.phase == PaintPhaseForeground && paintInfo.isPrinting())
        addPDFURLRectsForInlineChildrenRecursively(layoutObject, paintInfo, paintOffset);

    // If we have no lines then we have no work to do.
    if (!m_lineBoxList.firstLineBox())
        return;

    if (!m_lineBoxList.anyLineIntersectsRect(LineLayoutBoxModel(const_cast<LayoutBoxModelObject*>(&layoutObject)), paintInfo.cullRect(), paintOffset))
        return;

    PaintInfo info(paintInfo);

    // See if our root lines intersect with the dirty rect. If so, then we paint
    // them. Note that boxes can easily overlap, so we can't make any assumptions
    // based off positions of our first line box or our last line box.
    for (InlineFlowBox* curr = m_lineBoxList.firstLineBox(); curr; curr = curr->nextLineBox()) {
        if (m_lineBoxList.lineIntersectsDirtyRect(LineLayoutBoxModel(const_cast<LayoutBoxModelObject*>(&layoutObject)), curr, info.cullRect(), paintOffset)) {
            RootInlineBox& root = curr->root();
            curr->paint(info, paintOffset, root.lineTop(), root.lineBottom());
        }
    }
}
bool LinkHighlightImpl::computeHighlightLayerPathAndPosition(const LayoutBoxModelObject& paintInvalidationContainer)
{
    if (!m_node || !m_node->layoutObject() || !m_currentGraphicsLayer)
        return false;

    // FIXME: This is defensive code to avoid crashes such as those described in
    // crbug.com/440887. This should be cleaned up once we fix the root cause of
    // of the paint invalidation container not being composited.
    if (!paintInvalidationContainer.layer()->compositedLayerMapping() && !paintInvalidationContainer.layer()->groupedMapping())
        return false;

    // Get quads for node in absolute coordinates.
    Vector<FloatQuad> quads;
    computeQuads(*m_node, quads);
    DCHECK(quads.size());
    Path newPath;

    for (size_t quadIndex = 0; quadIndex < quads.size(); ++quadIndex) {
        FloatQuad absoluteQuad = quads[quadIndex];

        // Scrolling content layers have the same offset from layout object as the non-scrolling layers. Thus we need
        // to adjust for their scroll offset.
        if (m_isScrollingGraphicsLayer) {
            DoubleSize adjustedScrollOffset = paintInvalidationContainer.layer()->getScrollableArea()->adjustedScrollOffset();
            absoluteQuad.move(adjustedScrollOffset.width(), adjustedScrollOffset.height());
        }

        // Transform node quads in target absolute coords to local coordinates in the compositor layer.
        FloatQuad transformedQuad;
        convertTargetSpaceQuadToCompositedLayer(absoluteQuad, m_node->layoutObject(), paintInvalidationContainer, transformedQuad);

        // FIXME: for now, we'll only use rounded paths if we have a single node quad. The reason for this is that
        // we may sometimes get a chain of adjacent boxes (e.g. for text nodes) which end up looking like sausage
        // links: these should ideally be merged into a single rect before creating the path, but that's
        // another CL.
        if (quads.size() == 1 && transformedQuad.isRectilinear()
                && !m_owningWebViewImpl->settingsImpl()->mockGestureTapHighlightsEnabled()) {
            FloatSize rectRoundingRadii(3, 3);
            newPath.addRoundedRect(transformedQuad.boundingBox(), rectRoundingRadii);
        } else {
            addQuadToPath(transformedQuad, newPath);
        }
    }

    FloatRect boundingRect = newPath.boundingRect();
    newPath.translate(-toFloatSize(boundingRect.location()));

    bool pathHasChanged = !(newPath == m_path);
    if (pathHasChanged) {
        m_path = newPath;
        m_contentLayer->layer()->setBounds(enclosingIntRect(boundingRect).size());
    }

    m_contentLayer->layer()->setPosition(boundingRect.location());

    return pathHasChanged;
}
static PassRefPtr<EffectPaintPropertyNode> createEffectIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context)
{
    const ComputedStyle& style = object.styleRef();
    if (!object.isBox() || !style.hasOpacity())
        return nullptr;
    RefPtr<EffectPaintPropertyNode> newEffectNode = EffectPaintPropertyNode::create(style.opacity(), context.currentEffect);
    context.currentEffect = newEffectNode.get();
    return newEffectNode.release();
}
static PassRefPtr<TransformPaintPropertyNode> createPerspectiveIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context)
{
    const ComputedStyle& style = object.styleRef();
    if (!object.isBox() || !style.hasPerspective())
        return nullptr;

    RefPtr<TransformPaintPropertyNode> newTransformNodeForPerspective = TransformPaintPropertyNode::create(
        TransformationMatrix().applyPerspective(style.perspective()),
        perspectiveOrigin(toLayoutBox(object)) + toLayoutSize(context.paintOffset), context.currentTransform);
    context.currentTransform = newTransformNodeForPerspective.get();
    return newTransformNodeForPerspective.release();
}
static void checkIsClippingStackingContextAndContainer(LayoutBoxModelObject& obj)
{
    EXPECT_TRUE(obj.canContainFixedPositionObjects());
    EXPECT_TRUE(obj.hasClipRelatedProperty());
    EXPECT_TRUE(obj.style()->containsPaint());

    // TODO(leviw): Ideally, we wouldn't require a paint layer to handle the clipping
    // and stacking performed by paint containment.
    ASSERT(obj.layer());
    PaintLayer* layer = obj.layer();
    EXPECT_TRUE(layer->stackingNode() && layer->stackingNode()->isStackingContext());
}
static void updateOutOfFlowContext(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context)
{
    const ComputedStyle& style = object.styleRef();
    bool hasTransform = object.isBox() && style.hasTransform();
    if (style.position() != StaticPosition || hasTransform) {
        context.transformForOutOfFlowPositioned = context.currentTransform;
        context.paintOffsetForOutOfFlowPositioned = context.paintOffset;
    }
    if (hasTransform) {
        context.transformForFixedPositioned = context.currentTransform;
        context.paintOffsetForFixedPositioned = context.paintOffset;
    }
}
static PassRefPtr<TransformPaintPropertyNode> createPaintOffsetTranslationIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context)
{
    // TODO(trchen): Eliminate PaintLayer dependency.
    bool shouldCreatePaintOffsetTranslationNode = object.layer() && object.layer()->paintsWithTransform(GlobalPaintNormalPhase);

    if (context.paintOffset == LayoutPoint() || !shouldCreatePaintOffsetTranslationNode)
        return nullptr;

    RefPtr<TransformPaintPropertyNode> newTransformNodeForPaintOffsetTranslation = TransformPaintPropertyNode::create(
        TransformationMatrix().translate(context.paintOffset.x(), context.paintOffset.y()),
        FloatPoint3D(), context.currentTransform);
    context.currentTransform = newTransformNodeForPaintOffsetTranslation.get();
    context.paintOffset = LayoutPoint();
    return newTransformNodeForPaintOffsetTranslation.release();
}
void BlockPainter::paintContinuationOutlines(const PaintInfo& info, const LayoutPoint& paintOffset)
{
    LayoutInline* inlineCont = m_layoutBlock.inlineElementContinuation();
    if (inlineCont && inlineCont->style()->hasOutline() && inlineCont->style()->visibility() == VISIBLE) {
        LayoutInline* inlineLayoutObject = toLayoutInline(inlineCont->node()->layoutObject());
        LayoutBlock* cb = m_layoutBlock.containingBlock();

        bool inlineEnclosedInSelfPaintingLayer = false;
        for (LayoutBoxModelObject* box = inlineLayoutObject; box != cb; box = box->parent()->enclosingBoxModelObject()) {
            if (box->hasSelfPaintingLayer()) {
                inlineEnclosedInSelfPaintingLayer = true;
                break;
            }
        }

        // Do not add continuations for outline painting by our containing block if we are a relative positioned
        // anonymous block (i.e. have our own layer), paint them straightaway instead. This is because a block depends on layoutObjects in its continuation table being
        // in the same layer.
        if (!inlineEnclosedInSelfPaintingLayer && !m_layoutBlock.hasLayer()) {
            cb->addContinuationWithOutline(inlineLayoutObject);
        } else if (!inlineLayoutObject->firstLineBox() || (!inlineEnclosedInSelfPaintingLayer && m_layoutBlock.hasLayer())) {
            // The outline might be painted multiple times if multiple blocks have the same inline element continuation, and the inline has a self-painting layer.
            ScopeRecorder scopeRecorder(*info.context);
            InlinePainter(*inlineLayoutObject).paintOutline(info, paintOffset - m_layoutBlock.locationOffset() + inlineLayoutObject->containingBlock()->location());
        }
    }

    ContinuationOutlineTableMap* table = continuationOutlineTable();
    if (table->isEmpty())
        return;

    OwnPtr<ListHashSet<LayoutInline*>> continuations = table->take(&m_layoutBlock);
    if (!continuations)
        return;

    LayoutPoint accumulatedPaintOffset = paintOffset;
    // Paint each continuation outline.
    ListHashSet<LayoutInline*>::iterator end = continuations->end();
    for (ListHashSet<LayoutInline*>::iterator it = continuations->begin(); it != end; ++it) {
        // Need to add in the coordinates of the intervening blocks.
        LayoutInline* flow = *it;
        LayoutBlock* block = flow->containingBlock();
        for ( ; block && block != &m_layoutBlock; block = block->containingBlock())
            accumulatedPaintOffset.moveBy(block->location());
        ASSERT(block);
        InlinePainter(*flow).paintOutline(info, accumulatedPaintOffset);
    }
}
static PassRefPtr<TransformPaintPropertyNode> createTransformIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context)
{
    const ComputedStyle& style = object.styleRef();
    if (!object.isBox() || !style.hasTransform())
        return nullptr;

    ASSERT(context.paintOffset == LayoutPoint());

    TransformationMatrix matrix;
    style.applyTransform(matrix, toLayoutBox(object).size(), ComputedStyle::ExcludeTransformOrigin,
        ComputedStyle::IncludeMotionPath, ComputedStyle::IncludeIndependentTransformProperties);
    RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = TransformPaintPropertyNode::create(
        matrix, transformOrigin(toLayoutBox(object)), context.currentTransform);
    context.currentTransform = newTransformNodeForTransform.get();
    return newTransformNodeForTransform.release();
}
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;
}
static void invalidatePaintOfScrollbarIfNeeded(Scrollbar* scrollbar, GraphicsLayer* graphicsLayer, bool& previouslyWasOverlay, LayoutRect& previousPaintInvalidationRect, bool needsPaintInvalidationArg, LayoutBox& box, const PaintInvalidationState& paintInvalidationState, const LayoutBoxModelObject& paintInvalidationContainer)
{
    bool isOverlay = scrollbar && scrollbar->isOverlayScrollbar();

    // Calculate paint invalidation rect of the scrollbar, except overlay composited scrollbars because we invalidate the graphics layer only.
    LayoutRect newPaintInvalidationRect;
    if (scrollbar && !(graphicsLayer && isOverlay))
        newPaintInvalidationRect = scrollControlPaintInvalidationRect(scrollbar->frameRect(), box, paintInvalidationState, paintInvalidationContainer);

    bool needsPaintInvalidation = needsPaintInvalidationArg;
    if (graphicsLayer) {
        // If the scrollbar needs paint invalidation but didn't change location/size or the scrollbar is an
        // overlay scrollbar (paint invalidation rect is empty), invalidating the graphics layer is enough
        // (which has been done in ScrollableArea::setScrollbarNeedsPaintInvalidation()).
        // Otherwise invalidatePaintOfScrollControlIfNeeded() below will invalidate the old and new location
        // of the scrollbar on the box's paint invalidation container to ensure newly expanded/shrunk areas
        // of the box to be invalidated.
        needsPaintInvalidation = false;
    }

    // Invalidate the box's display item client if the box's padding box size is affected by change of the
    // non-overlay scrollbar width. We detect change of paint invalidation rect size instead of change of
    // scrollbar width change, which may have some false-positives (e.g. the scrollbar changed length but
    // not width) but won't invalidate more than expected because in the false-positive case the box must
    // have changed size and have been invalidated.
    LayoutSize newScrollbarUsedSpaceInBox;
    if (!isOverlay)
        newScrollbarUsedSpaceInBox = newPaintInvalidationRect.size();
    LayoutSize previousScrollbarUsedSpaceInBox;
    if (!previouslyWasOverlay)
        previousScrollbarUsedSpaceInBox= previousPaintInvalidationRect.size();
    if (newScrollbarUsedSpaceInBox != previousScrollbarUsedSpaceInBox)
        paintInvalidationContainer.invalidateDisplayItemClientOnBacking(box, PaintInvalidationScroll);

    bool invalidated = invalidatePaintOfScrollControlIfNeeded(newPaintInvalidationRect, previousPaintInvalidationRect, needsPaintInvalidation, box, paintInvalidationContainer);

    previousPaintInvalidationRect = newPaintInvalidationRect;
    previouslyWasOverlay = isOverlay;

    if (!invalidated || !scrollbar || graphicsLayer)
        return;

    paintInvalidationContainer.invalidateDisplayItemClientOnBacking(*scrollbar, PaintInvalidationScroll);
    if (scrollbar->isCustomScrollbar())
        toLayoutScrollbar(scrollbar)->invalidateDisplayItemClientsOfScrollbarParts(paintInvalidationContainer);
}
static PassRefPtr<TransformPaintPropertyNode> createScrollTranslationIfNeeded(const LayoutBoxModelObject& object, PaintPropertyTreeBuilderContext& context)
{
    if (!object.hasOverflowClip())
        return nullptr;

    PaintLayer* layer = 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();
}
AXObject* AXObjectCacheImpl::createFromRenderer(LayoutObject* layoutObject)
{
    // FIXME: How could layoutObject->node() ever not be an Element?
    Node* node = layoutObject->node();

    // If the node is aria role="list" or the aria role is empty and its a
    // ul/ol/dl type (it shouldn't be a list if aria says otherwise).
    if (nodeHasRole(node, "list") || nodeHasRole(node, "directory")
        || (nodeHasRole(node, nullAtom) && (isHTMLUListElement(node) || isHTMLOListElement(node) || isHTMLDListElement(node))))
        return AXList::create(layoutObject, *this);

    // aria tables
    if (nodeHasRole(node, "grid") || nodeHasRole(node, "treegrid"))
        return AXARIAGrid::create(layoutObject, *this);
    if (nodeHasRole(node, "row"))
        return AXARIAGridRow::create(layoutObject, *this);
    if (nodeHasRole(node, "gridcell") || nodeHasRole(node, "columnheader") || nodeHasRole(node, "rowheader"))
        return AXARIAGridCell::create(layoutObject, *this);

    // media controls
    if (node && node->isMediaControlElement())
        return AccessibilityMediaControl::create(layoutObject, *this);

    if (isHTMLOptionElement(node))
        return AXListBoxOption::create(layoutObject, *this);

    if (layoutObject->isSVGRoot())
        return AXSVGRoot::create(layoutObject, *this);

    if (layoutObject->isBoxModelObject()) {
        LayoutBoxModelObject* cssBox = toLayoutBoxModelObject(layoutObject);
        if (cssBox->isListBox())
            return AXListBox::create(toLayoutListBox(cssBox), *this);
        if (cssBox->isMenuList())
            return AXMenuList::create(toLayoutMenuList(cssBox), *this);

        // standard tables
        if (cssBox->isTable())
            return AXTable::create(toLayoutTable(cssBox), *this);
        if (cssBox->isTableRow())
            return AXTableRow::create(toLayoutTableRow(cssBox), *this);
        if (cssBox->isTableCell())
            return AXTableCell::create(toLayoutTableCell(cssBox), *this);

        // progress bar
        if (cssBox->isProgress())
            return AXProgressIndicator::create(toLayoutProgress(cssBox), *this);

        // input type=range
        if (cssBox->isSlider())
            return AXSlider::create(toLayoutSlider(cssBox), *this);
    }

    return AXLayoutObject::create(layoutObject, *this);
}
Beispiel #17
0
FloatWillBeLayoutUnit InlineBox::logicalHeight() const
{
    if (hasVirtualLogicalHeight())
        return virtualLogicalHeight();

    if (layoutObject().isText())
        return m_bitfields.isText() ? FloatWillBeLayoutUnit(layoutObject().style(isFirstLineStyle())->fontMetrics().height()) : FloatWillBeLayoutUnit();
    if (layoutObject().isBox() && parent())
        return isHorizontal() ? toLayoutBox(layoutObject()).size().height() : toLayoutBox(layoutObject()).size().width();

    ASSERT(isInlineFlowBox());
    LayoutBoxModelObject* flowObject = boxModelObject();
    const FontMetrics& fontMetrics = layoutObject().style(isFirstLineStyle())->fontMetrics();
    FloatWillBeLayoutUnit result = fontMetrics.height();
    if (parent())
        result += flowObject->borderAndPaddingLogicalHeight();
    return result;
}
static void convertTargetSpaceQuadToCompositedLayer(const FloatQuad& targetSpaceQuad, LayoutObject* targetLayoutObject, const LayoutBoxModelObject& paintInvalidationContainer, FloatQuad& compositedSpaceQuad)
{
    DCHECK(targetLayoutObject);
    for (unsigned i = 0; i < 4; ++i) {
        IntPoint point;
        switch (i) {
        case 0:
            point = roundedIntPoint(targetSpaceQuad.p1());
            break;
        case 1:
            point = roundedIntPoint(targetSpaceQuad.p2());
            break;
        case 2:
            point = roundedIntPoint(targetSpaceQuad.p3());
            break;
        case 3:
            point = roundedIntPoint(targetSpaceQuad.p4());
            break;
        }

        // FIXME: this does not need to be absolute, just in the paint invalidation container's space.
        point = targetLayoutObject->frame()->view()->contentsToRootFrame(point);
        point = paintInvalidationContainer.frame()->view()->rootFrameToContents(point);
        FloatPoint floatPoint = paintInvalidationContainer.absoluteToLocal(point, UseTransforms);
        PaintLayer::mapPointInPaintInvalidationContainerToBacking(paintInvalidationContainer, floatPoint);

        switch (i) {
        case 0:
            compositedSpaceQuad.setP1(floatPoint);
            break;
        case 1:
            compositedSpaceQuad.setP2(floatPoint);
            break;
        case 2:
            compositedSpaceQuad.setP3(floatPoint);
            break;
        case 3:
            compositedSpaceQuad.setP4(floatPoint);
            break;
        }
    }
}
LayerFixedPositionRecorder::LayerFixedPositionRecorder(GraphicsContext& graphicsContext, const LayoutBoxModelObject& layoutObject)
    : m_graphicsContext(graphicsContext)
    , m_layoutObject(layoutObject)
    , m_isFixedPosition(layoutObject.style()->position() == FixedPosition)
    , m_isFixedPositionContainer(layoutObject.canContainFixedPositionObjects())
{
    if (!RuntimeEnabledFeatures::slimmingPaintCompositorLayerizationEnabled())
        return;

    if (m_graphicsContext.displayItemList()->displayItemConstructionIsDisabled())
        return;

    if (m_isFixedPosition)
        m_graphicsContext.displayItemList()->createAndAppend<BeginFixedPositionDisplayItem>(m_layoutObject);

    // TODO(trchen): Adding a pair of display items on every transformed
    // element can be expensive. Investigate whether we can optimize out some
    // of them if applicable.
    if (m_isFixedPositionContainer)
        m_graphicsContext.displayItemList()->createAndAppend<BeginFixedPositionContainerDisplayItem>(m_layoutObject);
}
void CanvasRenderingContext2D::updateElementAccessibility(const Path& path,
                                                          Element* element) {
  element->document().updateStyleAndLayoutIgnorePendingStylesheets();
  AXObjectCache* axObjectCache = element->document().existingAXObjectCache();
  LayoutBoxModelObject* lbmo = canvas()->layoutBoxModelObject();
  LayoutObject* renderer = canvas()->layoutObject();
  if (!axObjectCache || !lbmo || !renderer)
    return;

  // Get the transformed path.
  Path transformedPath = path;
  transformedPath.transform(state().transform());

  // Add border and padding to the bounding rect.
  LayoutRect elementRect = enclosingLayoutRect(transformedPath.boundingRect());
  elementRect.move(lbmo->borderLeft() + lbmo->paddingLeft(),
                   lbmo->borderTop() + lbmo->paddingTop());

  // Update the accessible object.
  axObjectCache->setCanvasObjectBounds(canvas(), element, elementRect);
}
void LinkHighlightImpl::attachLinkHighlightToCompositingLayer(const LayoutBoxModelObject& paintInvalidationContainer)
{
    GraphicsLayer* newGraphicsLayer = paintInvalidationContainer.layer()->graphicsLayerBacking();
    m_isScrollingGraphicsLayer = false;
    // FIXME: There should always be a GraphicsLayer. See crbug.com/431961.
    if (paintInvalidationContainer.layer()->needsCompositedScrolling() && m_node->layoutObject() != &paintInvalidationContainer) {
        newGraphicsLayer = paintInvalidationContainer.layer()->graphicsLayerBackingForScrolling();
        m_isScrollingGraphicsLayer = true;
    }
    if (!newGraphicsLayer)
        return;

    m_clipLayer->setTransform(SkMatrix44(SkMatrix44::kIdentity_Constructor));

    if (m_currentGraphicsLayer != newGraphicsLayer) {
        if (m_currentGraphicsLayer)
            clearGraphicsLayerLinkHighlightPointer();

        m_currentGraphicsLayer = newGraphicsLayer;
        m_currentGraphicsLayer->addLinkHighlight(this);
    }
}
LayerClipRecorder::LayerClipRecorder(GraphicsContext& graphicsContext, const LayoutBoxModelObject& layoutObject, DisplayItem::Type clipType, const ClipRect& clipRect,
                                     const PaintLayerPaintingInfo* localPaintingInfo, const LayoutPoint& fragmentOffset, PaintLayerFlags paintFlags, BorderRadiusClippingRule rule)
    : m_graphicsContext(graphicsContext)
    , m_layoutObject(layoutObject)
    , m_clipType(clipType)
{
    IntRect snappedClipRect = pixelSnappedIntRect(clipRect.rect());
    Vector<FloatRoundedRect> roundedRects;
    if (localPaintingInfo && clipRect.hasRadius()) {
        collectRoundedRectClips(*layoutObject.layer(), *localPaintingInfo, graphicsContext, fragmentOffset, paintFlags, rule, roundedRects);
    }

    m_graphicsContext.paintController().createAndAppend<ClipDisplayItem>(layoutObject, m_clipType, snappedClipRect, roundedRects);
}
Beispiel #23
0
static void adjustClipRectsForChildren(const LayoutBoxModelObject& layoutObject,
                                       ClipRects& clipRects) {
  EPosition position = layoutObject.styleRef().position();
  // A fixed object is essentially the root of its containing block hierarchy,
  // so when we encounter such an object, we reset our clip rects to the
  // fixedClipRect.
  if (position == FixedPosition) {
    clipRects.setPosClipRect(clipRects.fixedClipRect());
    clipRects.setOverflowClipRect(clipRects.fixedClipRect());
    clipRects.setFixed(true);
  } else if (position == RelativePosition) {
    clipRects.setPosClipRect(clipRects.overflowClipRect());
  } else if (position == AbsolutePosition) {
    clipRects.setOverflowClipRect(clipRects.posClipRect());
  }
}
void PaintPropertyTreeBuilder::walk(LayoutBoxModelObject& object, const PaintPropertyTreeBuilderContext& context)
{
    ASSERT(object.isBox() != object.isLayoutInline()); // Either or.

    PaintPropertyTreeBuilderContext localContext(context);

    deriveBorderBoxFromContainerContext(object, localContext);
    RefPtr<TransformPaintPropertyNode> newTransformNodeForPaintOffsetTranslation = createPaintOffsetTranslationIfNeeded(object, localContext);
    RefPtr<TransformPaintPropertyNode> newTransformNodeForTransform = createTransformIfNeeded(object, localContext);
    RefPtr<EffectPaintPropertyNode> newEffectNode = createEffectIfNeeded(object, localContext);
    RefPtr<TransformPaintPropertyNode> newTransformNodeForPerspective = createPerspectiveIfNeeded(object, localContext);
    RefPtr<TransformPaintPropertyNode> newTransformNodeForScrollTranslation = createScrollTranslationIfNeeded(object, localContext);
    updateOutOfFlowContext(object, localContext);

    if (newTransformNodeForPaintOffsetTranslation || newTransformNodeForTransform || newEffectNode || newTransformNodeForPerspective || newTransformNodeForScrollTranslation) {
        OwnPtr<ObjectPaintProperties> updatedPaintProperties = ObjectPaintProperties::create(
            newTransformNodeForPaintOffsetTranslation.release(),
            newTransformNodeForTransform.release(),
            newEffectNode.release(),
            newTransformNodeForPerspective.release(),
            newTransformNodeForScrollTranslation.release());
        object.setObjectPaintProperties(updatedPaintProperties.release());
    } else {
        object.clearObjectPaintProperties();
    }

    // TODO(trchen): Walk subframes for LayoutFrame.

    // TODO(trchen): Implement SVG walk.
    if (object.isSVGRoot()) {
        return;
    }

    for (LayoutObject* child = object.slowFirstChild(); child; child = child->nextSibling()) {
        if (child->isText())
            continue;
        walk(toLayoutBoxModelObject(*child), localContext);
    }
}
PaintInvalidationState::PaintInvalidationState(PaintInvalidationState& next, LayoutBoxModelObject& layoutObject, const LayoutBoxModelObject& paintInvalidationContainer)
    : m_clipped(false)
    , m_cachedOffsetsEnabled(true)
    , m_forcedSubtreeInvalidationWithinContainer(next.m_forcedSubtreeInvalidationWithinContainer)
    , m_forcedSubtreeInvalidationRectUpdateWithinContainer(next.m_forcedSubtreeInvalidationRectUpdateWithinContainer)
    , m_paintInvalidationContainer(paintInvalidationContainer)
    , m_pendingDelayedPaintInvalidations(next.pendingDelayedPaintInvalidationTargets())
{
    // FIXME: SVG could probably benefit from a stack-based optimization like html does. crbug.com/391054
    bool establishesPaintInvalidationContainer = layoutObject == m_paintInvalidationContainer;
    bool fixed = layoutObject.style()->position() == FixedPosition;

    if (!layoutObject.supportsPaintInvalidationStateCachedOffsets() || !next.m_cachedOffsetsEnabled)
        m_cachedOffsetsEnabled = false;
    if (establishesPaintInvalidationContainer) {
        // When we hit a new paint invalidation container, we don't need to
        // continue forcing a check for paint invalidation, since we're
        // descending into a different invalidation container. (For instance if
        // our parents were moved, the entire container will just move.)
        m_forcedSubtreeInvalidationWithinContainer = false;
    } else {
        if (m_cachedOffsetsEnabled) {
            if (fixed) {
                FloatPoint fixedOffset = layoutObject.localToContainerPoint(FloatPoint(), &m_paintInvalidationContainer, TraverseDocumentBoundaries);
                m_paintOffset = LayoutSize(fixedOffset.x(), fixedOffset.y());
            } else {
                LayoutSize offset = layoutObject.isBox() && !layoutObject.isTableRow() ? toLayoutBox(layoutObject).locationOffset() : LayoutSize();
                m_paintOffset = next.m_paintOffset + offset;
            }

            if (layoutObject.isOutOfFlowPositioned() && !fixed) {
                if (LayoutObject* container = layoutObject.container()) {
                    if (container->style()->hasInFlowPosition() && container->isLayoutInline())
                        m_paintOffset += toLayoutInline(container)->offsetForInFlowPositionedInline(toLayoutBox(layoutObject));
                }
            }

            if (layoutObject.style()->hasInFlowPosition() && layoutObject.hasLayer())
                m_paintOffset += layoutObject.layer()->offsetForInFlowPosition();
        }

        m_clipped = !fixed && next.m_clipped;
        if (m_clipped)
            m_clipRect = next.m_clipRect;
    }

    if (m_cachedOffsetsEnabled && layoutObject.isSVGRoot()) {
        const LayoutSVGRoot& svgRoot = toLayoutSVGRoot(layoutObject);
        m_svgTransform = adoptPtr(new AffineTransform(svgRoot.localToBorderBoxTransform()));
        if (svgRoot.shouldApplyViewportClip())
            addClipRectRelativeToPaintOffset(LayoutSize(svgRoot.pixelSnappedSize()));
    }

    applyClipIfNeeded(layoutObject);

    // FIXME: <http://bugs.webkit.org/show_bug.cgi?id=13443> Apply control clip if present.
}
void BackgroundImageGeometry::calculate(
    const LayoutBoxModelObject& obj,
    const LayoutBoxModelObject* paintContainer,
    const GlobalPaintFlags globalPaintFlags,
    const FillLayer& fillLayer,
    const LayoutRect& paintRect) {
  LayoutUnit left;
  LayoutUnit top;
  LayoutSize positioningAreaSize;
  bool isLayoutView = obj.isLayoutView();
  const LayoutBox* rootBox = nullptr;
  if (isLayoutView) {
    // It is only possible reach here when root element has a box.
    Element* documentElement = obj.document().documentElement();
    DCHECK(documentElement);
    DCHECK(documentElement->layoutObject());
    DCHECK(documentElement->layoutObject()->isBox());
    rootBox = toLayoutBox(documentElement->layoutObject());
  }
  const LayoutBoxModelObject& positioningBox =
      isLayoutView ? static_cast<const LayoutBoxModelObject&>(*rootBox) : obj;

  // Determine the background positioning area and set destRect to the
  // background painting area.  destRect will be adjusted later if the
  // background is non-repeating.
  // FIXME: transforms spec says that fixed backgrounds behave like scroll
  // inside transforms.
  bool fixedAttachment = fillLayer.attachment() == FixedBackgroundAttachment;

  if (RuntimeEnabledFeatures::fastMobileScrollingEnabled()) {
    // As a side effect of an optimization to blit on scroll, we do not honor
    // the CSS property "background-attachment: fixed" because it may result in
    // rendering artifacts. Note, these artifacts only appear if we are blitting
    // on scroll of a page that has fixed background images.
    fixedAttachment = false;
  }

  if (!fixedAttachment) {
    setDestRect(paintRect);

    LayoutUnit right;
    LayoutUnit bottom;
    // Scroll and Local.
    if (fillLayer.origin() != BorderFillBox) {
      left = LayoutUnit(positioningBox.borderLeft());
      right = LayoutUnit(positioningBox.borderRight());
      top = LayoutUnit(positioningBox.borderTop());
      bottom = LayoutUnit(positioningBox.borderBottom());
      if (fillLayer.origin() == ContentFillBox) {
        left += positioningBox.paddingLeft();
        right += positioningBox.paddingRight();
        top += positioningBox.paddingTop();
        bottom += positioningBox.paddingBottom();
      }
    }

    if (isLayoutView) {
      // The background of the box generated by the root element covers the
      // entire canvas and will be painted by the view object, but the we should
      // still use the root element box for positioning.
      positioningAreaSize =
          rootBox->size() - LayoutSize(left + right, top + bottom),
      rootBox->location();
      // The input paint rect is specified in root element local coordinate
      // (i.e. a transform is applied on the context for painting), and is
      // expanded to cover the whole canvas.  Since left/top is relative to the
      // paint rect, we need to offset them back.
      left -= paintRect.x();
      top -= paintRect.y();
    } else {
      positioningAreaSize =
          paintRect.size() - LayoutSize(left + right, top + bottom);
    }
  } else {
    setHasNonLocalGeometry();

    LayoutRect viewportRect = obj.viewRect();
    if (fixedBackgroundPaintsInLocalCoordinates(obj, globalPaintFlags)) {
      viewportRect.setLocation(LayoutPoint());
    } else {
      if (FrameView* frameView = obj.view()->frameView())
        viewportRect.setLocation(IntPoint(frameView->scrollOffsetInt()));
      // Compensate the translations created by ScrollRecorders.
      // TODO(trchen): Fix this for SP phase 2. crbug.com/529963.
      viewportRect.moveBy(
          accumulatedScrollOffsetForFixedBackground(obj, paintContainer));
    }

    if (paintContainer)
      viewportRect.moveBy(
          LayoutPoint(-paintContainer->localToAbsolute(FloatPoint())));

    setDestRect(viewportRect);
    positioningAreaSize = destRect().size();
  }

  LayoutSize fillTileSize(
      calculateFillTileSize(positioningBox, fillLayer, positioningAreaSize));
  // It's necessary to apply the heuristic here prior to any further
  // calculations to avoid incorrectly using sub-pixel values that won't be
  // present in the painted tile.
  setTileSize(applySubPixelHeuristicToImageSize(fillTileSize, m_destRect));

  EFillRepeat backgroundRepeatX = fillLayer.repeatX();
  EFillRepeat backgroundRepeatY = fillLayer.repeatY();
  LayoutUnit unsnappedAvailableWidth =
      positioningAreaSize.width() - fillTileSize.width();
  LayoutUnit unsnappedAvailableHeight =
      positioningAreaSize.height() - fillTileSize.height();
  positioningAreaSize =
      LayoutSize(snapSizeToPixel(positioningAreaSize.width(), m_destRect.x()),
                 snapSizeToPixel(positioningAreaSize.height(), m_destRect.y()));
  LayoutUnit availableWidth = positioningAreaSize.width() - tileSize().width();
  LayoutUnit availableHeight =
      positioningAreaSize.height() - tileSize().height();

  LayoutUnit computedXPosition =
      roundedMinimumValueForLength(fillLayer.xPosition(), availableWidth);
  if (backgroundRepeatX == RoundFill &&
      positioningAreaSize.width() > LayoutUnit() &&
      fillTileSize.width() > LayoutUnit()) {
    int nrTiles = std::max(
        1, roundToInt(positioningAreaSize.width() / fillTileSize.width()));
    LayoutUnit roundedWidth = positioningAreaSize.width() / nrTiles;

    // Maintain aspect ratio if background-size: auto is set
    if (fillLayer.size().size.height().isAuto() &&
        backgroundRepeatY != RoundFill) {
      fillTileSize.setHeight(fillTileSize.height() * roundedWidth /
                             fillTileSize.width());
    }
    fillTileSize.setWidth(roundedWidth);

    setTileSize(applySubPixelHeuristicToImageSize(fillTileSize, m_destRect));
    setPhaseX(tileSize().width()
                  ? LayoutUnit(roundf(
                        tileSize().width() -
                        fmodf((computedXPosition + left), tileSize().width())))
                  : LayoutUnit());
    setSpaceSize(LayoutSize());
  }

  LayoutUnit computedYPosition =
      roundedMinimumValueForLength(fillLayer.yPosition(), availableHeight);
  if (backgroundRepeatY == RoundFill &&
      positioningAreaSize.height() > LayoutUnit() &&
      fillTileSize.height() > LayoutUnit()) {
    int nrTiles = std::max(
        1, roundToInt(positioningAreaSize.height() / fillTileSize.height()));
    LayoutUnit roundedHeight = positioningAreaSize.height() / nrTiles;
    // Maintain aspect ratio if background-size: auto is set
    if (fillLayer.size().size.width().isAuto() &&
        backgroundRepeatX != RoundFill) {
      fillTileSize.setWidth(fillTileSize.width() * roundedHeight /
                            fillTileSize.height());
    }
    fillTileSize.setHeight(roundedHeight);

    setTileSize(applySubPixelHeuristicToImageSize(fillTileSize, m_destRect));
    setPhaseY(tileSize().height()
                  ? LayoutUnit(roundf(
                        tileSize().height() -
                        fmodf((computedYPosition + top), tileSize().height())))
                  : LayoutUnit());
    setSpaceSize(LayoutSize());
  }

  if (backgroundRepeatX == RepeatFill) {
    setRepeatX(fillLayer, fillTileSize.width(), availableWidth,
               unsnappedAvailableWidth, left);
  } else if (backgroundRepeatX == SpaceFill &&
             tileSize().width() > LayoutUnit()) {
    LayoutUnit space = getSpaceBetweenImageTiles(positioningAreaSize.width(),
                                                 tileSize().width());
    if (space >= LayoutUnit())
      setSpaceX(space, availableWidth, left);
    else
      backgroundRepeatX = NoRepeatFill;
  }
  if (backgroundRepeatX == NoRepeatFill) {
    LayoutUnit xOffset = fillLayer.backgroundXOrigin() == RightEdge
                             ? availableWidth - computedXPosition
                             : computedXPosition;
    setNoRepeatX(left + xOffset);
  }

  if (backgroundRepeatY == RepeatFill) {
    setRepeatY(fillLayer, fillTileSize.height(), availableHeight,
               unsnappedAvailableHeight, top);
  } else if (backgroundRepeatY == SpaceFill &&
             tileSize().height() > LayoutUnit()) {
    LayoutUnit space = getSpaceBetweenImageTiles(positioningAreaSize.height(),
                                                 tileSize().height());
    if (space >= LayoutUnit())
      setSpaceY(space, availableHeight, top);
    else
      backgroundRepeatY = NoRepeatFill;
  }
  if (backgroundRepeatY == NoRepeatFill) {
    LayoutUnit yOffset = fillLayer.backgroundYOrigin() == BottomEdge
                             ? availableHeight - computedYPosition
                             : computedYPosition;
    setNoRepeatY(top + yOffset);
  }

  if (fixedAttachment)
    useFixedAttachment(paintRect.location());

  // Clip the final output rect to the paint rect
  m_destRect.intersect(paintRect);

  // Snap as-yet unsnapped values.
  setDestRect(LayoutRect(pixelSnappedIntRect(m_destRect)));
}
Beispiel #27
0
static void applyClipRects(const ClipRectsContext& context,
                           const LayoutBoxModelObject& layoutObject,
                           LayoutPoint offset,
                           ClipRects& clipRects) {
  DCHECK(layoutObject.hasClipRelatedProperty() ||
         (layoutObject.isSVGRoot() &&
          toLayoutSVGRoot(&layoutObject)->shouldApplyViewportClip()));
  LayoutView* view = layoutObject.view();
  DCHECK(view);
  if (clipRects.fixed() && context.rootLayer->layoutObject() == view)
    offset -= LayoutSize(view->frameView()->scrollOffset());
  if (layoutObject.hasOverflowClip() ||
      (layoutObject.isSVGRoot() &&
       toLayoutSVGRoot(&layoutObject)->shouldApplyViewportClip()) ||
      (layoutObject.styleRef().containsPaint() && layoutObject.isBox())) {
    ClipRect newOverflowClip =
        toLayoutBox(layoutObject)
            .overflowClipRect(offset, context.overlayScrollbarClipBehavior);
    newOverflowClip.setHasRadius(layoutObject.styleRef().hasBorderRadius());
    clipRects.setOverflowClipRect(
        intersection(newOverflowClip, clipRects.overflowClipRect()));
    if (layoutObject.isPositioned())
      clipRects.setPosClipRect(
          intersection(newOverflowClip, clipRects.posClipRect()));
    if (layoutObject.isLayoutView())
      clipRects.setFixedClipRect(
          intersection(newOverflowClip, clipRects.fixedClipRect()));
    if (layoutObject.styleRef().containsPaint()) {
      clipRects.setPosClipRect(
          intersection(newOverflowClip, clipRects.posClipRect()));
      clipRects.setFixedClipRect(
          intersection(newOverflowClip, clipRects.fixedClipRect()));
    }
  }
  if (layoutObject.hasClip()) {
    LayoutRect newClip = toLayoutBox(layoutObject).clipRect(offset);
    clipRects.setPosClipRect(
        intersection(newClip, clipRects.posClipRect()).setIsClippedByClipCss());
    clipRects.setOverflowClipRect(
        intersection(newClip, clipRects.overflowClipRect())
            .setIsClippedByClipCss());
    clipRects.setFixedClipRect(intersection(newClip, clipRects.fixedClipRect())
                                   .setIsClippedByClipCss());
  }
}
void InlineFlowBoxPainter::paint(const PaintInfo& paintInfo, const LayoutPoint& paintOffset, const LayoutUnit lineTop, const LayoutUnit lineBottom)
{
    LayoutRect overflowRect(m_inlineFlowBox.visualOverflowRect(lineTop, lineBottom));
    m_inlineFlowBox.flipForWritingMode(overflowRect);
    overflowRect.moveBy(paintOffset);

    if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect)))
        return;

    if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) {
        // Add ourselves to the paint info struct's list of inlines that need to paint their
        // outlines.
        if (m_inlineFlowBox.layoutObject().style()->visibility() == VISIBLE && m_inlineFlowBox.layoutObject().style()->hasOutline() && !m_inlineFlowBox.isRootInlineBox()) {
            LayoutInline& inlineFlow = toLayoutInline(m_inlineFlowBox.layoutObject());

            LayoutBlock* cb = 0;
            bool containingBlockPaintsContinuationOutline = inlineFlow.continuation() || inlineFlow.isInlineElementContinuation();
            if (containingBlockPaintsContinuationOutline) {
                // FIXME: See https://bugs.webkit.org/show_bug.cgi?id=54690. We currently don't reconnect inline continuations
                // after a child removal. As a result, those merged inlines do not get seperated and hence not get enclosed by
                // anonymous blocks. In this case, it is better to bail out and paint it ourself.
                LayoutBlock* enclosingAnonymousBlock = m_inlineFlowBox.layoutObject().containingBlock();
                if (!enclosingAnonymousBlock->isAnonymousBlock()) {
                    containingBlockPaintsContinuationOutline = false;
                } else {
                    cb = enclosingAnonymousBlock->containingBlock();
                    for (LayoutBoxModelObject* box = m_inlineFlowBox.boxModelObject(); box != cb; box = box->parent()->enclosingBoxModelObject()) {
                        if (box->hasSelfPaintingLayer()) {
                            containingBlockPaintsContinuationOutline = false;
                            break;
                        }
                    }
                }
            }

            if (containingBlockPaintsContinuationOutline) {
                // Add ourselves to the containing block of the entire continuation so that it can
                // paint us atomically.
                cb->addContinuationWithOutline(toLayoutInline(m_inlineFlowBox.layoutObject().node()->layoutObject()));
            } else if (!inlineFlow.isInlineElementContinuation()) {
                paintInfo.outlineObjects()->add(&inlineFlow);
            }
        }
    } else if (paintInfo.phase == PaintPhaseMask) {
        DrawingRecorder recorder(*paintInfo.context, m_inlineFlowBox, DisplayItem::paintPhaseToDrawingType(paintInfo.phase), pixelSnappedIntRect(overflowRect));
        if (!recorder.canUseCachedDrawing())
            paintMask(paintInfo, paintOffset);
        return;
    } else if (paintInfo.phase == PaintPhaseForeground) {
        // Paint our background, border and box-shadow.
        paintBoxDecorationBackground(paintInfo, paintOffset);
    }

    // Paint our children.
    if (paintInfo.phase != PaintPhaseSelfOutline) {
        PaintInfo childInfo(paintInfo);
        childInfo.phase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase;

        if (childInfo.paintingRoot && childInfo.paintingRoot->isDescendantOf(&m_inlineFlowBox.layoutObject()))
            childInfo.paintingRoot = 0;
        else
            childInfo.updatePaintingRootForChildren(&m_inlineFlowBox.layoutObject());

        for (InlineBox* curr = m_inlineFlowBox.firstChild(); curr; curr = curr->nextOnLine()) {
            if (curr->layoutObject().isText() || !curr->boxModelObject()->hasSelfPaintingLayer())
                curr->paint(childInfo, paintOffset, lineTop, lineBottom);
        }
    }
}