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()); } } }
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()); } }
LayoutSize MultiColumnFragmentainerGroup::flowThreadTranslationAtOffset( LayoutUnit offsetInFlowThread, LayoutBox::PageBoundaryRule rule, CoordinateSpaceConversion mode) const { LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); // A column out of range doesn't have a flow thread portion, so we need to // clamp to make sure that we stay within the actual columns. This means that // content in the overflow area will be mapped to the last actual column, // instead of being mapped to an imaginary column further ahead. unsigned columnIndex = offsetInFlowThread >= logicalBottomInFlowThread() ? actualColumnCount() - 1 : columnIndexAtOffset(offsetInFlowThread, rule); LayoutRect portionRect(flowThreadPortionRectAt(columnIndex)); flowThread->flipForWritingMode(portionRect); portionRect.moveBy(flowThread->topLeftLocation()); LayoutRect columnRect(columnRectAt(columnIndex)); columnRect.move(offsetFromColumnSet()); m_columnSet.flipForWritingMode(columnRect); columnRect.moveBy(m_columnSet.topLeftLocation()); LayoutSize translationRelativeToFlowThread = columnRect.location() - portionRect.location(); if (mode == CoordinateSpaceConversion::Containing) return translationRelativeToFlowThread; LayoutSize enclosingTranslation; if (LayoutMultiColumnFlowThread* enclosingFlowThread = flowThread->enclosingFlowThread()) { const MultiColumnFragmentainerGroup& firstRow = flowThread->firstMultiColumnSet()->firstFragmentainerGroup(); // Translation that would map points in the coordinate space of the // outermost flow thread to visual points in the first column in the first // fragmentainer group (row) in our multicol container. LayoutSize enclosingTranslationOrigin = enclosingFlowThread->flowThreadTranslationAtOffset( firstRow.blockOffsetInEnclosingFragmentationContext(), LayoutBox::AssociateWithLatterPage, mode); // Translation that would map points in the coordinate space of the // outermost flow thread to visual points in the first column in this // fragmentainer group. enclosingTranslation = enclosingFlowThread->flowThreadTranslationAtOffset( blockOffsetInEnclosingFragmentationContext(), LayoutBox::AssociateWithLatterPage, mode); // What we ultimately return from this method is a translation that maps // points in the coordinate space of our flow thread to a visual point in a // certain column in this fragmentainer group. We had to go all the way up // to the outermost flow thread, since this fragmentainer group may be in a // different outer column than the first outer column that this multicol // container lives in. It's the visual distance between the first // fragmentainer group and this fragmentainer group that we need to add to // the translation. enclosingTranslation -= enclosingTranslationOrigin; } return enclosingTranslation + translationRelativeToFlowThread; }