void RenderLineBoxList::dirtyLinesFromChangedChild(RenderBoxModelObject& container, RenderObject& child) { ASSERT(is<RenderInline>(container) || is<RenderBlockFlow>(container)); if (!container.parent() || (is<RenderBlockFlow>(container) && container.selfNeedsLayout())) return; RenderInline* inlineContainer = is<RenderInline>(container) ? &downcast<RenderInline>(container) : nullptr; InlineBox* firstBox = inlineContainer ? inlineContainer->firstLineBoxIncludingCulling() : firstLineBox(); // If we have no first line box, then just bail early. if (!firstBox) { // For an empty inline, 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 = nullptr; RenderObject* current; for (current = child.previousSibling(); current; current = current->previousSibling()) { if (current->isFloatingOrOutOfFlowPositioned()) continue; if (current->isReplaced()) { if (auto wrapper = downcast<RenderBox>(*current).inlineBoxWrapper()) box = &wrapper->root(); } if (is<RenderLineBreak>(*current)) { if (auto wrapper = downcast<RenderLineBreak>(*current).inlineBoxWrapper()) box = &wrapper->root(); } else if (is<RenderText>(*current)) { if (InlineTextBox* textBox = downcast<RenderText>(*current).lastTextBox()) box = &textBox->root(); } else if (is<RenderInline>(*current)) { InlineBox* lastSiblingBox = downcast<RenderInline>(*current).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) { 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 RenderBlock::layoutInlineChildren // calls setLineBreakInfo with the result of findNextLineBreak. findNextLineBreak, // despite the name, actually returns the first RenderObject after the BR. // <rdar://problem/3849947> "Typing after pasting line does not appear until after window resize." if (RootInlineBox* prevBox = box->prevRootBox()) prevBox->markDirty(); // FIXME: We shouldn't need to always dirty the next line. This is only strictly // necessary some of the time, in situations involving BRs. if (RootInlineBox* nextBox = box->nextRootBox()) { nextBox->markDirty(); // Dedicated linebox for floats may be added as the last rootbox. If this occurs with BRs inside inlines that propagte their lineboxes to // the parent flow, we need to invalidate it explicitly. // FIXME: We should be able to figure out the actual "changed child" even when we are calling through empty inlines recursively. if (is<RenderInline>(child) && !downcast<RenderInline>(child).firstLineBoxIncludingCulling()) { auto* lastRootBox = nextBox->blockFlow().lastRootBox(); if (lastRootBox->isTrailingFloatsRootInlineBox() && !lastRootBox->isDirty()) lastRootBox->markDirty(); } } } }