Example #1
0
void ColumnBalancer::traverseSubtree(const LayoutBox& box)
{
    if (box.childrenInline() && box.isLayoutBlockFlow()) {
        // Look for breaks between lines.
        for (const RootInlineBox* line = toLayoutBlockFlow(box).firstRootBox(); line; line = line->nextRootBox()) {
            LayoutUnit lineTopInFlowThread = m_flowThreadOffset + line->lineTopWithLeading();
            if (lineTopInFlowThread < group().logicalTopInFlowThread())
                continue;
            if (lineTopInFlowThread >= group().logicalBottomInFlowThread())
                break;
            examineLine(*line);
        }
    }

    const LayoutFlowThread* flowThread = group().columnSet().flowThread();
    bool isHorizontalWritingMode = flowThread->isHorizontalWritingMode();

    // Look for breaks between and inside block-level children. Even if this is a block flow with
    // inline children, there may be interesting floats to examine here.
    for (const LayoutObject* child = box.slowFirstChild(); child; child = child->nextSibling()) {
        if (!child->isBox() || child->isInline())
            continue;
        const LayoutBox& childBox = toLayoutBox(*child);
        LayoutRect overflowRect = childBox.layoutOverflowRect();
        LayoutUnit childLogicalBottomWithOverflow = childBox.logicalTop() + (isHorizontalWritingMode ? overflowRect.maxY() : overflowRect.maxX());
        if (m_flowThreadOffset + childLogicalBottomWithOverflow <= group().logicalTopInFlowThread()) {
            // This child is fully above the fragmentainer group we're examining.
            continue;
        }
        LayoutUnit childLogicalTopWithOverflow = childBox.logicalTop() + (isHorizontalWritingMode ? overflowRect.y() : overflowRect.x());
        if (m_flowThreadOffset + childLogicalTopWithOverflow >= group().logicalBottomInFlowThread()) {
            // This child is fully below the fragmentainer group we're examining. We cannot just
            // stop here, though, thanks to negative margins. So keep looking.
            continue;
        }
        if (childBox.isOutOfFlowPositioned() || childBox.isColumnSpanAll())
            continue;

        // Tables are wicked. Both table rows and table cells are relative to their table section.
        LayoutUnit offsetForThisChild = childBox.isTableRow() ? LayoutUnit() : childBox.logicalTop();
        m_flowThreadOffset += offsetForThisChild;

        examineBoxAfterEntering(childBox);
        // Unless the child is unsplittable, or if the child establishes an inner multicol
        // container, we descend into its subtree for further examination.
        if (childBox.paginationBreakability() != LayoutBox::ForbidBreaks
            && (!childBox.isLayoutBlockFlow() || !toLayoutBlockFlow(childBox).multiColumnFlowThread()))
            traverseSubtree(childBox);
        examineBoxBeforeLeaving(childBox);

        m_flowThreadOffset -= offsetForThisChild;
    }
}
Example #2
0
static inline InlineFlowBox* flowBoxForLayoutObject(LayoutObject* layoutObject)
{
    if (!layoutObject)
        return nullptr;

    if (layoutObject->isLayoutBlock()) {
        // If we're given a block element, it has to be a LayoutSVGText.
        ASSERT(layoutObject->isSVGText());
        LayoutBlockFlow* layoutBlockFlow = toLayoutBlockFlow(layoutObject);

        // LayoutSVGText only ever contains a single line box.
        InlineFlowBox* flowBox = layoutBlockFlow->firstLineBox();
        ASSERT(flowBox == layoutBlockFlow->lastLineBox());
        return flowBox;
    }

    if (layoutObject->isLayoutInline()) {
        // We're given a LayoutSVGInline or objects that derive from it (LayoutSVGTSpan / LayoutSVGTextPath)
        LayoutInline* layoutInline = toLayoutInline(layoutObject);

        // LayoutSVGInline only ever contains a single line box.
        InlineFlowBox* flowBox = layoutInline->firstLineBox();
        ASSERT(flowBox == layoutInline->lastLineBox());
        return flowBox;
    }

    ASSERT_NOT_REACHED();
    return nullptr;
}
Example #3
0
void InitialColumnHeightFinder::examineBoxAfterEntering(const LayoutBox& box)
{
    ASSERT(isFirstAfterBreak(flowThreadOffset()) || !box.paginationStrut());
    if (box.hasForcedBreakBefore()) {
        addContentRun(flowThreadOffset());
    } else if (isFirstAfterBreak(flowThreadOffset())) {
        // This box is first after a soft break.
        recordStrutBeforeOffset(flowThreadOffset(), box.paginationStrut());
    }

    if (box.hasForcedBreakAfter())
        addContentRun(flowThreadOffset() + box.logicalHeight());

    if (box.paginationBreakability() != LayoutBox::AllowAnyBreaks) {
        LayoutUnit unsplittableLogicalHeight = box.logicalHeight();
        if (box.isFloating())
            unsplittableLogicalHeight += box.marginBefore() + box.marginAfter();
        m_tallestUnbreakableLogicalHeight = std::max(m_tallestUnbreakableLogicalHeight, unsplittableLogicalHeight);
    } else if (box.isLayoutBlockFlow()) {
        if (LayoutMultiColumnFlowThread* innerFlowThread = toLayoutBlockFlow(box).multiColumnFlowThread()) {
            LayoutUnit offsetInInnerFlowThread = flowThreadOffset() - innerFlowThread->blockOffsetInEnclosingFlowThread();
            LayoutUnit innerUnbreakableHeight = innerFlowThread->tallestUnbreakableLogicalHeight(offsetInInnerFlowThread);
            m_tallestUnbreakableLogicalHeight = std::max(m_tallestUnbreakableLogicalHeight, innerUnbreakableHeight);
        }
    }
}
Example #4
0
void ColumnBalancer::traverseSubtree(const LayoutBox& box) {
  if (box.childrenInline() && box.isLayoutBlockFlow()) {
    // Look for breaks between lines.
    traverseLines(toLayoutBlockFlow(box));
  }

  // Look for breaks between and inside block-level children. Even if this is a
  // block flow with inline children, there may be interesting floats to examine
  // here.
  traverseChildren(box);
}
Example #5
0
void MinimumSpaceShortageFinder::examineBoxAfterEntering(const LayoutBox& box)
{
    if (box.hasForcedBreakBefore())
        m_forcedBreaksCount++;
    if (box.hasForcedBreakAfter())
        m_forcedBreaksCount++;

    // Look for breaks before the child box.
    bool isFirstAfterBreak = this->isFirstAfterBreak(flowThreadOffset());
    ASSERT(isFirstAfterBreak || !box.paginationStrut());
    LayoutBox::PaginationBreakability breakability = box.paginationBreakability();
    if (isFirstAfterBreak && !box.hasForcedBreakBefore()) {
        // This box is first after a soft break.
        LayoutUnit strut = box.paginationStrut();
        // Figure out how much more space we would need to prevent it from being pushed to the next column.
        recordSpaceShortage(box.logicalHeight() - strut);
        if (breakability != LayoutBox::ForbidBreaks && m_pendingStrut == LayoutUnit::min()) {
            // We now want to look for the first piece of unbreakable content (e.g. a line or a
            // block-displayed image) inside this block. That ought to be a good candidate for
            // minimum space shortage; a much better one than reporting space shortage for the
            // entire block (which we'll also do (further down), in case we couldn't find anything
            // more suitable).
            m_pendingStrut = strut;
        }
    }

    if (breakability != LayoutBox::ForbidBreaks) {
        // See if this breakable box crosses column boundaries.
        LayoutUnit bottomInFlowThread = flowThreadOffset() + box.logicalHeight();
        if (isFirstAfterBreak || group().columnLogicalTopForOffset(flowThreadOffset()) != group().columnLogicalTopForOffset(bottomInFlowThread)) {
            // If the child crosses a column boundary, record space shortage, in case nothing
            // inside it has already done so. The column balancer needs to know by how much it
            // has to stretch the columns to make more content fit. If no breaks are reported
            // (but do occur), the balancer will have no clue. Only measure the space after the
            // last column boundary, in case it crosses more than one.
            LayoutUnit spaceUsedInLastColumn = bottomInFlowThread - group().columnLogicalTopForOffset(bottomInFlowThread);
            recordSpaceShortage(spaceUsedInLastColumn);
        }
    }

    // If this is an inner multicol container, look for space shortage inside it.
    if (!box.isLayoutBlockFlow())
        return;
    LayoutMultiColumnFlowThread* flowThread = toLayoutBlockFlow(box).multiColumnFlowThread();
    if (!flowThread)
        return;
    for (const LayoutMultiColumnSet* columnSet = flowThread->firstMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) {
        for (const MultiColumnFragmentainerGroup& row : columnSet->fragmentainerGroups()) {
            MinimumSpaceShortageFinder innerFinder(row);
            recordSpaceShortage(innerFinder.minimumSpaceShortage());
        }
    }
}
Example #6
0
// Bounds of the LayoutObject relative to the scroller's visible content rect.
static LayoutRect relativeBounds(const LayoutObject* layoutObject,
                                 const ScrollableArea* scroller) {
  LayoutRect localBounds;
  if (layoutObject->isBox()) {
    localBounds = toLayoutBox(layoutObject)->borderBoxRect();
    if (!layoutObject->hasOverflowClip()) {
      // borderBoxRect doesn't include overflow content and floats.
      LayoutUnit maxHeight =
          std::max(localBounds.height(),
                   toLayoutBox(layoutObject)->layoutOverflowRect().height());
      if (layoutObject->isLayoutBlockFlow() &&
          toLayoutBlockFlow(layoutObject)->containsFloats()) {
        // Note that lowestFloatLogicalBottom doesn't include floating
        // grandchildren.
        maxHeight = std::max(
            maxHeight,
            toLayoutBlockFlow(layoutObject)->lowestFloatLogicalBottom());
      }
      localBounds.setHeight(maxHeight);
    }
  } else if (layoutObject->isText()) {
    // TODO(skobes): Use first and last InlineTextBox only?
    for (InlineTextBox* box = toLayoutText(layoutObject)->firstTextBox(); box;
         box = box->nextTextBox())
      localBounds.unite(box->frameRect());
  } else {
    // Only LayoutBox and LayoutText are supported.
    ASSERT_NOT_REACHED();
  }

  LayoutRect relativeBounds = LayoutRect(
      scroller->localToVisibleContentQuad(FloatRect(localBounds), layoutObject)
          .boundingBox());

  return relativeBounds;
}
Example #7
0
String HTMLTextFormControlElement::valueWithHardLineBreaks() const
{
    // FIXME: It's not acceptable to ignore the HardWrap setting when there is no layoutObject.
    // While we have no evidence this has ever been a practical problem, it would be best to fix it some day.
    HTMLElement* innerText = innerEditorElement();
    if (!innerText || !isTextFormControl())
        return value();

    LayoutBlockFlow* layoutObject = toLayoutBlockFlow(innerText->layoutObject());
    if (!layoutObject)
        return value();

    Node* breakNode;
    unsigned breakOffset;
    RootInlineBox* line = layoutObject->firstRootBox();
    if (!line)
        return value();

    getNextSoftBreak(line, breakNode, breakOffset);

    StringBuilder result;
    for (Node& node : NodeTraversal::descendantsOf(*innerText)) {
        if (isHTMLBRElement(node)) {
            ASSERT(&node == innerText->lastChild());
            if (&node != innerText->lastChild())
                result.append(newlineCharacter);
        } else if (node.isTextNode()) {
            String data = toText(node).data();
            unsigned length = data.length();
            unsigned position = 0;
            while (breakNode == node && breakOffset <= length) {
                if (breakOffset > position) {
                    result.append(data, position, breakOffset - position);
                    position = breakOffset;
                    result.append(newlineCharacter);
                }
                getNextSoftBreak(line, breakNode, breakOffset);
            }
            result.append(data, position, length - position);
        }
        while (breakNode == node)
            getNextSoftBreak(line, breakNode, breakOffset);
    }
    return result.toString();
}
Example #8
0
void InitialColumnHeightFinder::examineBoxAfterEntering(
    const LayoutBox& box,
    LayoutUnit childLogicalHeight,
    EBreak previousBreakAfterValue) {
  if (m_lastBreakSeen > flowThreadOffset()) {
    // We have moved backwards. We're probably in a parallel flow, caused by
    // floats, sibling table cells, etc.
    m_lastBreakSeen = LayoutUnit();
  }
  if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) {
    if (box.needsForcedBreakBefore(previousBreakAfterValue)) {
      addContentRun(flowThreadOffset());
    } else if (isFirstAfterBreak(flowThreadOffset()) &&
               m_lastBreakSeen != flowThreadOffset()) {
      // This box is first after a soft break.
      m_lastBreakSeen = flowThreadOffset();
      recordStrutBeforeOffset(flowThreadOffset(), box.paginationStrut());
    }
  }

  if (box.getPaginationBreakability() != LayoutBox::AllowAnyBreaks) {
    m_tallestUnbreakableLogicalHeight =
        std::max(m_tallestUnbreakableLogicalHeight, childLogicalHeight);
    return;
  }
  // Need to examine inner multicol containers to find their tallest unbreakable
  // piece of content.
  if (!box.isLayoutBlockFlow())
    return;
  LayoutMultiColumnFlowThread* innerFlowThread =
      toLayoutBlockFlow(box).multiColumnFlowThread();
  if (!innerFlowThread || innerFlowThread->isLayoutPagedFlowThread())
    return;
  LayoutUnit offsetInInnerFlowThread =
      flowThreadOffset() -
      innerFlowThread->blockOffsetInEnclosingFragmentationContext();
  LayoutUnit innerUnbreakableHeight =
      innerFlowThread->tallestUnbreakableLogicalHeight(offsetInInnerFlowThread);
  m_tallestUnbreakableLogicalHeight =
      std::max(m_tallestUnbreakableLogicalHeight, innerUnbreakableHeight);
}
Example #9
0
void InitialColumnHeightFinder::examineBoxAfterEntering(
    const LayoutBox& box,
    EBreak previousBreakAfterValue) {
  if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) {
    if (box.needsForcedBreakBefore(previousBreakAfterValue)) {
      addContentRun(flowThreadOffset());
    } else {
      ASSERT(isFirstAfterBreak(flowThreadOffset()) || !box.paginationStrut());
      if (isFirstAfterBreak(flowThreadOffset())) {
        // This box is first after a soft break.
        recordStrutBeforeOffset(flowThreadOffset(), box.paginationStrut());
      }
    }
  }

  if (box.getPaginationBreakability() != LayoutBox::AllowAnyBreaks) {
    LayoutUnit unsplittableLogicalHeight = box.logicalHeight();
    if (box.isFloating())
      unsplittableLogicalHeight += box.marginBefore() + box.marginAfter();
    m_tallestUnbreakableLogicalHeight =
        std::max(m_tallestUnbreakableLogicalHeight, unsplittableLogicalHeight);
    return;
  }
  // Need to examine inner multicol containers to find their tallest unbreakable
  // piece of content.
  if (!box.isLayoutBlockFlow())
    return;
  LayoutMultiColumnFlowThread* innerFlowThread =
      toLayoutBlockFlow(box).multiColumnFlowThread();
  if (!innerFlowThread || innerFlowThread->isLayoutPagedFlowThread())
    return;
  LayoutUnit offsetInInnerFlowThread =
      flowThreadOffset() -
      innerFlowThread->blockOffsetInEnclosingFragmentationContext();
  LayoutUnit innerUnbreakableHeight =
      innerFlowThread->tallestUnbreakableLogicalHeight(offsetInInnerFlowThread);
  m_tallestUnbreakableLogicalHeight =
      std::max(m_tallestUnbreakableLogicalHeight, innerUnbreakableHeight);
}
bool LayoutMultiColumnFlowThread::descendantIsValidColumnSpanner(LayoutObject* descendant) const
{
    // This method needs to behave correctly in the following situations:
    // - When the descendant doesn't have a spanner placeholder but should have one (return true)
    // - When the descendant doesn't have a spanner placeholder and still should not have one (return false)
    // - When the descendant has a spanner placeholder but should no longer have one (return false)
    // - When the descendant has a spanner placeholder and should still have one (return true)

    // We assume that we're inside the flow thread. This function is not to be called otherwise.
    ASSERT(descendant->isDescendantOf(this));

    // The spec says that column-span only applies to in-flow block-level elements.
    if (descendant->style()->columnSpan() != ColumnSpanAll || !descendant->isBox() || descendant->isInline() || descendant->isFloatingOrOutOfFlowPositioned())
        return false;

    if (!descendant->containingBlock()->isLayoutBlockFlow()) {
        // Needs to be in a block-flow container, and not e.g. a table.
        return false;
    }

    // This looks like a spanner, but if we're inside something unbreakable or something that
    // establishes a new formatting context, it's not to be treated as one.
    for (LayoutBox* ancestor = toLayoutBox(descendant)->parentBox(); ancestor; ancestor = ancestor->containingBlock()) {
        if (ancestor->isLayoutFlowThread()) {
            ASSERT(ancestor == this);
            return true;
        }
        if (!ancestor->isLayoutBlockFlow())
            return false;
        const LayoutBlockFlow& ancestorBlockFlow = *toLayoutBlockFlow(ancestor);
        if (ancestorBlockFlow.createsNewFormattingContext() || ancestorBlockFlow.paginationBreakability() == ForbidBreaks)
            return false;
    }
    ASSERT_NOT_REACHED();
    return false;
}
Example #11
0
// We need to balance the benefit of subtree optimization and the cost of subtree display items.
// Only output subtree information if the block has multiple children or multiple line boxes.
static bool needsSubtreeRecorder(const LayoutBlock& layoutBlock)
{
    return (layoutBlock.firstChild() && layoutBlock.firstChild()->nextSibling())
        || (layoutBlock.isLayoutBlockFlow() && toLayoutBlockFlow(layoutBlock).firstLineBox() && toLayoutBlockFlow(layoutBlock).firstLineBox()->nextLineBox());
}
static inline bool isMultiColumnContainer(const LayoutObject& object)
{
    if (!object.isLayoutBlockFlow())
        return false;
    return toLayoutBlockFlow(object).multiColumnFlowThread();
}
Example #13
0
void ColumnBalancer::traverseChildren(const LayoutObject& object) {
  // The break-after value from the previous in-flow block-level object to be
  // joined with the break-before value of the next in-flow block-level sibling.
  EBreak previousBreakAfterValue = BreakAuto;

  for (const LayoutObject* child = object.slowFirstChild(); child;
       child = child->nextSibling()) {
    if (!child->isBox()) {
      // Keep traversing inside inlines. There may be floats there.
      if (child->isLayoutInline())
        traverseChildren(*child);
      continue;
    }

    const LayoutBox& childBox = toLayoutBox(*child);

    LayoutUnit borderEdgeOffset;
    LayoutUnit logicalTop = childBox.logicalTop();
    LayoutUnit logicalHeight = childBox.logicalHeightWithVisibleOverflow();
    // Floats' margins don't collapse with column boundaries, and we don't want
    // to break inside them, or separate them from the float's border box. Set
    // the offset to the margin-before edge (rather than border-before edge),
    // and include the block direction margins in the child height.
    if (childBox.isFloating()) {
      LayoutUnit marginBefore = childBox.marginBefore(object.style());
      LayoutUnit marginAfter = childBox.marginAfter(object.style());
      logicalHeight =
          std::max(logicalHeight, childBox.logicalHeight() + marginAfter);
      logicalTop -= marginBefore;
      logicalHeight += marginBefore;

      // As soon as we want to process content inside this child, though, we
      // need to get to its border-before edge.
      borderEdgeOffset = marginBefore;
    }

    if (m_flowThreadOffset + logicalTop + logicalHeight <=
        logicalTopInFlowThread()) {
      // This child is fully above the flow thread portion we're examining.
      continue;
    }
    if (m_flowThreadOffset + logicalTop >= logicalBottomInFlowThread()) {
      // This child is fully below the flow thread portion we're examining. We
      // cannot just stop here, though, thanks to negative margins.
      // So keep looking.
      continue;
    }
    if (childBox.isOutOfFlowPositioned() || childBox.isColumnSpanAll())
      continue;

    // Tables are wicked. Both table rows and table cells are relative to their
    // table section.
    LayoutUnit offsetForThisChild =
        childBox.isTableRow() ? LayoutUnit() : logicalTop;

    m_flowThreadOffset += offsetForThisChild;

    examineBoxAfterEntering(childBox, logicalHeight, previousBreakAfterValue);
    // Unless the child is unsplittable, or if the child establishes an inner
    // multicol container, we descend into its subtree for further examination.
    if (childBox.getPaginationBreakability() != LayoutBox::ForbidBreaks &&
        (!childBox.isLayoutBlockFlow() ||
         !toLayoutBlockFlow(childBox).multiColumnFlowThread())) {
      // We need to get to the border edge before processing content inside
      // this child. If the child is floated, we're currently at the margin
      // edge.
      m_flowThreadOffset += borderEdgeOffset;
      traverseSubtree(childBox);
      m_flowThreadOffset -= borderEdgeOffset;
    }
    previousBreakAfterValue = childBox.breakAfter();
    examineBoxBeforeLeaving(childBox, logicalHeight);

    m_flowThreadOffset -= offsetForThisChild;
  }
}
Example #14
0
void MinimumSpaceShortageFinder::examineBoxAfterEntering(
    const LayoutBox& box,
    LayoutUnit childLogicalHeight,
    EBreak previousBreakAfterValue) {
  LayoutBox::PaginationBreakability breakability =
      box.getPaginationBreakability();

  // Look for breaks before the child box.
  if (isLogicalTopWithinBounds(flowThreadOffset() - box.paginationStrut())) {
    if (box.needsForcedBreakBefore(previousBreakAfterValue)) {
      m_forcedBreaksCount++;
    } else {
      if (isFirstAfterBreak(flowThreadOffset())) {
        // This box is first after a soft break.
        LayoutUnit strut = box.paginationStrut();
        // Figure out how much more space we would need to prevent it from being
        // pushed to the next column.
        recordSpaceShortage(childLogicalHeight - strut);
        if (breakability != LayoutBox::ForbidBreaks &&
            m_pendingStrut == LayoutUnit::min()) {
          // We now want to look for the first piece of unbreakable content
          // (e.g. a line or a block-displayed image) inside this block. That
          // ought to be a good candidate for minimum space shortage; a much
          // better one than reporting space shortage for the entire block
          // (which we'll also do (further down), in case we couldn't find
          // anything more suitable).
          m_pendingStrut = strut;
        }
      }
    }
  }

  if (breakability != LayoutBox::ForbidBreaks) {
    // See if this breakable box crosses column boundaries.
    LayoutUnit bottomInFlowThread = flowThreadOffset() + childLogicalHeight;
    const MultiColumnFragmentainerGroup& group =
        groupAtOffset(flowThreadOffset());
    if (isFirstAfterBreak(flowThreadOffset()) ||
        group.columnLogicalTopForOffset(flowThreadOffset()) !=
            group.columnLogicalTopForOffset(bottomInFlowThread)) {
      // If the child crosses a column boundary, record space shortage, in case
      // nothing inside it has already done so. The column balancer needs to
      // know by how much it has to stretch the columns to make more content
      // fit. If no breaks are reported (but do occur), the balancer will have
      // no clue. Only measure the space after the last column boundary, in case
      // it crosses more than one.
      LayoutUnit spaceUsedInLastColumn =
          bottomInFlowThread -
          group.columnLogicalTopForOffset(bottomInFlowThread);
      recordSpaceShortage(spaceUsedInLastColumn);
    }
  }

  // If this is an inner multicol container, look for space shortage inside it.
  if (!box.isLayoutBlockFlow())
    return;
  LayoutMultiColumnFlowThread* flowThread =
      toLayoutBlockFlow(box).multiColumnFlowThread();
  if (!flowThread || flowThread->isLayoutPagedFlowThread())
    return;
  for (const LayoutMultiColumnSet* columnSet =
           flowThread->firstMultiColumnSet();
       columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) {
    // Establish an inner shortage finder for this column set in the inner
    // multicol container. We need to let it walk through all fragmentainer
    // groups in one go, or we'd miss the column boundaries between each
    // fragmentainer group. We need to record space shortage there too.
    MinimumSpaceShortageFinder innerFinder(
        *columnSet, columnSet->logicalTopInFlowThread(),
        columnSet->logicalBottomInFlowThread());
    recordSpaceShortage(innerFinder.minimumSpaceShortage());
  }
}
Example #15
0
void BlockPainter::paintObject(const PaintInfo& paintInfo,
                               const LayoutPoint& paintOffset) {
  const PaintPhase paintPhase = paintInfo.phase;

  if (shouldPaintSelfBlockBackground(paintPhase)) {
    if (m_layoutBlock.style()->visibility() == EVisibility::Visible &&
        m_layoutBlock.hasBoxDecorationBackground())
      m_layoutBlock.paintBoxDecorationBackground(paintInfo, paintOffset);
    // We're done. We don't bother painting any children.
    if (paintPhase == PaintPhaseSelfBlockBackgroundOnly)
      return;
  }

  if (paintInfo.paintRootBackgroundOnly())
    return;

  if (paintPhase == PaintPhaseMask &&
      m_layoutBlock.style()->visibility() == EVisibility::Visible) {
    m_layoutBlock.paintMask(paintInfo, paintOffset);
    return;
  }

  if (paintPhase == PaintPhaseClippingMask &&
      m_layoutBlock.style()->visibility() == EVisibility::Visible) {
    BoxPainter(m_layoutBlock).paintClippingMask(paintInfo, paintOffset);
    return;
  }

  if (paintPhase == PaintPhaseForeground && paintInfo.isPrinting())
    ObjectPainter(m_layoutBlock).addPDFURLRectIfNeeded(paintInfo, paintOffset);

  if (paintPhase != PaintPhaseSelfOutlineOnly) {
    Optional<ScopedPaintChunkProperties> m_scopedScrollProperty;
    Optional<ScrollRecorder> scrollRecorder;
    Optional<PaintInfo> scrolledPaintInfo;
    if (RuntimeEnabledFeatures::slimmingPaintV2Enabled()) {
      const auto* objectProperties = m_layoutBlock.paintProperties();
      if (auto* scroll =
              objectProperties ? objectProperties->scroll() : nullptr) {
        PaintChunkProperties properties(paintInfo.context.getPaintController()
                                            .currentPaintChunkProperties());
        auto* scrollTranslation = objectProperties->scrollTranslation();
        DCHECK(scrollTranslation);
        properties.transform = scrollTranslation;
        properties.scroll = scroll;
        m_scopedScrollProperty.emplace(
            paintInfo.context.getPaintController(), m_layoutBlock,
            DisplayItem::paintPhaseToDrawingType(paintPhase), properties);
        scrolledPaintInfo.emplace(paintInfo);
        scrolledPaintInfo->updateCullRect(
            scrollTranslation->matrix().toAffineTransform());
      }
    } else if (m_layoutBlock.hasOverflowClip()) {
      IntSize scrollOffset = m_layoutBlock.scrolledContentOffset();
      if (m_layoutBlock.layer()->scrollsOverflow() || !scrollOffset.isZero()) {
        scrollRecorder.emplace(paintInfo.context, m_layoutBlock, paintPhase,
                               scrollOffset);
        scrolledPaintInfo.emplace(paintInfo);
        AffineTransform transform;
        transform.translate(-scrollOffset.width(), -scrollOffset.height());
        scrolledPaintInfo->updateCullRect(transform);
      }
    }

    const PaintInfo& contentsPaintInfo =
        scrolledPaintInfo ? *scrolledPaintInfo : paintInfo;

    if (m_layoutBlock.isLayoutBlockFlow()) {
      BlockFlowPainter blockFlowPainter(toLayoutBlockFlow(m_layoutBlock));
      blockFlowPainter.paintContents(contentsPaintInfo, paintOffset);
      if (paintPhase == PaintPhaseFloat || paintPhase == PaintPhaseSelection ||
          paintPhase == PaintPhaseTextClip)
        blockFlowPainter.paintFloats(contentsPaintInfo, paintOffset);
    } else {
      paintContents(contentsPaintInfo, paintOffset);
    }
  }

  if (shouldPaintSelfOutline(paintPhase))
    ObjectPainter(m_layoutBlock).paintOutline(paintInfo, paintOffset);

  // If the caret's node's layout object's containing block is this block, and
  // the paint action is PaintPhaseForeground, then paint the caret.
  if (paintPhase == PaintPhaseForeground && m_layoutBlock.hasCaret())
    paintCarets(paintInfo, paintOffset);
}