LayoutSize RenderMultiColumnFlowThread::physicalTranslationOffsetFromFlowToRegion(const RenderRegion* renderRegion, const LayoutUnit logicalOffset) const { // Now that we know which multicolumn set we hit, we need to get the appropriate translation offset for the column. const RenderMultiColumnSet* columnSet = toRenderMultiColumnSet(renderRegion); LayoutPoint translationOffset = columnSet->columnTranslationForOffset(logicalOffset); // Now we know how we want the rect to be translated into the region. At this point we're converting // back to physical coordinates. if (style().isFlippedBlocksWritingMode()) { LayoutRect portionRect(columnSet->flowThreadPortionRect()); LayoutRect columnRect = columnSet->columnRectAt(0); LayoutUnit physicalDeltaFromPortionBottom = logicalHeight() - columnSet->logicalBottomInFlowThread(); if (isHorizontalWritingMode()) columnRect.setHeight(portionRect.height()); else columnRect.setWidth(portionRect.width()); columnSet->flipForWritingMode(columnRect); if (isHorizontalWritingMode()) translationOffset.move(0, columnRect.y() - portionRect.y() - physicalDeltaFromPortionBottom); else translationOffset.move(columnRect.x() - portionRect.x() - physicalDeltaFromPortionBottom, 0); } return LayoutSize(translationOffset.x(), translationOffset.y()); }
RenderObject* RenderMultiColumnBlock::layoutSpecialExcludedChild(bool relayoutChildren) { if (!m_flowThread) return 0; // Update the dimensions of our regions before we lay out the flow thread. // FIXME: Eventually this is going to get way more complicated, and we will be destroying regions // instead of trying to keep them around. bool shouldInvalidateRegions = false; for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) { if (childBox == m_flowThread) continue; if (relayoutChildren || childBox->needsLayout()) { if (!m_inBalancingPass && childBox->isRenderMultiColumnSet()) toRenderMultiColumnSet(childBox)->prepareForLayout(); shouldInvalidateRegions = true; } } if (shouldInvalidateRegions) m_flowThread->invalidateRegions(); if (relayoutChildren) m_flowThread->setChildNeedsLayout(MarkOnlyThis); setLogicalTopForChild(*m_flowThread, borderAndPaddingBefore()); m_flowThread->layoutIfNeeded(); determineLogicalLeftPositionForChild(*m_flowThread); return m_flowThread; }
void RenderMultiColumnFlowThread::autoGenerateRegionsToBlockOffset(LayoutUnit offset) { // This function ensures we have the correct column set information at all times. // For a simple multi-column layout in continuous media, only one column set child is required. // Once a column is nested inside an enclosing pagination context, the number of column sets // required becomes 2n-1, where n is the total number of nested pagination contexts. For example: // // Column layout with no enclosing pagination model = 2 * 1 - 1 = 1 column set. // Columns inside pages = 2 * 2 - 1 = 3 column sets (bottom of first page, all the subsequent pages, then the last page). // Columns inside columns inside pages = 2 * 3 - 1 = 5 column sets. // // In addition, column spans will force a column set to "split" into before/after sets around the spanning element. // // Finally, we will need to deal with columns inside regions. If regions have variable widths, then there will need // to be unique column sets created inside any region whose width is different from its surrounding regions. This is // actually pretty similar to the spanning case, in that we break up the column sets whenever the width varies. // // FIXME: Parent fragmentation contexts might require us to insert additional column sets here, // but we don't worry about it for now. This matches the old multi-column code. Right now our // goal is just feature parity with the old multi-column code so that we can switch over to the // new code as soon as possible. The one column set that we need has already been made during // render tree creation, so there's nothing to do here, for the time being. (void)offset; // hide warning ASSERT(toRenderMultiColumnSet(regionAtBlockOffset(0, offset, true, DisallowRegionAutoGeneration))); }
void RenderMultiColumnBlock::ensureColumnSets() { // This function ensures we have the correct column set information before we get into layout. // For a simple multi-column layout in continuous media, only one column set child is required. // Once a column is nested inside an enclosing pagination context, the number of column sets // required becomes 2n-1, where n is the total number of nested pagination contexts. For example: // // Column layout with no enclosing pagination model = 2 * 1 - 1 = 1 column set. // Columns inside pages = 2 * 2 - 1 = 3 column sets (bottom of first page, all the subsequent pages, then the last page). // Columns inside columns inside pages = 2 * 3 - 1 = 5 column sets. // // In addition, column spans will force a column set to "split" into before/after sets around the spanning region. // // Finally, we will need to deal with columns inside regions. If regions have variable widths, then there will need // to be unique column sets created inside any region whose width is different from its surrounding regions. This is // actually pretty similar to the spanning case, in that we break up the column sets whenever the width varies. // // FIXME: For now just make one column set. This matches the old multi-column code. // Right now our goal is just feature parity with the old multi-column code so that we can switch over to the // new code as soon as possible. if (!flowThread()) return; RenderMultiColumnSet* columnSet = firstChild()->isRenderMultiColumnSet() ? toRenderMultiColumnSet(firstChild()) : 0; if (!columnSet) { columnSet = RenderMultiColumnSet::createAnonymous(flowThread()); columnSet->setStyle(RenderStyle::createAnonymousStyleWithDisplay(style(), BLOCK)); RenderBlock::addChild(columnSet, firstChild()); } columnSet->setRequiresBalancing(requiresBalancing()); }
RenderMultiColumnSet* RenderMultiColumnSet::previousSiblingMultiColumnSet() const { for (RenderObject* sibling = previousSibling(); sibling; sibling = sibling->previousSibling()) { if (sibling->isRenderMultiColumnSet()) return toRenderMultiColumnSet(sibling); } return 0; }
RenderMultiColumnSet* RenderMultiColumnFlowThread::lastMultiColumnSet() const { for (RenderObject* sibling = multiColumnBlockFlow()->lastChild(); sibling; sibling = sibling->previousSibling()) { if (sibling->isRenderMultiColumnSet()) return toRenderMultiColumnSet(sibling); } return nullptr; }
RenderMultiColumnSet* RenderMultiColumnFlowThread::firstMultiColumnSet() const { for (RenderObject* sibling = nextSibling(); sibling; sibling = sibling->nextSibling()) { if (sibling->isRenderMultiColumnSet()) return toRenderMultiColumnSet(sibling); } return nullptr; }
void RenderMultiColumnFlowThread::setRegionRangeForBox(const RenderBox* box, RenderRegion* startRegion, RenderRegion* endRegion) { // Some column sets may have zero height, which means that two or more sets may start at the // exact same flow thread position, which means that some parts of the code may believe that a // given box lives in sets that it doesn't really live in. Make some adjustments here and // include such sets if they are adjacent to the start and/or end regions. for (RenderMultiColumnSet* columnSet = toRenderMultiColumnSet(startRegion)->previousSiblingMultiColumnSet(); columnSet; columnSet = columnSet->previousSiblingMultiColumnSet()) { if (columnSet->logicalHeightInFlowThread()) break; startRegion = columnSet; } for (RenderMultiColumnSet* columnSet = toRenderMultiColumnSet(startRegion)->nextSiblingMultiColumnSet(); columnSet; columnSet = columnSet->nextSiblingMultiColumnSet()) { if (columnSet->logicalHeightInFlowThread()) break; endRegion = columnSet; } RenderFlowThread::setRegionRangeForBox(box, startRegion, endRegion); }
bool RenderMultiColumnFlowThread::addForcedRegionBreak(const RenderBlock* block, LayoutUnit offset, RenderBox* /*breakChild*/, bool /*isBefore*/, LayoutUnit* offsetBreakAdjustment) { if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlockOffset(block, offset))) { multicolSet->addForcedBreak(offset); if (offsetBreakAdjustment) *offsetBreakAdjustment = pageLogicalHeightForOffset(offset) ? pageRemainingLogicalHeightForOffset(offset, IncludePageBoundary) : LayoutUnit::fromPixel(0); return true; } return false; }
void RenderMultiColumnFlowThread::addRegionToThread(RenderRegion* renderRegion) { RenderMultiColumnSet* columnSet = toRenderMultiColumnSet(renderRegion); if (RenderMultiColumnSet* nextSet = columnSet->nextSiblingMultiColumnSet()) { RenderRegionList::iterator it = m_regionList.find(nextSet); ASSERT(it != m_regionList.end()); m_regionList.insertBefore(it, columnSet); } else m_regionList.add(columnSet); renderRegion->setIsValid(true); }
void RenderMultiColumnFlowThread::flowThreadDescendantBoxLaidOut(RenderBox* descendant) { if (!descendant->isRenderMultiColumnSpannerPlaceholder()) return; RenderMultiColumnSpannerPlaceholder* placeholder = toRenderMultiColumnSpannerPlaceholder(descendant); RenderBlock* container = placeholder->containingBlock(); for (RenderBox* prev = previousColumnSetOrSpannerSiblingOf(placeholder->spanner()); prev; prev = previousColumnSetOrSpannerSiblingOf(prev)) { if (prev->isRenderMultiColumnSet()) { toRenderMultiColumnSet(prev)->endFlow(container, placeholder->logicalTop()); break; } } for (RenderBox* next = nextColumnSetOrSpannerSiblingOf(placeholder->spanner()); next; next = nextColumnSetOrSpannerSiblingOf(next)) { if (next->isRenderMultiColumnSet()) { m_lastSetWorkedOn = toRenderMultiColumnSet(next); m_lastSetWorkedOn->beginFlow(container); break; } } }
void RenderMultiColumnFlowThread::layout() { ASSERT(!m_inLayout); m_inLayout = true; m_lastSetWorkedOn = nullptr; if (RenderBox* first = firstColumnSetOrSpanner()) { if (first->isRenderMultiColumnSet()) { m_lastSetWorkedOn = toRenderMultiColumnSet(first); m_lastSetWorkedOn->beginFlow(this); } } RenderFlowThread::layout(); if (RenderMultiColumnSet* lastSet = lastMultiColumnSet()) { if (!nextColumnSetOrSpannerSiblingOf(lastSet)) lastSet->endFlow(this, logicalHeight()); lastSet->expandToEncompassFlowThreadContentsIfNeeded(); } m_inLayout = false; m_lastSetWorkedOn = nullptr; }
bool RenderMultiColumnBlock::relayoutForPagination(bool, LayoutUnit, LayoutStateMaintainer& statePusher) { if (m_inBalancingPass || !requiresBalancing()) return false; m_inBalancingPass = true; // Prevent re-entering this method (and recursion into layout). bool needsRelayout; bool neededRelayout = false; bool firstPass = true; do { // Column heights may change here because of balancing. We may have to do multiple layout // passes, depending on how the contents is fitted to the changed column heights. In most // cases, laying out again twice or even just once will suffice. Sometimes we need more // passes than that, though, but the number of retries should not exceed the number of // columns, unless we have a bug. needsRelayout = false; for (RenderBox* childBox = firstChildBox(); childBox; childBox = childBox->nextSiblingBox()) if (childBox != m_flowThread && childBox->isRenderMultiColumnSet()) { RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(childBox); if (multicolSet->calculateBalancedHeight(firstPass)) { multicolSet->setChildNeedsLayout(MarkOnlyThis); needsRelayout = true; } } if (needsRelayout) { // Layout again. Column balancing resulted in a new height. neededRelayout = true; m_flowThread->setChildNeedsLayout(MarkOnlyThis); setChildNeedsLayout(MarkOnlyThis); if (firstPass) statePusher.pop(); layoutBlock(false); } firstPass = false; } while (needsRelayout); m_inBalancingPass = false; return neededRelayout; }
void RenderMultiColumnFlowThread::autoGenerateRegionsToBlockOffset(LayoutUnit /*offset*/) { // This function ensures we have the correct column set information at all times. // For a simple multi-column layout in continuous media, only one column set child is required. // Once a column is nested inside an enclosing pagination context, the number of column sets // required becomes 2n-1, where n is the total number of nested pagination contexts. For example: // // Column layout with no enclosing pagination model = 2 * 1 - 1 = 1 column set. // Columns inside pages = 2 * 2 - 1 = 3 column sets (bottom of first page, all the subsequent pages, then the last page). // Columns inside columns inside pages = 2 * 3 - 1 = 5 column sets. // // In addition, column spans will force a column set to "split" into before/after sets around the spanning element. // // Finally, we will need to deal with columns inside regions. If regions have variable widths, then there will need // to be unique column sets created inside any region whose width is different from its surrounding regions. This is // actually pretty similar to the spanning case, in that we break up the column sets whenever the width varies. // // FIXME: For now just make one column set. This matches the old multi-column code. // Right now our goal is just feature parity with the old multi-column code so that we can switch over to the // new code as soon as possible. RenderMultiColumnSet* firstSet = toRenderMultiColumnSet(firstRegion()); if (firstSet) return; invalidateRegions(); RenderBlockFlow* parentBlock = toRenderBlockFlow(parent()); firstSet = new RenderMultiColumnSet(*this, RenderStyle::createAnonymousStyleWithDisplay(&parentBlock->style(), BLOCK)); firstSet->initializeStyle(); parentBlock->RenderBlock::addChild(firstSet); // Even though we aren't placed yet, we can go ahead and set up our size. At this point we're // typically in the middle of laying out the thread, attempting to paginate, and we need to do // some rudimentary "layout" of the set now, so that pagination will work. firstSet->prepareForLayout(); validateRegions(); }
RenderRegion* RenderMultiColumnFlowThread::mapFromFlowToRegion(TransformState& transformState) const { if (!hasValidRegionInfo()) return nullptr; // Get back into our local flow thread space. LayoutRect boxRect = transformState.mappedQuad().enclosingBoundingBox(); flipForWritingMode(boxRect); // FIXME: We need to refactor RenderObject::absoluteQuads to be able to split the quads across regions, // for now we just take the center of the mapped enclosing box and map it to a column. LayoutPoint center = boxRect.center(); LayoutUnit centerOffset = isHorizontalWritingMode() ? center.y() : center.x(); RenderRegion* renderRegion = const_cast<RenderMultiColumnFlowThread*>(this)->regionAtBlockOffset(this, centerOffset, true, DisallowRegionAutoGeneration); if (!renderRegion) return nullptr; // Now that we know which multicolumn set we hit, we need to get the appropriate translation offset for the column. RenderMultiColumnSet* columnSet = toRenderMultiColumnSet(renderRegion); LayoutPoint translationOffset = columnSet->columnTranslationForOffset(centerOffset); // Now we know how we want the rect to be translated into the region. LayoutRect flippedRegionRect(renderRegion->flowThreadPortionRect()); if (isHorizontalWritingMode()) flippedRegionRect.setHeight(columnSet->computedColumnHeight()); else flippedRegionRect.setWidth(columnSet->computedColumnHeight()); flipForWritingMode(flippedRegionRect); flippedRegionRect.moveBy(-translationOffset); // There is an additional offset to apply, which is the offset of the region within the multi-column space. transformState.move(renderRegion->contentBoxRect().location() - flippedRegionRect.location()); return renderRegion; }
void RenderMultiColumnFlowThread::setPageBreak(const RenderBlock* block, LayoutUnit offset, LayoutUnit spaceShortage) { if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlockOffset(block, offset))) multicolSet->recordSpaceShortage(spaceShortage); }
void RenderMultiColumnFlowThread::updateMinimumPageHeight(const RenderBlock* block, LayoutUnit offset, LayoutUnit minHeight) { if (RenderMultiColumnSet* multicolSet = toRenderMultiColumnSet(regionAtBlockOffset(block, offset))) multicolSet->updateMinimumColumnHeight(minHeight); }