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; }
LayoutRubyText* LayoutRubyRun::rubyText() const { LayoutObject* child = firstChild(); // If in future it becomes necessary to support floating or positioned ruby text, // layout will have to be changed to handle them properly. ASSERT(!child || !child->isRubyText() || !child->isFloatingOrOutOfFlowPositioned()); return child && child->isRubyText() ? static_cast<LayoutRubyText*>(child) : 0; }
LayoutBox* LayoutFieldset::findInFlowLegend() const { for (LayoutObject* legend = firstChild(); legend; legend = legend->nextSibling()) { if (legend->isFloatingOrOutOfFlowPositioned()) continue; if (isHTMLLegendElement(legend->node())) return toLayoutBox(legend); } return nullptr; }
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(); } }
LayoutObject* FirstLetterPseudoElement::firstLetterTextLayoutObject(const Element& element) { LayoutObject* parentLayoutObject = 0; // If we are looking at a first letter element then we need to find the // first letter text layoutObject from the parent node, and not ourselves. if (element.isFirstLetterPseudoElement()) parentLayoutObject = element.parentOrShadowHostElement()->layoutObject(); else parentLayoutObject = element.layoutObject(); if (!parentLayoutObject || !parentLayoutObject->style()->hasPseudoStyle(FIRST_LETTER) || !canHaveGeneratedChildren(*parentLayoutObject) || !(parentLayoutObject->isLayoutBlockFlow() || parentLayoutObject->isLayoutButton())) return nullptr; // Drill down into our children and look for our first text child. LayoutObject* firstLetterTextLayoutObject = parentLayoutObject->slowFirstChild(); while (firstLetterTextLayoutObject) { // This can be called when the first letter layoutObject is already in the tree. We do not // want to consider that layoutObject for our text layoutObject so we go to the sibling (which is // the LayoutTextFragment for the remaining text). if (firstLetterTextLayoutObject->style() && firstLetterTextLayoutObject->style()->styleType() == FIRST_LETTER) { firstLetterTextLayoutObject = firstLetterTextLayoutObject->nextSibling(); } else if (firstLetterTextLayoutObject->isText()) { // FIXME: If there is leading punctuation in a different LayoutText than // the first letter, we'll not apply the correct style to it. RefPtr<StringImpl> str = toLayoutText(firstLetterTextLayoutObject)->isTextFragment() ? toLayoutTextFragment(firstLetterTextLayoutObject)->completeText() : toLayoutText(firstLetterTextLayoutObject)->originalText(); if (firstLetterLength(str.get()) || isInvalidFirstLetterLayoutObject(firstLetterTextLayoutObject)) break; firstLetterTextLayoutObject = firstLetterTextLayoutObject->nextSibling(); } else if (firstLetterTextLayoutObject->isListMarker()) { firstLetterTextLayoutObject = firstLetterTextLayoutObject->nextSibling(); } else if (firstLetterTextLayoutObject->isFloatingOrOutOfFlowPositioned()) { if (firstLetterTextLayoutObject->style()->styleType() == FIRST_LETTER) { firstLetterTextLayoutObject = firstLetterTextLayoutObject->slowFirstChild(); break; } firstLetterTextLayoutObject = firstLetterTextLayoutObject->nextSibling(); } else if (firstLetterTextLayoutObject->isReplaced() || firstLetterTextLayoutObject->isLayoutButton() || firstLetterTextLayoutObject->isMenuList()) { return nullptr; } else if (firstLetterTextLayoutObject->isFlexibleBoxIncludingDeprecated() || firstLetterTextLayoutObject->isLayoutGrid()) { firstLetterTextLayoutObject = firstLetterTextLayoutObject->nextSibling(); } else if (!firstLetterTextLayoutObject->isInline() && firstLetterTextLayoutObject->style()->hasPseudoStyle(FIRST_LETTER) && canHaveGeneratedChildren(*firstLetterTextLayoutObject)) { // There is a layoutObject further down the tree which has FIRST_LETTER set. When that node // is attached we will handle setting up the first letter then. return nullptr; } else { firstLetterTextLayoutObject = firstLetterTextLayoutObject->slowFirstChild(); } } // No first letter text to display, we're done. // FIXME: This black-list of disallowed LayoutText subclasses is fragile. crbug.com/422336. // Should counter be on this list? What about LayoutTextFragment? if (!firstLetterTextLayoutObject || !firstLetterTextLayoutObject->isText() || isInvalidFirstLetterLayoutObject(firstLetterTextLayoutObject)) return nullptr; return firstLetterTextLayoutObject; }