static LayoutObject* getParentOfFirstLineBox(LayoutBlockFlow* curr, LayoutObject* marker) { LayoutObject* firstChild = curr->firstChild(); if (!firstChild) return nullptr; bool inQuirksMode = curr->document().inQuirksMode(); for (LayoutObject* currChild = firstChild; currChild; currChild = currChild->nextSibling()) { if (currChild == marker) continue; if (currChild->isInline() && (!currChild->isLayoutInline() || curr->generatesLineBoxesForInlineChild(currChild))) return curr; if (currChild->isFloating() || currChild->isOutOfFlowPositioned()) continue; if (!currChild->isLayoutBlockFlow() || (currChild->isBox() && toLayoutBox(currChild)->isWritingModeRoot())) break; if (curr->isListItem() && inQuirksMode && currChild->node() && (isHTMLUListElement(*currChild->node()) || isHTMLOListElement(*currChild->node()))) break; LayoutObject* lineBox = getParentOfFirstLineBox(toLayoutBlockFlow(currChild), marker); if (lineBox) return lineBox; } return nullptr; }
bool Text::textLayoutObjectIsNeeded(const ComputedStyle& style, const LayoutObject& parent) { if (!parent.canHaveChildren()) return false; if (isEditingText()) return true; if (!length()) return false; if (style.display() == NONE) return false; if (!containsOnlyWhitespace()) return true; if (!canHaveWhitespaceChildren(parent, this)) return false; // pre-wrap in SVG never makes layoutObject. if (style.whiteSpace() == PRE_WRAP && parent.isSVG()) return false; // pre/pre-wrap/-bb-pre-wrap-text/pre-line always make layoutObjects. if (style.preserveNewline()) return true; // childNeedsDistributionRecalc() here is rare, only happens JS calling surroundContents() etc. from DOMNodeInsertedIntoDocument etc. if (document().childNeedsDistributionRecalc()) return true; const LayoutObject* prev = LayoutTreeBuilderTraversal::previousSiblingLayoutObject(*this); if (prev && prev->isBR()) // <span><br/> <br/></span> return false; if (parent.isLayoutInline()) { // <span><div/> <div/></span> if (prev && !prev->isInline() && !prev->isOutOfFlowPositioned()) return false; } else { if (parent.isLayoutBlock() && !parent.childrenInline() && (!prev || !prev->isInline())) return false; // Avoiding creation of a layoutObject for the text node is a non-essential memory optimization. // So to avoid blowing up on very wide DOMs, we limit the number of siblings to visit. unsigned maxSiblingsToVisit = 50; LayoutObject* first = parent.slowFirstChild(); while (first && first->isFloatingOrOutOfFlowPositioned() && maxSiblingsToVisit--) first = first->nextSibling(); if (!first || first == layoutObject() || LayoutTreeBuilderTraversal::nextSiblingLayoutObject(*this) == first) { // Whitespace at the start of a block just goes away. Don't even // make a layout object for this text. return false; } } return true; }
void LayoutAnalyzer::push(const LayoutObject& o) { increment(TotalLayoutObjectsThatWereLaidOut); if (!o.everHadLayout()) increment(LayoutObjectsThatHadNeverHadLayout); if (o.selfNeedsLayout()) increment(LayoutObjectsThatNeedLayoutForThemselves); if (o.needsPositionedMovementLayout()) increment(LayoutObjectsThatNeedPositionedMovementLayout); if (o.isOutOfFlowPositioned()) increment(LayoutObjectsThatAreOutOfFlowPositioned); if (o.isTableCell()) increment(LayoutObjectsThatAreTableCells); if (o.isFloating()) increment(LayoutObjectsThatAreFloating); if (o.style()->specifiesColumns()) increment(LayoutObjectsThatSpecifyColumns); if (o.hasLayer()) increment(LayoutObjectsThatHaveALayer); if (o.isLayoutInline() && o.alwaysCreateLineBoxesForLayoutInline()) increment(LayoutInlineObjectsThatAlwaysCreateLineBoxes); if (o.isText()) { const LayoutText& t = *toLayoutText(&o); if (t.canUseSimpleFontCodePath()) { increment(LayoutObjectsThatAreTextAndCanUseTheSimpleFontCodePath); increment(CharactersInLayoutObjectsThatAreTextAndCanUseTheSimpleFontCodePath, t.textLength()); } else { increment(LayoutObjectsThatAreTextAndCanNotUseTheSimpleFontCodePath); increment(CharactersInLayoutObjectsThatAreTextAndCanNotUseTheSimpleFontCodePath, t.textLength()); } } // This might be a root in a subtree layout, in which case the LayoutObject // has a parent but the stack is empty. If a LayoutObject subclass forgets // to call push() and is a root in a subtree layout, then this // assert would only fail if that LayoutObject instance has any children // that need layout and do call push(). // LayoutBlock::layoutPositionedObjects() hoists positioned descendants. // LayoutBlockFlow::layoutInlineChildren() walks through inlines. // LayoutTableSection::layoutRows() walks through rows. if (!o.isPositioned() && !o.isTableCell() && !o.isSVGResourceContainer() && (m_stack.size() != 0) && !(o.parent()->childrenInline() && (o.isReplaced() || o.isFloating() || o.isOutOfFlowPositioned()))) { ASSERT(o.parent() == m_stack.peek()); } m_stack.push(&o); // This refers to LayoutAnalyzer depth, not layout tree depth or DOM tree // depth. LayoutAnalyzer depth is generally closer to C++ stack recursion // depth. See above exceptions for when LayoutAnalyzer depth != layout tree // depth. if (m_stack.size() > m_counters[LayoutAnalyzerStackMaximumDepth]) m_counters[LayoutAnalyzerStackMaximumDepth] = m_stack.size(); }
float TextAutosizer::inflate(LayoutObject* parent, InflateBehavior behavior, float multiplier) { Cluster* cluster = currentCluster(); bool hasTextChild = false; LayoutObject* child = nullptr; if (parent->isLayoutBlock() && (parent->childrenInline() || behavior == DescendToInnerBlocks)) child = toLayoutBlock(parent)->firstChild(); else if (parent->isLayoutInline()) child = toLayoutInline(parent)->firstChild(); while (child) { if (child->isText()) { hasTextChild = true; // We only calculate this multiplier on-demand to ensure the parent block of this text // has entered layout. if (!multiplier) multiplier = cluster->m_flags & SUPPRESSING ? 1.0f : clusterMultiplier(cluster); applyMultiplier(child, multiplier); // FIXME: Investigate why MarkOnlyThis is sufficient. if (parent->isLayoutInline()) child->setPreferredLogicalWidthsDirty(MarkOnlyThis); } else if (child->isLayoutInline()) { multiplier = inflate(child, behavior, multiplier); } else if (child->isLayoutBlock() && behavior == DescendToInnerBlocks && !classifyBlock(child, INDEPENDENT | EXPLICIT_WIDTH | SUPPRESSING)) { multiplier = inflate(child, behavior, multiplier); } child = child->nextSibling(); } if (hasTextChild) { applyMultiplier(parent, multiplier); // Parent handles line spacing. } else if (!parent->isListItem()) { // For consistency, a block with no immediate text child should always have a // multiplier of 1. applyMultiplier(parent, 1); } if (parent->isListItem()) { float multiplier = clusterMultiplier(cluster); applyMultiplier(parent, multiplier); // The list item has to be treated special because we can have a tree such that you have // a list item for a form inside it. The list marker then ends up inside the form and when // we try to get the clusterMultiplier we have the wrong cluster root to work from and get // the wrong value. LayoutListItem* item = toLayoutListItem(parent); if (LayoutListMarker* marker = item->marker()) { applyMultiplier(marker, multiplier); marker->setPreferredLogicalWidthsDirty(MarkOnlyThis); } } return multiplier; }
static void addPDFURLRectsForInlineChildrenRecursively(const LayoutObject& layoutObject, const PaintInfo& paintInfo, const LayoutPoint& paintOffset) { for (LayoutObject* child = layoutObject.slowFirstChild(); child; child = child->nextSibling()) { if (!child->isLayoutInline() || toLayoutBoxModelObject(child)->hasSelfPaintingLayer()) continue; ObjectPainter(*child).addPDFURLRectIfNeeded(paintInfo, paintOffset); addPDFURLRectsForInlineChildrenRecursively(*child, paintInfo, paintOffset); } }
TracedLayoutObject::TracedLayoutObject(const LayoutObject& object) : m_address((unsigned long) &object) , m_isAnonymous(object.isAnonymous()) , m_isPositioned(object.isOutOfFlowPositioned()) , m_isRelPositioned(object.isRelPositioned()) , m_isStickyPositioned(object.isStickyPositioned()) , m_isFloating(object.isFloating()) , m_selfNeeds(object.selfNeedsLayout()) , m_positionedMovement(object.needsPositionedMovementLayout()) , m_childNeeds(object.normalChildNeedsLayout()) , m_posChildNeeds(object.posChildNeedsLayout()) , m_isTableCell(object.isTableCell()) , m_name(String(object.name()).isolatedCopy()) , m_absRect(object.absoluteBoundingBoxRect()) { if (Node* node = object.node()) { m_tag = String(node->nodeName()).isolatedCopy(); if (node->isElementNode()) { Element& element = toElement(*node); if (element.hasID()) m_id = String(element.getIdAttribute()).isolatedCopy(); if (element.hasClass()) { for (size_t i = 0; i < element.classNames().size(); ++i) { m_classNames.append( String(element.classNames()[i]).isolatedCopy()); } } } } // FIXME: When the fixmes in LayoutTreeAsText::writeLayoutObject() are // fixed, deduplicate it with this. if (object.isText()) { m_rect = LayoutRect(toLayoutText(object).linesBoundingBox()); } else if (object.isLayoutInline()) { m_rect = LayoutRect(toLayoutInline(object).linesBoundingBox()); } else if (object.isBox()) { m_rect = toLayoutBox(&object)->frameRect(); } if (m_isTableCell) { const LayoutTableCell& c = toLayoutTableCell(object); if (c.row() && c.row()->rowIndexWasSet() && c.hasCol() && (!c.row()->section() || !c.row()->section()->needsCellRecalc())) { m_row = c.rowIndex(); m_col = c.col(); m_rowSpan = c.rowSpan(); m_colSpan = c.colSpan(); } } for (LayoutObject* child = object.slowFirstChild(); child; child = child->nextSibling()) { m_children.append(adoptRef(new TracedLayoutObject(*child))); } }
void LinkHighlight::computeQuads(const Node& node, Vector<FloatQuad>& outQuads) const { if (!node.layoutObject()) return; LayoutObject* renderer = node.layoutObject(); // For inline elements, absoluteQuads will return a line box based on the line-height // and font metrics, which is technically incorrect as replaced elements like images // should use their intristic height and expand the linebox as needed. To get an // appropriately sized highlight we descend into the children and have them add their // boxes. if (renderer->isLayoutInline()) { for (Node* child = NodeRenderingTraversal::firstChild(node); child; child = NodeRenderingTraversal::nextSibling(*child)) computeQuads(*child, outQuads); } else { // FIXME: this does not need to be absolute, just in the paint invalidation container's space. renderer->absoluteQuads(outQuads); } }
void LayoutAnalyzer::push(const LayoutObject& o) { increment(TotalLayoutObjectsThatWereLaidOut); if (!o.everHadLayout()) increment(LayoutObjectsThatHadNeverHadLayout); if (o.selfNeedsLayout()) increment(LayoutObjectsThatNeedLayoutForThemselves); if (o.needsPositionedMovementLayout()) increment(LayoutObjectsThatNeedPositionedMovementLayout); if (o.isOutOfFlowPositioned()) increment(LayoutObjectsThatAreOutOfFlowPositioned); if (o.isTableCell()) increment(LayoutObjectsThatAreTableCells); if (o.isFloating()) increment(LayoutObjectsThatAreFloating); if (o.style()->specifiesColumns()) increment(LayoutObjectsThatSpecifyColumns); if (o.hasLayer()) increment(LayoutObjectsThatHaveALayer); if (o.isLayoutInline() && o.alwaysCreateLineBoxesForLayoutInline()) increment(LayoutInlineObjectsThatAlwaysCreateLineBoxes); if (o.isText()) { const LayoutText& t = *toLayoutText(&o); if (t.canUseSimpleFontCodePath()) { increment(LayoutObjectsThatAreTextAndCanUseTheSimpleFontCodePath); increment(CharactersInLayoutObjectsThatAreTextAndCanUseTheSimpleFontCodePath, t.textLength()); } else { increment(LayoutObjectsThatAreTextAndCanNotUseTheSimpleFontCodePath); increment(CharactersInLayoutObjectsThatAreTextAndCanNotUseTheSimpleFontCodePath, t.textLength()); } } ++m_depth; // This refers to LayoutAnalyzer depth, which is generally closer to C++ // stack recursion depth, not layout tree depth or DOM tree depth. m_counters[LayoutAnalyzerStackMaximumDepth] = max(m_counters[LayoutAnalyzerStackMaximumDepth], m_depth); }
void LineBoxList::dirtyLinesFromChangedChild(LayoutObject* container, LayoutObject* child) { if (!container->parent() || (container->isLayoutBlock() && (container->selfNeedsLayout() || !container->isLayoutBlockFlow()))) return; LayoutInline* inlineContainer = container->isLayoutInline() ? toLayoutInline(container) : 0; InlineBox* firstBox = inlineContainer ? inlineContainer->firstLineBoxIncludingCulling() : firstLineBox(); // If we have no first line box, then just bail early. if (!firstBox) { // For an empty inline, go ahead and propagate the check up to our parent, unless the parent // is already dirty. if (container->isInline() && !container->ancestorLineBoxDirty()) { container->parent()->dirtyLinesFromChangedChild(container); container->setAncestorLineBoxDirty(); // Mark the container to avoid dirtying the same lines again across multiple destroy() calls of the same subtree. } return; } // Try to figure out which line box we belong in. First try to find a previous // line box by examining our siblings. If we didn't find a line box, then use our // parent's first line box. RootInlineBox* box = 0; LayoutObject* curr = 0; ListHashSet<LayoutObject*, 16> potentialLineBreakObjects; potentialLineBreakObjects.add(child); for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) { potentialLineBreakObjects.add(curr); if (curr->isFloatingOrOutOfFlowPositioned()) continue; if (curr->isReplaced()) { InlineBox* wrapper = toLayoutBox(curr)->inlineBoxWrapper(); if (wrapper) box = &wrapper->root(); } else if (curr->isText()) { InlineTextBox* textBox = toLayoutText(curr)->lastTextBox(); if (textBox) box = &textBox->root(); } else if (curr->isLayoutInline()) { InlineBox* lastSiblingBox = toLayoutInline(curr)->lastLineBoxIncludingCulling(); if (lastSiblingBox) box = &lastSiblingBox->root(); } if (box) break; } if (!box) { if (inlineContainer && !inlineContainer->alwaysCreateLineBoxes()) { // https://bugs.webkit.org/show_bug.cgi?id=60778 // We may have just removed a <br> with no line box that was our first child. In this case // we won't find a previous sibling, but firstBox can be pointing to a following sibling. // This isn't good enough, since we won't locate the root line box that encloses the removed // <br>. We have to just over-invalidate a bit and go up to our parent. if (!inlineContainer->ancestorLineBoxDirty()) { inlineContainer->parent()->dirtyLinesFromChangedChild(inlineContainer); inlineContainer->setAncestorLineBoxDirty(); // Mark the container to avoid dirtying the same lines again across multiple destroy() calls of the same subtree. } return; } box = &firstBox->root(); } // If we found a line box, then dirty it. if (box) { RootInlineBox* adjacentBox; box->markDirty(); // dirty the adjacent lines that might be affected // NOTE: we dirty the previous line because RootInlineBox objects cache // the address of the first object on the next line after a BR, which we may be // invalidating here. For more info, see how LayoutBlock::layoutInlineChildren // calls setLineBreakInfo with the result of findNextLineBreak. findNextLineBreak, // despite the name, actually returns the first LayoutObject after the BR. // <rdar://problem/3849947> "Typing after pasting line does not appear until after window resize." adjacentBox = box->prevRootBox(); if (adjacentBox) adjacentBox->markDirty(); adjacentBox = box->nextRootBox(); // If |child| or any of its immediately previous siblings with culled lineboxes is the object after a line-break in |box| or the linebox after it // then that means |child| actually sits on the linebox after |box| (or is its line-break object) and so we need to dirty it as well. if (adjacentBox && (potentialLineBreakObjects.contains(box->lineBreakObj()) || potentialLineBreakObjects.contains(adjacentBox->lineBreakObj()) || child->isBR() || isIsolated(container->style()->unicodeBidi()))) adjacentBox->markDirty(); } }
bool InspectorHighlight::buildNodeQuads(Node* node, FloatQuad* content, FloatQuad* padding, FloatQuad* border, FloatQuad* margin) { LayoutObject* layoutObject = node->layoutObject(); if (!layoutObject) return false; FrameView* containingView = layoutObject->frameView(); if (!containingView) return false; if (!layoutObject->isBox() && !layoutObject->isLayoutInline()) return false; LayoutRect contentBox; LayoutRect paddingBox; LayoutRect borderBox; LayoutRect marginBox; if (layoutObject->isBox()) { LayoutBox* layoutBox = toLayoutBox(layoutObject); // LayoutBox returns the "pure" content area box, exclusive of the scrollbars (if present), which also count towards the content area in CSS. const int verticalScrollbarWidth = layoutBox->verticalScrollbarWidth(); const int horizontalScrollbarHeight = layoutBox->horizontalScrollbarHeight(); contentBox = layoutBox->contentBoxRect(); contentBox.setWidth(contentBox.width() + verticalScrollbarWidth); contentBox.setHeight(contentBox.height() + horizontalScrollbarHeight); paddingBox = layoutBox->paddingBoxRect(); paddingBox.setWidth(paddingBox.width() + verticalScrollbarWidth); paddingBox.setHeight(paddingBox.height() + horizontalScrollbarHeight); borderBox = layoutBox->borderBoxRect(); marginBox = LayoutRect(borderBox.x() - layoutBox->marginLeft(), borderBox.y() - layoutBox->marginTop(), borderBox.width() + layoutBox->marginWidth(), borderBox.height() + layoutBox->marginHeight()); } else { LayoutInline* layoutInline = toLayoutInline(layoutObject); // LayoutInline's bounding box includes paddings and borders, excludes margins. borderBox = LayoutRect(layoutInline->linesBoundingBox()); paddingBox = LayoutRect(borderBox.x() + layoutInline->borderLeft(), borderBox.y() + layoutInline->borderTop(), borderBox.width() - layoutInline->borderLeft() - layoutInline->borderRight(), borderBox.height() - layoutInline->borderTop() - layoutInline->borderBottom()); contentBox = LayoutRect(paddingBox.x() + layoutInline->paddingLeft(), paddingBox.y() + layoutInline->paddingTop(), paddingBox.width() - layoutInline->paddingLeft() - layoutInline->paddingRight(), paddingBox.height() - layoutInline->paddingTop() - layoutInline->paddingBottom()); // Ignore marginTop and marginBottom for inlines. marginBox = LayoutRect(borderBox.x() - layoutInline->marginLeft(), borderBox.y(), borderBox.width() + layoutInline->marginWidth(), borderBox.height()); } *content = layoutObject->localToAbsoluteQuad(FloatRect(contentBox)); *padding = layoutObject->localToAbsoluteQuad(FloatRect(paddingBox)); *border = layoutObject->localToAbsoluteQuad(FloatRect(borderBox)); *margin = layoutObject->localToAbsoluteQuad(FloatRect(marginBox)); contentsQuadToViewport(containingView, *content); contentsQuadToViewport(containingView, *padding); contentsQuadToViewport(containingView, *border); contentsQuadToViewport(containingView, *margin); return true; }