void RenderRubyRun::layout() { RenderBlockFlow::layout(); RenderRubyText* rt = rubyText(); if (!rt) return; rt->setLogicalLeft(0); // Place the RenderRubyText such that its bottom is flush with the lineTop of the first line of the RenderRubyBase. LayoutUnit lastLineRubyTextBottom = rt->logicalHeight(); LayoutUnit firstLineRubyTextTop = 0; RootInlineBox* rootBox = rt->lastRootBox(); if (rootBox) { // In order to align, we have to ignore negative leading. firstLineRubyTextTop = rt->firstRootBox()->logicalTopLayoutOverflow(); lastLineRubyTextBottom = rootBox->logicalBottomLayoutOverflow(); } if (isHorizontalWritingMode() && rt->style().rubyPosition() == RubyPositionInterCharacter) { // Bopomofo. We need to move the RenderRubyText over to the right side and center it // vertically relative to the base. const Font& font = style().font(); float distanceBetweenBase = max(font.letterSpacing(), 2.0f * rt->style().font().fontMetrics().height()); setWidth(width() + distanceBetweenBase - font.letterSpacing()); if (RenderRubyBase* rb = rubyBase()) { LayoutUnit firstLineTop = 0; LayoutUnit lastLineBottom = logicalHeight(); RootInlineBox* rootBox = rb->firstRootBox(); if (rootBox) firstLineTop = rootBox->logicalTopLayoutOverflow(); firstLineTop += rb->logicalTop(); if (rootBox) lastLineBottom = rootBox->logicalBottomLayoutOverflow(); lastLineBottom += rb->logicalTop(); rt->setX(rb->x() + rb->width() - font.letterSpacing()); LayoutUnit extent = lastLineBottom - firstLineTop; rt->setY(firstLineTop + (extent - rt->height()) / 2); } } else if (style().isFlippedLinesWritingMode() == (style().rubyPosition() == RubyPositionAfter)) { LayoutUnit firstLineTop = 0; if (RenderRubyBase* rb = rubyBase()) { RootInlineBox* rootBox = rb->firstRootBox(); if (rootBox) firstLineTop = rootBox->logicalTopLayoutOverflow(); firstLineTop += rb->logicalTop(); } rt->setLogicalTop(-lastLineRubyTextBottom + firstLineTop); } else { LayoutUnit lastLineBottom = logicalHeight(); if (RenderRubyBase* rb = rubyBase()) { RootInlineBox* rootBox = rb->lastRootBox(); if (rootBox) lastLineBottom = rootBox->logicalBottomLayoutOverflow(); lastLineBottom += rb->logicalTop(); } rt->setLogicalTop(-firstLineRubyTextTop + lastLineBottom); } // Update our overflow to account for the new RenderRubyText position. computeOverflow(clientLogicalBottom()); }
void RenderLineBoxList::dirtyLinesFromChangedChild(RenderObject* container, RenderObject* child) { if (!container->parent() || (container->isRenderBlock() && (container->selfNeedsLayout() || !container->isBlockFlow()))) return; // If we have no first line box, then just bail early. if (!firstLineBox()) { // For an empty inline, go ahead and propagate the check up to our parent, unless the parent // is already dirty. if (container->isInline() && !container->parent()->selfNeedsLayout()) container->parent()->dirtyLinesFromChangedChild(container); 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->isFloatingOrPositioned()) 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()) { InlineFlowBox* flowBox = toRenderInline(curr)->lastLineBox(); if (flowBox) box = flowBox->root(); } if (box) break; } if (!box) 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(container->isRenderInline() || container->isRenderBlockFlow()); if (!container->parent() || (container->isRenderBlockFlow() && container->selfNeedsLayout())) 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 = 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) { 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() || (current && current->isBR()) || insertedAfterLeadingSpace || isIsolated(container->style().unicodeBidi()))) adjacentBox->markDirty(); } }
RootInlineBox* RenderSVGText::createRootInlineBox() { RootInlineBox* box = new (renderArena()) SVGRootInlineBox(this); box->setHasVirtualLogicalHeight(); return box; }