void RootInlineBox::childRemoved(InlineBox* box) { if (box->renderer() == m_lineBreakObj) setLineBreakInfo(0, 0, BidiStatus()); for (RootInlineBox* prev = prevRootBox(); prev && prev->lineBreakObj() == box->renderer(); prev = prev->prevRootBox()) { prev->setLineBreakInfo(0, 0, BidiStatus()); prev->markDirty(); } }
void RootInlineBox::childRemoved(InlineBox* box) { if (box->object() == m_lineBreakObj) setLineBreakInfo(0, 0, 0, 0); for (RootInlineBox* prev = prevRootBox(); prev && prev->lineBreakObj() == box->object(); prev = prev->prevRootBox()) { prev->setLineBreakInfo(0, 0, 0, 0); prev->markDirty(); } }
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(); } }
void RenderFlow::dirtyLinesFromChangedChild(RenderObject* child, bool adding) { if (!parent() || selfNeedsLayout() || isTable()) return; // For an empty inline, go ahead and propagate the check up to our parent. if (isInline() && !firstLineBox()) return parent()->dirtyLinesFromChangedChild(this); // 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; RenderObject* curr = 0; for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) { if (curr->isFloatingOrPositioned()) continue; if (curr->isReplaced()) { InlineBox* wrapper = curr->inlineBoxWrapper(); if (wrapper) box = wrapper->root(); } else if (curr->isText()) { InlineTextBox* textBox = static_cast<RenderText*>(curr)->lastTextBox(); if (textBox) box = textBox->root(); } else if (curr->isInlineFlow()) { InlineRunBox* runBox = static_cast<RenderFlow*>(curr)->lastLineBox(); if (runBox) box = runBox->root(); } if (box) break; } if (!box && firstLineBox()) box = firstLineBox()->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 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." adjacentBox = box->prevRootBox(); if (adjacentBox) adjacentBox->markDirty(); if (child->isBR() || (curr && curr->isBR())) { adjacentBox = box->nextRootBox(); if (adjacentBox) adjacentBox->markDirty(); } } }
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(); } } } }
bool RenderTextLineBoxes::dirtyRange(RenderText& renderer, unsigned start, unsigned end, int lengthDelta) { RootInlineBox* firstRootBox = nullptr; RootInlineBox* lastRootBox = nullptr; // Dirty all text boxes that include characters in between offset and offset+len. bool dirtiedLines = false; for (auto current = m_first; current; current = current->nextTextBox()) { // FIXME: This shouldn't rely on the end of a dirty line box. See https://bugs.webkit.org/show_bug.cgi?id=97264 // Text run is entirely before the affected range. if (current->end() < start) continue; // Text run is entirely after the affected range. if (current->start() > end) { current->offsetRun(lengthDelta); auto& rootBox = current->root(); if (!firstRootBox) { firstRootBox = &rootBox; if (!dirtiedLines) { // The affected area was in between two runs. Go ahead and mark the root box of // the run after the affected area as dirty. firstRootBox->markDirty(); dirtiedLines = true; } } lastRootBox = &rootBox; continue; } if (current->end() >= start && current->end() <= end) { // Text run overlaps with the left end of the affected range. current->dirtyLineBoxes(); dirtiedLines = true; continue; } if (current->start() <= start && current->end() >= end) { // Text run subsumes the affected range. current->dirtyLineBoxes(); dirtiedLines = true; continue; } if (current->start() <= end && current->end() >= end) { // Text run overlaps with right end of the affected range. current->dirtyLineBoxes(); dirtiedLines = true; continue; } } // Now we have to walk all of the clean lines and adjust their cached line break information // to reflect our updated offsets. if (lastRootBox) lastRootBox = lastRootBox->nextRootBox(); if (firstRootBox) { auto previousRootBox = firstRootBox->prevRootBox(); if (previousRootBox) firstRootBox = previousRootBox; } else if (m_last) { ASSERT(!lastRootBox); firstRootBox = &m_last->root(); firstRootBox->markDirty(); dirtiedLines = true; } for (auto current = firstRootBox; current && current != lastRootBox; current = current->nextRootBox()) { if (current->lineBreakObj() == &renderer && current->lineBreakPos() > end) current->setLineBreakPos(current->lineBreakPos() + lengthDelta); } // If the text node is empty, dirty the line where new text will be inserted. if (!m_first && renderer.parent()) { renderer.parent()->dirtyLinesFromChangedChild(&renderer); dirtiedLines = true; } return dirtiedLines; }
void RenderLineBoxList::dirtyLinesFromChangedChild(RenderObject* container, RenderObject* child) { if (!container->parent() || (container->isRenderBlock() && (container->selfNeedsLayout() || !container->isBlockFlow()))) return; RenderInline* inlineContainer = container->isRenderInline() ? toRenderInline(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; RenderObject* curr = 0; for (curr = child->previousSibling(); curr; curr = curr->previousSibling()) { if (curr->isFloatingOrOutOfFlowPositioned()) continue; if (curr->isReplaced()) { InlineBox* wrapper = toRenderBox(curr)->inlineBoxWrapper(); if (wrapper) box = wrapper->root(); } else if (curr->isText()) { InlineTextBox* textBox = toRenderText(curr)->lastTextBox(); if (textBox) box = textBox->root(); } else if (curr->isRenderInline()) { InlineBox* lastSiblingBox = toRenderInline(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 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." adjacentBox = box->prevRootBox(); if (adjacentBox) adjacentBox->markDirty(); adjacentBox = box->nextRootBox(); // If |child| has been inserted before the first element in the linebox, but after collapsed leading // space, the search for |child|'s linebox will go past the leading space to the previous linebox and select that // one as |box|. If we hit that situation here, dirty the |box| actually containing the child too. bool insertedAfterLeadingSpace = box->lineBreakObj() == child->previousSibling(); if (adjacentBox && (adjacentBox->lineBreakObj() == child || child->isBR() || (curr && curr->isBR()) || insertedAfterLeadingSpace)) adjacentBox->markDirty(); } }