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; } }
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; }
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); } } }
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); }
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()); } } }
// 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; }
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(); }
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); }
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; }
// 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(); }
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; } }
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()); } }
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); }