LayoutUnit LayoutMultiColumnSet::columnGap() const { LayoutBlockFlow* parentBlock = multiColumnBlockFlow(); if (parentBlock->style()->hasNormalColumnGap()) return parentBlock->style()->fontDescription().computedPixelSize(); // "1em" is recommended as the normal gap setting. Matches <p> margins. return parentBlock->style()->columnGap(); }
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 LayoutMultiColumnFlowThread::evacuateAndDestroy() { LayoutBlockFlow* multicolContainer = multiColumnBlockFlow(); m_isBeingEvacuated = true; // Remove all sets and spanners. while (LayoutBox* columnBox = firstMultiColumnBox()) { ASSERT(columnBox->isAnonymous()); columnBox->destroy(); } ASSERT(!previousSibling()); ASSERT(!nextSibling()); // Finally we can promote all flow thread's children. Before we move them to the flow thread's // container, we need to unregister the flow thread, so that they aren't just re-added again to // the flow thread that we're trying to empty. multicolContainer->resetMultiColumnFlowThread(); moveAllChildrenTo(multicolContainer, true); // We used to manually nuke the line box tree here, but that should happen automatically when // moving children around (the code above). ASSERT(!firstLineBox()); destroy(); }
inline static float availableWidthAtOffset(const LayoutBlockFlow& block, const LayoutUnit& offset, bool shouldIndentText, float& newLineLeft, float& newLineRight, const LayoutUnit& lineHeight = 0) { newLineLeft = block.logicalLeftOffsetForLine(offset, shouldIndentText, lineHeight).toFloat(); newLineRight = block.logicalRightOffsetForLine(offset, shouldIndentText, lineHeight).toFloat(); return std::max(0.0f, newLineRight - newLineLeft); }
LayoutUnit MultiColumnFragmentainerGroup::calculateMaxColumnHeight() const { LayoutBlockFlow* multicolBlock = m_columnSet.multiColumnBlockFlow(); const ComputedStyle& multicolStyle = multicolBlock->styleRef(); LayoutMultiColumnFlowThread* flowThread = m_columnSet.multiColumnFlowThread(); LayoutUnit availableHeight = flowThread->columnHeightAvailable(); LayoutUnit maxColumnHeight = availableHeight ? availableHeight : LayoutUnit::max(); if (!multicolStyle.logicalMaxHeight().isMaxSizeNone()) { LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(MaxSize, multicolStyle.logicalMaxHeight(), -1); if (logicalMaxHeight != -1 && maxColumnHeight > logicalMaxHeight) maxColumnHeight = logicalMaxHeight; } LayoutUnit maxHeight = heightAdjustedForRowOffset(maxColumnHeight); if (LayoutMultiColumnFlowThread* enclosingFlowThread = flowThread->enclosingFlowThread()) { if (enclosingFlowThread->isPageLogicalHeightKnown()) { // We're nested inside another fragmentation context whose fragmentainer heights are // known. This constrains the max height. LayoutUnit remainingOuterLogicalHeight = enclosingFlowThread->pageRemainingLogicalHeightForOffset(blockOffsetInEnclosingFlowThread(), LayoutBlock::AssociateWithLatterPage); ASSERT(remainingOuterLogicalHeight > 0); if (maxHeight > remainingOuterLogicalHeight) maxHeight = remainingOuterLogicalHeight; } } return maxHeight; }
void LayoutMultiColumnFlowThread::populate() { LayoutBlockFlow* multicolContainer = multiColumnBlockFlow(); ASSERT(!nextSibling()); // Reparent children preceding the flow thread into the flow thread. It's multicol content // now. At this point there's obviously nothing after the flow thread, but layoutObjects (column // sets and spanners) will be inserted there as we insert elements into the flow thread. multicolContainer->moveChildrenTo(this, multicolContainer->firstChild(), this, true); }
void LayoutMultiColumnFlowThread::createAndInsertMultiColumnSet(LayoutBox* insertBefore) { LayoutBlockFlow* multicolContainer = multiColumnBlockFlow(); LayoutMultiColumnSet* newSet = LayoutMultiColumnSet::createAnonymous(*this, multicolContainer->styleRef()); multicolContainer->LayoutBlock::addChild(newSet, insertBefore); invalidateColumnSets(); // We cannot handle immediate column set siblings (and there's no need for it, either). // There has to be at least one spanner separating them. ASSERT(!newSet->previousSiblingMultiColumnBox() || !newSet->previousSiblingMultiColumnBox()->isLayoutMultiColumnSet()); ASSERT(!newSet->nextSiblingMultiColumnBox() || !newSet->nextSiblingMultiColumnBox()->isLayoutMultiColumnSet()); }
void PaintLayerStackingNode::rebuildZOrderLists() { #if DCHECK_IS_ON() DCHECK(m_layerListMutationAllowed); #endif DCHECK(isDirtyStackingContext()); for (PaintLayer* child = layer()->firstChild(); child; child = child->nextSibling()) child->stackingNode()->collectLayers(m_posZOrderList, m_negZOrderList); // Sort the two lists. if (m_posZOrderList) std::stable_sort(m_posZOrderList->begin(), m_posZOrderList->end(), compareZIndex); if (m_negZOrderList) std::stable_sort(m_negZOrderList->begin(), m_negZOrderList->end(), compareZIndex); // Append layers for top layer elements after normal layer collection, to // ensure they are on top regardless of z-indexes. The layoutObjects of top // layer elements are children of the view, sorted in top layer stacking // order. if (layer()->isRootLayer()) { LayoutBlockFlow* rootBlock = layoutObject()->view(); // If the viewport is paginated, everything (including "top-layer" elements) // gets redirected to the flow thread. So that's where we have to look, in // that case. if (LayoutBlockFlow* multiColumnFlowThread = rootBlock->multiColumnFlowThread()) rootBlock = multiColumnFlowThread; for (LayoutObject* child = rootBlock->firstChild(); child; child = child->nextSibling()) { Element* childElement = (child->node() && child->node()->isElementNode()) ? toElement(child->node()) : 0; if (childElement && childElement->isInTopLayer()) { PaintLayer* layer = toLayoutBoxModelObject(child)->layer(); // Create the buffer if it doesn't exist yet. if (!m_posZOrderList) m_posZOrderList = wrapUnique(new Vector<PaintLayerStackingNode*>); m_posZOrderList->append(layer->stackingNode()); } } } #if ENABLE(ASSERT) updateStackingParentForZOrderLists(this); #endif m_zOrderListsDirty = false; }
LayoutUnit MultiColumnFragmentainerGroup::calculateMaxColumnHeight() const { LayoutBlockFlow* multicolBlock = m_columnSet.multiColumnBlockFlow(); const ComputedStyle& multicolStyle = multicolBlock->styleRef(); LayoutUnit availableHeight = m_columnSet.multiColumnFlowThread()->columnHeightAvailable(); LayoutUnit maxColumnHeight = availableHeight ? availableHeight : LayoutUnit::max(); if (!multicolStyle.logicalMaxHeight().isMaxSizeNone()) { LayoutUnit logicalMaxHeight = multicolBlock->computeContentLogicalHeight(MaxSize, multicolStyle.logicalMaxHeight(), -1); if (logicalMaxHeight != -1 && maxColumnHeight > logicalMaxHeight) maxColumnHeight = logicalMaxHeight; } return heightAdjustedForRowOffset(maxColumnHeight); }
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(); }
LayoutUnit MultiColumnFragmentainerGroup::heightAdjustedForRowOffset(LayoutUnit height) const { // Adjust for the top offset within the content box of the multicol container (containing // block), unless we're in the first set. We know that the top offset for the first set will be // zero, but if the multicol container has non-zero top border or padding, the set's top offset // (initially being 0 and relative to the border box) will be negative until it has been laid // out. Had we used this bogus offset, we would calculate the wrong height, and risk performing // a wasted layout iteration. Of course all other sets (if any) have this problem in the first // layout pass too, but there's really nothing we can do there until the flow thread has been // laid out anyway. if (m_columnSet.previousSiblingMultiColumnSet()) { LayoutBlockFlow* multicolBlock = m_columnSet.multiColumnBlockFlow(); LayoutUnit contentLogicalTop = m_columnSet.logicalTop() - multicolBlock->borderAndPaddingBefore(); height -= contentLogicalTop; } height -= logicalTop(); return max(height, LayoutUnit(1)); // Let's avoid zero height, as that would probably cause an infinite amount of columns to be created. }
void Fullscreen::setFullScreenLayoutObject(LayoutFullScreen* layoutObject) { if (layoutObject == m_fullScreenLayoutObject) return; if (layoutObject && m_savedPlaceholderComputedStyle) { layoutObject->createPlaceholder(m_savedPlaceholderComputedStyle.release(), m_savedPlaceholderFrameRect); } else if (layoutObject && m_fullScreenLayoutObject && m_fullScreenLayoutObject->placeholder()) { LayoutBlockFlow* placeholder = m_fullScreenLayoutObject->placeholder(); layoutObject->createPlaceholder( ComputedStyle::clone(placeholder->styleRef()), placeholder->frameRect()); } if (m_fullScreenLayoutObject) m_fullScreenLayoutObject->unwrapLayoutObject(); DCHECK(!m_fullScreenLayoutObject); m_fullScreenLayoutObject = layoutObject; }
void ColumnBalancer::traverseLines(const LayoutBlockFlow& blockFlow) { for (const RootInlineBox* line = blockFlow.firstRootBox(); line; line = line->nextRootBox()) { LayoutUnit lineTopInFlowThread = m_flowThreadOffset + line->lineTopWithLeading(); if (lineTopInFlowThread < logicalTopInFlowThread()) continue; if (lineTopInFlowThread >= logicalBottomInFlowThread()) break; examineLine(*line); } }
void LayoutMultiColumnFlowThread::createAndInsertSpannerPlaceholder(LayoutBox* spannerObjectInFlowThread, LayoutObject* insertedBeforeInFlowThread) { LayoutBox* insertBeforeColumnBox = nullptr; LayoutMultiColumnSet* setToSplit = nullptr; if (insertedBeforeInFlowThread) { // The spanner is inserted before something. Figure out what this entails. If the // next object is a spanner too, it means that we can simply insert a new spanner // placeholder in front of its placeholder. insertBeforeColumnBox = insertedBeforeInFlowThread->spannerPlaceholder(); if (!insertBeforeColumnBox) { // The next object isn't a spanner; it's regular column content. Examine what // comes right before us in the flow thread, then. LayoutObject* previousLayoutObject = previousInPreOrderSkippingOutOfFlow(this, spannerObjectInFlowThread); if (!previousLayoutObject || previousLayoutObject == this) { // The spanner is inserted as the first child of the multicol container, // which means that we simply insert a new spanner placeholder at the // beginning. insertBeforeColumnBox = firstMultiColumnBox(); } else if (LayoutMultiColumnSpannerPlaceholder* previousPlaceholder = containingColumnSpannerPlaceholder(previousLayoutObject)) { // Before us is another spanner. We belong right after it then. insertBeforeColumnBox = previousPlaceholder->nextSiblingMultiColumnBox(); } else { // We're inside regular column content with both feet. Find out which column // set this is. It needs to be split it into two sets, so that we can insert // a new spanner placeholder between them. setToSplit = mapDescendantToColumnSet(previousLayoutObject); ASSERT(setToSplit == mapDescendantToColumnSet(insertedBeforeInFlowThread)); insertBeforeColumnBox = setToSplit->nextSiblingMultiColumnBox(); // We've found out which set that needs to be split. Now proceed to // inserting the spanner placeholder, and then insert a second column set. } } ASSERT(setToSplit || insertBeforeColumnBox); } LayoutBlockFlow* multicolContainer = multiColumnBlockFlow(); LayoutMultiColumnSpannerPlaceholder* newPlaceholder = LayoutMultiColumnSpannerPlaceholder::createAnonymous(multicolContainer->styleRef(), *spannerObjectInFlowThread); ASSERT(!insertBeforeColumnBox || insertBeforeColumnBox->parent() == multicolContainer); multicolContainer->LayoutBlock::addChild(newPlaceholder, insertBeforeColumnBox); spannerObjectInFlowThread->setSpannerPlaceholder(*newPlaceholder); if (setToSplit) createAndInsertMultiColumnSet(insertBeforeColumnBox); }