LayoutSize LayoutMultiColumnFlowThread::flowThreadTranslationAtOffset(LayoutUnit offsetInFlowThread) const { LayoutMultiColumnSet* columnSet = columnSetAtBlockOffset(offsetInFlowThread); if (!columnSet) return LayoutSize(0, 0); return columnSet->flowThreadTranslationAtOffset(offsetInFlowThread); }
void LayoutMultiColumnFlowThread::appendNewFragmentainerGroupIfNeeded(LayoutUnit offsetInFlowThread) { if (!isPageLogicalHeightKnown()) { // If we have no clue about the height of the multicol container, bail. This situation // occurs initially when an auto-height multicol container is nested inside another // auto-height multicol container. We need at least an estimated height of the outer // multicol container before we can check what an inner fragmentainer group has room for. // Its height is indefinite for now. return; } LayoutMultiColumnSet* columnSet = columnSetAtBlockOffset(offsetInFlowThread); if (columnSet->isInitialHeightCalculated()) { // We only insert additional fragmentainer groups in the initial layout pass. We only want // to balance columns in the last fragmentainer group (if we need to balance at all), so we // want that last fragmentainer group to be the same one in all layout passes that follow. return; } if (!columnSet->hasFragmentainerGroupForColumnAt(offsetInFlowThread)) { FragmentationContext* enclosingFragmentationContext = this->enclosingFragmentationContext(); if (!enclosingFragmentationContext) return; // Not nested. We'll never need more rows than the one we already have then. ASSERT(!isLayoutPagedFlowThread()); // We have run out of columns here, so we add another row to hold more columns. When we add // a new row, it implicitly means that we're inserting another column in our enclosing // multicol container. That in turn may mean that we've run out of columns there too. const MultiColumnFragmentainerGroup& newRow = columnSet->appendNewFragmentainerGroup(); if (LayoutMultiColumnFlowThread* enclosingFlowThread = enclosingFragmentationContext->associatedFlowThread()) enclosingFlowThread->appendNewFragmentainerGroupIfNeeded(newRow.blockOffsetInEnclosingFragmentationContext()); } }
void LayoutMultiColumnFlowThread::appendNewFragmentainerGroupIfNeeded(LayoutUnit bottomOffsetInFlowThread) { if (!isPageLogicalHeightKnown()) { // If we have no clue about the height of the multicol container, bail. This situation // occurs initially when an auto-height multicol container is nested inside another // auto-height multicol container. We need at least an estimated height of the outer // multicol container before we can check what an inner fragmentainer group has room for. // Its height is indefinite for now. return; } // TODO(mstensho): bottomOffsetInFlowThread is an endpoint-exclusive offset, i.e. the offset // just after the bottom of some object. So, ideally, columnSetAtBlockOffset() should be // informed about this (i.e. take a PageBoundaryRule argument). This is not the only place with // this issue; see also pageRemainingLogicalHeightForOffset(). LayoutMultiColumnSet* columnSet = columnSetAtBlockOffset(bottomOffsetInFlowThread); if (columnSet->isInitialHeightCalculated()) { // We only insert additional fragmentainer groups in the initial layout pass. We only want // to balance columns in the last fragmentainer group (if we need to balance at all), so we // want that last fragmentainer group to be the same one in all layout passes that follow. return; } if (!columnSet->hasFragmentainerGroupForColumnAt(bottomOffsetInFlowThread)) { FragmentationContext* enclosingFragmentationContext = this->enclosingFragmentationContext(); if (!enclosingFragmentationContext) return; // Not nested. We'll never need more rows than the one we already have then. ASSERT(!isLayoutPagedFlowThread()); // We have run out of columns here, so we add another row to hold more columns. When we add // a new row, it implicitly means that we're inserting another column in our enclosing // multicol container. That in turn may mean that we've run out of columns there too. const MultiColumnFragmentainerGroup& newRow = columnSet->appendNewFragmentainerGroup(); if (LayoutMultiColumnFlowThread* enclosingFlowThread = enclosingFragmentationContext->associatedFlowThread()) enclosingFlowThread->appendNewFragmentainerGroupIfNeeded(newRow.blockOffsetInEnclosingFragmentationContext() + newRow.logicalHeight()); } }
LayoutUnit RenderFlowThread::pageLogicalHeightForOffset(LayoutUnit offset) { RenderMultiColumnSet* columnSet = columnSetAtBlockOffset(offset); if (!columnSet) return 0; return columnSet->pageLogicalHeight(); }
void RenderFlowThread::setRegionRangeForBox(const RenderBox* box, LayoutUnit offsetFromLogicalTopOfFirstPage) { if (!hasRegions()) return; // FIXME: Not right for differing writing-modes. RenderMultiColumnSet* startColumnSet = columnSetAtBlockOffset(offsetFromLogicalTopOfFirstPage); RenderMultiColumnSet* endColumnSet = columnSetAtBlockOffset(offsetFromLogicalTopOfFirstPage + box->logicalHeight()); RenderMultiColumnSetRangeMap::iterator it = m_multiColumnSetRangeMap.find(box); if (it == m_multiColumnSetRangeMap.end()) { m_multiColumnSetRangeMap.set(box, RenderMultiColumnSetRange(startColumnSet, endColumnSet)); return; } // If nothing changed, just bail. RenderMultiColumnSetRange& range = it->value; if (range.startColumnSet() == startColumnSet && range.endColumnSet() == endColumnSet) return; range.setRange(startColumnSet, endColumnSet); }
LayoutUnit RenderFlowThread::pageRemainingLogicalHeightForOffset(LayoutUnit offset, PageBoundaryRule pageBoundaryRule) { RenderMultiColumnSet* columnSet = columnSetAtBlockOffset(offset); if (!columnSet) return 0; LayoutUnit pageLogicalTop = columnSet->pageLogicalTopForOffset(offset); LayoutUnit pageLogicalHeight = columnSet->pageLogicalHeight(); LayoutUnit pageLogicalBottom = pageLogicalTop + pageLogicalHeight; LayoutUnit remainingHeight = pageLogicalBottom - offset; if (pageBoundaryRule == IncludePageBoundary) { // If IncludePageBoundary is set, the line exactly on the top edge of a // columnSet will act as being part of the previous columnSet. remainingHeight = intMod(remainingHeight, pageLogicalHeight); } return remainingHeight; }
LayoutUnit LayoutMultiColumnFlowThread::tallestUnbreakableLogicalHeight(LayoutUnit offsetInFlowThread) const { if (LayoutMultiColumnSet* multicolSet = columnSetAtBlockOffset(offsetInFlowThread)) return multicolSet->tallestUnbreakableLogicalHeight(); return LayoutUnit(); }
LayoutUnit RenderFlowThread::pageLogicalTopForOffset(LayoutUnit offset) { RenderMultiColumnSet* columnSet = columnSetAtBlockOffset(offset); return columnSet ? columnSet->pageLogicalTopForOffset(offset) : LayoutUnit(); }
LayoutPoint RenderFlowThread::adjustedPositionRelativeToOffsetParent(const RenderBoxModelObject& boxModelObject, const LayoutPoint& startPoint) { LayoutPoint referencePoint = startPoint; // FIXME: This needs to be adapted for different writing modes inside the flow thread. RenderMultiColumnSet* startColumnSet = columnSetAtBlockOffset(referencePoint.y()); if (startColumnSet) { // Take into account the offset coordinates of the columnSet. RenderObject* currObject = startColumnSet; RenderObject* currOffsetParentRenderer; Element* currOffsetParentElement; while ((currOffsetParentElement = currObject->offsetParent()) && (currOffsetParentRenderer = currOffsetParentElement->renderer())) { if (currObject->isBoxModelObject()) referencePoint.move(toRenderBoxModelObject(currObject)->offsetLeft(), toRenderBoxModelObject(currObject)->offsetTop()); // Since we're looking for the offset relative to the body, we must also // take into consideration the borders of the columnSet's offsetParent. if (currOffsetParentRenderer->isBox() && !currOffsetParentRenderer->isBody()) referencePoint.move(toRenderBox(currOffsetParentRenderer)->borderLeft(), toRenderBox(currOffsetParentRenderer)->borderTop()); currObject = currOffsetParentRenderer; } // We need to check if any of this box's containing blocks start in a different columnSet // and if so, drop the object's top position (which was computed relative to its containing block // and is no longer valid) and recompute it using the columnSet in which it flows as reference. bool wasComputedRelativeToOtherRegion = false; const RenderBlock* objContainingBlock = boxModelObject.containingBlock(); while (objContainingBlock) { // Check if this object is in a different columnSet. RenderMultiColumnSet* parentStartRegion = 0; RenderMultiColumnSet* parentEndRegion = 0; getRegionRangeForBox(objContainingBlock, parentStartRegion, parentEndRegion); if (parentStartRegion && parentStartRegion != startColumnSet) { wasComputedRelativeToOtherRegion = true; break; } objContainingBlock = objContainingBlock->containingBlock(); } if (wasComputedRelativeToOtherRegion) { // Get the logical top coordinate of the current object. LayoutUnit top = 0; if (boxModelObject.isRenderBlock()) { top = toRenderBlock(&boxModelObject)->offsetFromLogicalTopOfFirstPage(); } else { if (boxModelObject.containingBlock()) top = boxModelObject.containingBlock()->offsetFromLogicalTopOfFirstPage(); if (boxModelObject.isBox()) top += toRenderBox(&boxModelObject)->topLeftLocation().y(); else if (boxModelObject.isRenderInline()) top -= toRenderInline(&boxModelObject)->borderTop(); } // Get the logical top of the columnSet this object starts in // and compute the object's top, relative to the columnSet's top. LayoutUnit regionLogicalTop = startColumnSet->pageLogicalTopForOffset(top); LayoutUnit topRelativeToRegion = top - regionLogicalTop; referencePoint.setY(startColumnSet->offsetTop() + topRelativeToRegion); // Since the top has been overriden, check if the // relative positioning must be reconsidered. if (boxModelObject.isRelPositioned()) referencePoint.move(0, boxModelObject.relativePositionOffset().height()); } // Since we're looking for the offset relative to the body, we must also // take into consideration the borders of the columnSet. referencePoint.move(startColumnSet->borderLeft(), startColumnSet->borderTop()); } return referencePoint; }