diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2016-04-10 09:28:39 +0000 |
commit | 32761a6cee1d0dee366b885b7b9c777e67885688 (patch) | |
tree | d6bec92bebfb216f4126356e55518842c2f476a1 /Source/WebCore/rendering/RenderBlockLineLayout.cpp | |
parent | a4e969f4965059196ca948db781e52f7cfebf19e (diff) | |
download | WebKitGtk-tarball-32761a6cee1d0dee366b885b7b9c777e67885688.tar.gz |
webkitgtk-2.4.11webkitgtk-2.4.11
Diffstat (limited to 'Source/WebCore/rendering/RenderBlockLineLayout.cpp')
-rw-r--r-- | Source/WebCore/rendering/RenderBlockLineLayout.cpp | 1221 |
1 files changed, 585 insertions, 636 deletions
diff --git a/Source/WebCore/rendering/RenderBlockLineLayout.cpp b/Source/WebCore/rendering/RenderBlockLineLayout.cpp index 451de09c9..3e546abec 100644 --- a/Source/WebCore/rendering/RenderBlockLineLayout.cpp +++ b/Source/WebCore/rendering/RenderBlockLineLayout.cpp @@ -26,22 +26,18 @@ #include "AXObjectCache.h" #include "BidiResolver.h" -#include "BreakingContext.h" +#include "BreakingContextInlineHeaders.h" #include "FloatingObjects.h" #include "InlineElementBox.h" #include "InlineIterator.h" #include "InlineTextBox.h" -#include "InlineTextBoxStyle.h" #include "LineLayoutState.h" #include "Logging.h" #include "RenderBlockFlow.h" #include "RenderFlowThread.h" #include "RenderLineBreak.h" #include "RenderRegion.h" -#include "RenderRubyBase.h" -#include "RenderRubyText.h" #include "RenderView.h" -#include "SVGRootInlineBox.h" #include "Settings.h" #include "SimpleLineLayoutFunctions.h" #include "TrailingFloatsRootInlineBox.h" @@ -49,6 +45,10 @@ #include <wtf/RefCountedLeakCounter.h> #include <wtf/StdLibExtras.h> +#if ENABLE(SVG) +#include "SVGRootInlineBox.h" +#endif + namespace WebCore { static void determineDirectionality(TextDirection& dir, InlineIterator iter) @@ -71,12 +71,13 @@ static void determineDirectionality(TextDirection& dir, InlineIterator iter) } } -inline BidiRun* createRun(int start, int end, RenderObject& obj, InlineBidiResolver& resolver) +inline BidiRun* createRun(int start, int end, RenderObject* obj, InlineBidiResolver& resolver) { - return new BidiRun(start, end, obj, resolver.context(), resolver.dir()); + ASSERT(obj); + return new BidiRun(start, end, *obj, resolver.context(), resolver.dir()); } -void RenderBlockFlow::appendRunsForObject(BidiRunList<BidiRun>* runs, int start, int end, RenderObject& obj, InlineBidiResolver& resolver) +void RenderBlockFlow::appendRunsForObject(BidiRunList<BidiRun>& runs, int start, int end, RenderObject* obj, InlineBidiResolver& resolver) { if (start > end || shouldSkipCreatingRunsForObject(obj)) return; @@ -87,34 +88,33 @@ void RenderBlockFlow::appendRunsForObject(BidiRunList<BidiRun>* runs, int start, if (haveNextMidpoint) nextMidpoint = lineMidpointState.midpoints()[lineMidpointState.currentMidpoint()]; if (lineMidpointState.betweenMidpoints()) { - if (!haveNextMidpoint || (&obj != nextMidpoint.renderer())) + if (!(haveNextMidpoint && nextMidpoint.renderer() == obj)) return; // This is a new start point. Stop ignoring objects and // adjust our start. + lineMidpointState.setBetweenMidpoints(false); start = nextMidpoint.offset(); lineMidpointState.incrementCurrentMidpoint(); - if (start < end) { - appendRunsForObject(runs, start, end, obj, resolver); - return; - } + if (start < end) + return appendRunsForObject(runs, start, end, obj, resolver); } else { - if (!haveNextMidpoint || (&obj != nextMidpoint.renderer())) { - if (runs) - runs->addRun(createRun(start, end, obj, resolver)); + if (!haveNextMidpoint || (obj != nextMidpoint.renderer())) { + runs.addRun(createRun(start, end, obj, resolver)); return; } - // An end midpoint has been encountered within our object. We need to append a run with our endpoint. + // An end midpoint has been encountered within our object. We + // need to go ahead and append a run with our endpoint. if (static_cast<int>(nextMidpoint.offset() + 1) <= end) { + lineMidpointState.setBetweenMidpoints(true); lineMidpointState.incrementCurrentMidpoint(); - // The end of the line is before the object we're inspecting. Skip everything and return - if (nextMidpoint.refersToEndOfPreviousNode()) - return; - if (static_cast<int>(nextMidpoint.offset() + 1) > start && runs) - runs->addRun(createRun(start, nextMidpoint.offset() + 1, obj, resolver)); - appendRunsForObject(runs, nextMidpoint.offset() + 1, end, obj, resolver); - } else if (runs) - runs->addRun(createRun(start, end, obj, resolver)); + if (nextMidpoint.offset() != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it. + if (static_cast<int>(nextMidpoint.offset() + 1) > start) + runs.addRun(createRun(start, nextMidpoint.offset() + 1, obj, resolver)); + return appendRunsForObject(runs, nextMidpoint.offset() + 1, end, obj, resolver); + } + } else + runs.addRun(createRun(start, end, obj, resolver)); } } @@ -127,7 +127,7 @@ RootInlineBox* RenderBlockFlow::createAndAppendRootInlineBox() { auto newRootBox = createRootInlineBox(); RootInlineBox* rootBox = newRootBox.get(); - m_lineBoxes.appendLineBox(WTFMove(newRootBox)); + m_lineBoxes.appendLineBox(std::move(newRootBox)); if (UNLIKELY(AXObjectCache::accessibilityEnabled()) && firstRootBox() == rootBox) { if (AXObjectCache* cache = document().existingAXObjectCache()) @@ -137,41 +137,41 @@ RootInlineBox* RenderBlockFlow::createAndAppendRootInlineBox() return rootBox; } -static inline InlineBox* createInlineBoxForRenderer(RenderObject* renderer, bool isRootLineBox, bool isOnlyRun = false) +static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false) { if (isRootLineBox) - return downcast<RenderBlockFlow>(*renderer).createAndAppendRootInlineBox(); + return toRenderBlockFlow(obj)->createAndAppendRootInlineBox(); - if (is<RenderText>(*renderer)) - return downcast<RenderText>(*renderer).createInlineTextBox(); + if (obj->isText()) + return toRenderText(obj)->createInlineTextBox(); - if (is<RenderBox>(*renderer)) { + if (obj->isBox()) { // FIXME: This is terrible. This branch returns an *owned* pointer! - return downcast<RenderBox>(*renderer).createInlineBox().release(); + return toRenderBox(obj)->createInlineBox().release(); } - if (is<RenderLineBreak>(*renderer)) { + if (obj->isLineBreak()) { // FIXME: This is terrible. This branch returns an *owned* pointer! - auto inlineBox = downcast<RenderLineBreak>(*renderer).createInlineBox().release(); + auto inlineBox = toRenderLineBreak(obj)->createInlineBox().release(); // We only treat a box as text for a <br> if we are on a line by ourself or in strict mode // (Note the use of strict mode. In "almost strict" mode, we don't treat the box for <br> as text.) - inlineBox->setBehavesLikeText(isOnlyRun || renderer->document().inNoQuirksMode() || renderer->isLineBreakOpportunity()); + inlineBox->setBehavesLikeText(isOnlyRun || obj->document().inNoQuirksMode() || obj->isLineBreakOpportunity()); return inlineBox; } - return downcast<RenderInline>(*renderer).createAndAppendInlineFlowBox(); + return toRenderInline(obj)->createAndAppendInlineFlowBox(); } static inline void dirtyLineBoxesForRenderer(RenderObject& renderer, bool fullLayout) { - if (is<RenderText>(renderer)) { - RenderText& renderText = downcast<RenderText>(renderer); + if (renderer.isText()) { + RenderText& renderText = toRenderText(renderer); updateCounterIfNeeded(renderText); renderText.dirtyLineBoxes(fullLayout); - } else if (is<RenderLineBreak>(renderer)) - downcast<RenderLineBreak>(renderer).dirtyLineBoxes(fullLayout); + } else if (renderer.isLineBreak()) + toRenderLineBreak(renderer).dirtyLineBoxes(fullLayout); else - downcast<RenderInline>(renderer).dirtyLineBoxes(fullLayout); + toRenderInline(renderer).dirtyLineBoxes(fullLayout); } static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox) @@ -184,21 +184,21 @@ static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox) return false; } -InlineFlowBox* RenderBlockFlow::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox) +InlineFlowBox* RenderBlockFlow::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox, bool startNewSegment) { // See if we have an unconstructed line box for this object that is also // the last item on the line. unsigned lineDepth = 1; - InlineFlowBox* parentBox = nullptr; - InlineFlowBox* result = nullptr; + InlineFlowBox* parentBox = 0; + InlineFlowBox* result = 0; bool hasDefaultLineBoxContain = style().lineBoxContain() == RenderStyle::initialLineBoxContain(); do { - ASSERT_WITH_SECURITY_IMPLICATION(is<RenderInline>(*obj) || obj == this); + ASSERT_WITH_SECURITY_IMPLICATION(obj->isRenderInline() || obj == this); - RenderInline* inlineFlow = obj != this ? downcast<RenderInline>(obj) : nullptr; + RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0; // Get the last box we made for this render object. - parentBox = inlineFlow ? inlineFlow->lastLineBox() : downcast<RenderBlockFlow>(*obj).lastRootBox(); + parentBox = inlineFlow ? inlineFlow->lastLineBox() : toRenderBlockFlow(obj)->lastRootBox(); // If this box or its ancestor is constructed then it is from a previous line, and we need // to make a new box for our line. If this box or its ancestor is unconstructed but it has @@ -207,12 +207,14 @@ InlineFlowBox* RenderBlockFlow::createLineBoxes(RenderObject* obj, const LineInf // the same line (this can happen with very fancy language mixtures). bool constructedNewBox = false; bool allowedToConstructNewBox = !hasDefaultLineBoxContain || !inlineFlow || inlineFlow->alwaysCreateLineBoxes(); - bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox); + bool mustCreateBoxesToRoot = startNewSegment && !(parentBox && parentBox->isRootInlineBox()); + bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox) && !mustCreateBoxesToRoot; if (allowedToConstructNewBox && !canUseExistingParentBox) { // We need to make a new box for this render object. Once // made, we need to place it at the end of the current line. InlineBox* newBox = createInlineBoxForRenderer(obj, obj == this); - parentBox = downcast<InlineFlowBox>(newBox); + ASSERT_WITH_SECURITY_IMPLICATION(newBox->isInlineFlowBox()); + parentBox = toInlineFlowBox(newBox); parentBox->setIsFirstLine(lineInfo.isFirstLine()); parentBox->setIsHorizontal(isHorizontalWritingMode()); if (!hasDefaultLineBoxContain) @@ -262,10 +264,10 @@ static bool reachedEndOfTextRenderer(const BidiRunList<BidiRun>& bidiRuns) if (!run) return true; unsigned pos = run->stop(); - const RenderObject& renderer = run->renderer(); - if (!is<RenderText>(renderer)) + const RenderObject& r = run->renderer(); + if (!r.isText()) return false; - const RenderText& renderText = downcast<RenderText>(renderer); + const RenderText& renderText = toRenderText(r); unsigned length = renderText.textLength(); if (pos >= length) return true; @@ -282,7 +284,6 @@ RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, co bool rootHasSelectedChildren = false; InlineFlowBox* parentBox = 0; int runCount = bidiRuns.runCount() - lineInfo.runsFromLeadingWhitespace(); - for (BidiRun* r = bidiRuns.firstRun(); r; r = r->next()) { // Create a box for our object. bool isOnlyRun = (runCount == 1); @@ -293,20 +294,24 @@ RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, co continue; InlineBox* box = createInlineBoxForRenderer(&r->renderer(), false, isOnlyRun); - r->setBox(box); + r->setBox(*box); if (!rootHasSelectedChildren && box->renderer().selectionState() != RenderObject::SelectionNone) rootHasSelectedChildren = true; - + // If we have no parent box yet, or if the run is not simply a sibling, // then we need to construct inline boxes as necessary to properly enclose the // run's inline box. Segments can only be siblings at the root level, as // they are positioned separately. - if (!parentBox || &parentBox->renderer() != r->renderer().parent()) { +#if ENABLE(CSS_SHAPES) + bool runStartsSegment = r->m_startsSegment; +#else + bool runStartsSegment = false; +#endif + if (!parentBox || &parentBox->renderer() != r->renderer().parent() || runStartsSegment) // Create new inline boxes all the way back to the appropriate insertion point. - RenderObject* parentToUse = r->renderer().parent(); - parentBox = createLineBoxes(parentToUse, lineInfo, box); - } else { + parentBox = createLineBoxes(r->renderer().parent(), lineInfo, box, runStartsSegment); + else { // Append the inline box to this line. parentBox->addToLine(box); } @@ -314,13 +319,13 @@ RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, co bool visuallyOrdered = r->renderer().style().rtlOrdering() == VisualOrder; box->setBidiLevel(r->level()); - if (is<InlineTextBox>(*box)) { - auto& textBox = downcast<InlineTextBox>(*box); - textBox.setStart(r->m_start); - textBox.setLen(r->m_stop - r->m_start); - textBox.setDirOverride(r->dirOverride(visuallyOrdered)); + if (box->isInlineTextBox()) { + InlineTextBox* text = toInlineTextBox(box); + text->setStart(r->m_start); + text->setLen(r->m_stop - r->m_start); + text->setDirOverride(r->dirOverride(visuallyOrdered)); if (r->m_hasHyphen) - textBox.setHasHyphen(true); + text->setHasHyphen(true); } } @@ -350,12 +355,6 @@ RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, co ETextAlign RenderBlockFlow::textAlignmentForLine(bool endsWithSoftBreak) const { ETextAlign alignment = style().textAlign(); -#if ENABLE(CSS3_TEXT) - TextJustify textJustify = style().textJustify(); - if (alignment == JUSTIFY && textJustify == TextJustifyNone) - return style().direction() == LTR ? LEFT : RIGHT; -#endif - if (endsWithSoftBreak) return alignment; @@ -380,7 +379,7 @@ ETextAlign RenderBlockFlow::textAlignmentForLine(bool endsWithSoftBreak) const case TextAlignLastJustify: return JUSTIFY; case TextAlignLastAuto: - if (textJustify == TextJustifyDistribute) + if (style().textJustify() == TextJustifyDistribute) return JUSTIFY; return TASTART; } @@ -414,7 +413,8 @@ static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection, totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth(); trailingSpaceRun->box()->setLogicalWidth(0); } - logicalLeft += std::max(0.f, availableLogicalWidth - totalLogicalWidth); + if (totalLogicalWidth < availableLogicalWidth) + logicalLeft += availableLogicalWidth - totalLogicalWidth; return; } @@ -441,8 +441,8 @@ static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection, void RenderBlockFlow::setMarginsForRubyRun(BidiRun* run, RenderRubyRun& renderer, RenderObject* previousObject, const LineInfo& lineInfo) { - float startOverhang; - float endOverhang; + int startOverhang; + int endOverhang; RenderObject* nextObject = 0; for (BidiRun* runWithNextObject = run->next(); runWithNextObject; runWithNextObject = runWithNextObject->next()) { if (!runWithNextObject->renderer().isOutOfFlowPositioned() && !runWithNextObject->box()->isLineBreak()) { @@ -455,13 +455,13 @@ void RenderBlockFlow::setMarginsForRubyRun(BidiRun* run, RenderRubyRun& renderer setMarginEndForChild(renderer, -endOverhang); } -static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* run, RenderText& renderer, float xPos, const LineInfo& lineInfo, +static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* run, RenderText* renderer, float xPos, const LineInfo& lineInfo, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements) { - HashSet<const Font*> fallbackFonts; + HashSet<const SimpleFontData*> fallbackFonts; GlyphOverflow glyphOverflow; - const FontCascade& font = lineStyle(*renderer.parent(), lineInfo).fontCascade(); + const Font& font = lineStyle(*renderer->parent(), lineInfo).font(); // Always compute glyph overflow if the block's line-box-contain value is "glyphs". if (lineBox->fitsToGlyphs()) { // If we don't stick out of the root line's font box, then don't bother computing our glyph overflow. This optimization @@ -477,13 +477,13 @@ static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* ru } LayoutUnit hyphenWidth = 0; - if (downcast<InlineTextBox>(*run->box()).hasHyphen()) + if (toInlineTextBox(run->box())->hasHyphen()) hyphenWidth = measureHyphenWidth(renderer, font, &fallbackFonts); float measuredWidth = 0; - bool kerningIsEnabled = font.enableKerning(); - bool canUseSimpleFontCodePath = renderer.canUseSimpleFontCodePath(); + bool kerningIsEnabled = font.typesettingFeatures() & Kerning; + bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath(); // Since we don't cache glyph overflows, we need to re-measure the run if // the style is linebox-contain: glyph. @@ -494,23 +494,23 @@ static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* ru WordMeasurement& wordMeasurement = wordMeasurements[i]; if (wordMeasurement.width <= 0 || wordMeasurement.startOffset == wordMeasurement.endOffset) continue; - if (wordMeasurement.renderer != &renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop) + if (wordMeasurement.renderer != renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop) continue; lastEndOffset = wordMeasurement.endOffset; if (kerningIsEnabled && lastEndOffset == run->m_stop) { int wordLength = lastEndOffset - wordMeasurement.startOffset; GlyphOverflow overflow; - measuredWidth += renderer.width(wordMeasurement.startOffset, wordLength, xPos + measuredWidth, lineInfo.isFirstLine(), + measuredWidth += renderer->width(wordMeasurement.startOffset, wordLength, xPos + measuredWidth, lineInfo.isFirstLine(), &wordMeasurement.fallbackFonts, &overflow); - UChar c = renderer.characterAt(wordMeasurement.startOffset); + UChar c = renderer->characterAt(wordMeasurement.startOffset); if (i > 0 && wordLength == 1 && (c == ' ' || c == '\t')) - measuredWidth += renderer.style().fontCascade().wordSpacing(); + measuredWidth += renderer->style().font().wordSpacing(); } else measuredWidth += wordMeasurement.width; if (!wordMeasurement.fallbackFonts.isEmpty()) { - HashSet<const Font*>::const_iterator end = wordMeasurement.fallbackFonts.end(); - for (HashSet<const Font*>::const_iterator it = wordMeasurement.fallbackFonts.begin(); it != end; ++it) + HashSet<const SimpleFontData*>::const_iterator end = wordMeasurement.fallbackFonts.end(); + for (HashSet<const SimpleFontData*>::const_iterator it = wordMeasurement.fallbackFonts.begin(); it != end; ++it) fallbackFonts.add(*it); } } @@ -522,114 +522,60 @@ static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* ru } if (!measuredWidth) - measuredWidth = renderer.width(run->m_start, run->m_stop - run->m_start, xPos, lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow); + measuredWidth = renderer->width(run->m_start, run->m_stop - run->m_start, xPos, lineInfo.isFirstLine(), &fallbackFonts, &glyphOverflow); run->box()->setLogicalWidth(measuredWidth + hyphenWidth); if (!fallbackFonts.isEmpty()) { ASSERT(run->box()->behavesLikeText()); - GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(downcast<InlineTextBox>(run->box()), std::make_pair(Vector<const Font*>(), GlyphOverflow())).iterator; + GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->box()), std::make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator; ASSERT(it->value.first.isEmpty()); copyToVector(fallbackFonts, it->value.first); run->box()->parent()->clearDescendantsHaveSameLineHeightAndBaseline(); } - - // Include text decoration visual overflow as part of the glyph overflow. - if (renderer.style().textDecorationsInEffect() != TextDecorationNone) - glyphOverflow.extendTo(visualOverflowForDecorations(run->box()->lineStyle(), downcast<InlineTextBox>(run->box()))); - - if (!glyphOverflow.isEmpty()) { + if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) { ASSERT(run->box()->behavesLikeText()); - GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(downcast<InlineTextBox>(run->box()), std::make_pair(Vector<const Font*>(), GlyphOverflow())).iterator; + GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->box()), std::make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator; it->value.second = glyphOverflow; run->box()->clearKnownToHaveNoOverflow(); } } -void RenderBlockFlow::updateRubyForJustifiedText(RenderRubyRun& rubyRun, BidiRun& r, const Vector<unsigned, 16>& expansionOpportunities, unsigned& expansionOpportunityCount, float& totalLogicalWidth, float availableLogicalWidth, size_t& i) -{ - if (!rubyRun.rubyBase() || !rubyRun.rubyBase()->firstRootBox() || rubyRun.rubyBase()->firstRootBox()->nextRootBox() || !r.renderer().style().collapseWhiteSpace()) - return; - - auto& rubyBase = *rubyRun.rubyBase(); - auto& rootBox = *rubyBase.firstRootBox(); - - float totalExpansion = 0; - unsigned totalOpportunitiesInRun = 0; - for (auto* leafChild = rootBox.firstLeafChild(); leafChild; leafChild = leafChild->nextLeafChild()) { - if (!leafChild->isInlineTextBox()) - continue; - - unsigned opportunitiesInRun = expansionOpportunities[i++]; - ASSERT(opportunitiesInRun <= expansionOpportunityCount); - auto expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount; - totalExpansion += expansion; - totalOpportunitiesInRun += opportunitiesInRun; - } - - ASSERT(!rubyRun.hasOverrideLogicalContentWidth()); - float newBaseWidth = rubyRun.logicalWidth() + totalExpansion + marginStartForChild(rubyRun) + marginEndForChild(rubyRun); - float newRubyRunWidth = rubyRun.logicalWidth() + totalExpansion; - rubyBase.setInitialOffset((newRubyRunWidth - newBaseWidth) / 2); - rubyRun.setOverrideLogicalContentWidth(newRubyRunWidth); - rubyRun.setNeedsLayout(MarkOnlyThis); - rootBox.markDirty(); - if (RenderRubyText* rubyText = rubyRun.rubyText()) { - if (RootInlineBox* textRootBox = rubyText->firstRootBox()) - textRootBox->markDirty(); - } - rubyRun.layoutBlock(true); - rubyRun.clearOverrideLogicalContentWidth(); - r.box()->setExpansion(newRubyRunWidth - r.box()->logicalWidth()); - - // This relayout caused the size of the RenderRubyText and the RenderRubyBase to change, dependent on the line's current expansion. Next time we relayout the - // RenderRubyRun, make sure that we relayout the RenderRubyBase and RenderRubyText as well. - rubyBase.setNeedsLayout(MarkOnlyThis); - if (RenderRubyText* rubyText = rubyRun.rubyText()) - rubyText->setNeedsLayout(MarkOnlyThis); - - totalLogicalWidth += totalExpansion; - expansionOpportunityCount -= totalOpportunitiesInRun; -} - -void RenderBlockFlow::computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, const Vector<unsigned, 16>& expansionOpportunities, unsigned expansionOpportunityCount, float totalLogicalWidth, float availableLogicalWidth) +static inline void computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, Vector<unsigned, 16>& expansionOpportunities, unsigned expansionOpportunityCount, float& totalLogicalWidth, float availableLogicalWidth) { if (!expansionOpportunityCount || availableLogicalWidth <= totalLogicalWidth) return; size_t i = 0; - for (BidiRun* run = firstRun; run; run = run->next()) { - if (!run->box() || run == trailingSpaceRun) + for (BidiRun* r = firstRun; r; r = r->next()) { +#if ENABLE(CSS_SHAPES) + // This method is called once per segment, do not move past the current segment. + if (r->m_startsSegment) + break; +#endif + if (!r->box() || r == trailingSpaceRun) continue; - if (is<RenderText>(run->renderer())) { + if (r->renderer().isText()) { unsigned opportunitiesInRun = expansionOpportunities[i++]; ASSERT(opportunitiesInRun <= expansionOpportunityCount); // Only justify text if whitespace is collapsed. - if (run->renderer().style().collapseWhiteSpace()) { - InlineTextBox& textBox = downcast<InlineTextBox>(*run->box()); - float expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount; - textBox.setExpansion(expansion); + if (r->renderer().style().collapseWhiteSpace()) { + InlineTextBox* textBox = toInlineTextBox(r->box()); + int expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount; + textBox->setExpansion(expansion); totalLogicalWidth += expansion; } expansionOpportunityCount -= opportunitiesInRun; - } else if (is<RenderRubyRun>(run->renderer())) - updateRubyForJustifiedText(downcast<RenderRubyRun>(run->renderer()), *run, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth, i); - - if (!expansionOpportunityCount) - break; + if (!expansionOpportunityCount) + break; + } } } -void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign, const RootInlineBox* rootInlineBox, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, int expansionOpportunityCount) +void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, int expansionOpportunityCount) { - TextDirection direction; - if (rootInlineBox && style().unicodeBidi() == Plaintext) - direction = rootInlineBox->direction(); - else - direction = style().direction(); - // Armed with the total width of the line (without justification), // we now examine our text-align property in order to determine where to position the // objects horizontally. The total width of the line can be increased if we end up @@ -658,13 +604,13 @@ void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign } FALLTHROUGH; case TASTART: - if (direction == LTR) + if (style().isLeftToRightDirection()) updateLogicalWidthForLeftAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); else updateLogicalWidthForRightAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); break; case TAEND: - if (direction == LTR) + if (style().isLeftToRightDirection()) updateLogicalWidthForRightAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); else updateLogicalWidthForLeftAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth); @@ -672,17 +618,11 @@ void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign } } -static void updateLogicalInlinePositions(RenderBlockFlow& block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, bool firstLine, - IndentTextOrNot shouldIndentText, LayoutUnit boxLogicalHeight, RootInlineBox* rootBox) +static void updateLogicalInlinePositions(RenderBlockFlow& block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, bool firstLine, IndentTextOrNot shouldIndentText, LayoutUnit boxLogicalHeight) { LayoutUnit lineLogicalHeight = block.minLineHeightForReplacedRenderer(firstLine, boxLogicalHeight); - if (rootBox->hasAnonymousInlineBlock()) { - lineLogicalLeft = block.logicalLeftOffsetForContent(block.logicalHeight()); - lineLogicalRight = block.logicalRightOffsetForContent(block.logicalHeight()); - } else { - lineLogicalLeft = block.logicalLeftOffsetForLine(block.logicalHeight(), shouldIndentText, lineLogicalHeight); - lineLogicalRight = block.logicalRightOffsetForLine(block.logicalHeight(), shouldIndentText, lineLogicalHeight); - } + lineLogicalLeft = block.pixelSnappedLogicalLeftOffsetForLine(block.logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight); + lineLogicalRight = block.pixelSnappedLogicalRightOffsetForLine(block.logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight); availableLogicalWidth = lineLogicalRight - lineLogicalLeft; } @@ -700,110 +640,50 @@ void RenderBlockFlow::computeInlineDirectionPositionsForLine(RootInlineBox* line float lineLogicalLeft; float lineLogicalRight; float availableLogicalWidth; - updateLogicalInlinePositions(*this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, 0, lineBox); + updateLogicalInlinePositions(*this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, 0); bool needsWordSpacing; +#if ENABLE(CSS_SHAPES) + ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo(); + if (shapeInsideInfo && shapeInsideInfo->hasSegments()) { + BidiRun* segmentStart = firstRun; + const SegmentList& segments = shapeInsideInfo->segments(); + float logicalLeft = std::max<float>(roundToInt(segments[0].logicalLeft), lineLogicalLeft); + float logicalRight = std::min<float>(floorToInt(segments[0].logicalRight), lineLogicalRight); + float startLogicalLeft = logicalLeft; + float endLogicalRight = logicalLeft; + float minLogicalLeft = logicalLeft; + float maxLogicalRight = logicalLeft; + lineBox->beginPlacingBoxRangesInInlineDirection(logicalLeft); + for (size_t i = 0; i < segments.size(); i++) { + if (i) { + logicalLeft = std::max<float>(roundToInt(segments[i].logicalLeft), lineLogicalLeft); + logicalRight = std::min<float>(floorToInt(segments[i].logicalRight), lineLogicalRight); + } + availableLogicalWidth = logicalRight - logicalLeft; + BidiRun* newSegmentStart = computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, logicalLeft, availableLogicalWidth, segmentStart, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements); + needsWordSpacing = false; + endLogicalRight = lineBox->placeBoxRangeInInlineDirection(segmentStart->box(), newSegmentStart ? newSegmentStart->box() : 0, logicalLeft, minLogicalLeft, maxLogicalRight, needsWordSpacing, textBoxDataMap); + if (!newSegmentStart || !newSegmentStart->next()) + break; + ASSERT(newSegmentStart->m_startsSegment); + // Discard the empty segment start marker bidi runs + segmentStart = newSegmentStart->next(); + } + lineBox->endPlacingBoxRangesInInlineDirection(startLogicalLeft, endLogicalRight, minLogicalLeft, maxLogicalRight); + return; + } +#endif if (firstRun && firstRun->renderer().isReplaced()) { - RenderBox& renderBox = downcast<RenderBox>(firstRun->renderer()); - updateLogicalInlinePositions(*this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, renderBox.logicalHeight(), lineBox); + RenderBox& renderBox = toRenderBox(firstRun->renderer()); + updateLogicalInlinePositions(*this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, renderBox.logicalHeight()); } computeInlineDirectionPositionsForSegment(lineBox, lineInfo, textAlign, lineLogicalLeft, availableLogicalWidth, firstRun, trailingSpaceRun, textBoxDataMap, verticalPositionCache, wordMeasurements); // The widths of all runs are now known. We can now place every inline box (and // compute accurate widths for the inline flow boxes). needsWordSpacing = false; - lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing); -} - -static inline ExpansionBehavior expansionBehaviorForInlineTextBox(RenderBlockFlow& block, InlineTextBox& textBox, BidiRun* previousRun, BidiRun* nextRun, ETextAlign textAlign, bool isAfterExpansion) -{ - // Tatechuyoko is modeled as the Object Replacement Character (U+FFFC), which can never have expansion opportunities inside nor intrinsically adjacent to it. - if (textBox.renderer().style().textCombine() == TextCombineHorizontal) - return ForbidLeadingExpansion | ForbidTrailingExpansion; - - ExpansionBehavior result = 0; - bool setLeadingExpansion = false; - bool setTrailingExpansion = false; - if (textAlign == JUSTIFY) { - // If the next box is ruby, and we're justifying, and the first box in the ruby base has a leading expansion, and we are a text box, then force a trailing expansion. - if (nextRun && is<RenderRubyRun>(nextRun->renderer()) && downcast<RenderRubyRun>(nextRun->renderer()).rubyBase() && nextRun->renderer().style().collapseWhiteSpace()) { - auto& rubyBase = *downcast<RenderRubyRun>(nextRun->renderer()).rubyBase(); - if (rubyBase.firstRootBox() && !rubyBase.firstRootBox()->nextRootBox()) { - if (auto* leafChild = rubyBase.firstRootBox()->firstLeafChild()) { - if (is<InlineTextBox>(*leafChild)) { - // FIXME: This leadingExpansionOpportunity doesn't actually work because it doesn't perform the UBA - if (FontCascade::leadingExpansionOpportunity(downcast<RenderText>(leafChild->renderer()).stringView(), leafChild->direction())) { - setTrailingExpansion = true; - result |= ForceTrailingExpansion; - } - } - } - } - } - // Same thing, except if we're following a ruby - if (previousRun && is<RenderRubyRun>(previousRun->renderer()) && downcast<RenderRubyRun>(previousRun->renderer()).rubyBase() && previousRun->renderer().style().collapseWhiteSpace()) { - auto& rubyBase = *downcast<RenderRubyRun>(previousRun->renderer()).rubyBase(); - if (rubyBase.firstRootBox() && !rubyBase.firstRootBox()->nextRootBox()) { - if (auto* leafChild = rubyBase.firstRootBox()->lastLeafChild()) { - if (is<InlineTextBox>(*leafChild)) { - // FIXME: This leadingExpansionOpportunity doesn't actually work because it doesn't perform the UBA - if (FontCascade::trailingExpansionOpportunity(downcast<RenderText>(leafChild->renderer()).stringView(), leafChild->direction())) { - setLeadingExpansion = true; - result |= ForceLeadingExpansion; - } - } - } - } - } - // If we're the first box inside a ruby base, forbid a leading expansion, and vice-versa - if (is<RenderRubyBase>(block)) { - RenderRubyBase& rubyBase = downcast<RenderRubyBase>(block); - if (&textBox == rubyBase.firstRootBox()->firstLeafChild()) { - setLeadingExpansion = true; - result |= ForbidLeadingExpansion; - } if (&textBox == rubyBase.firstRootBox()->lastLeafChild()) { - setTrailingExpansion = true; - result |= ForbidTrailingExpansion; - } - } - } - if (!setLeadingExpansion) - result |= isAfterExpansion ? ForbidLeadingExpansion : AllowLeadingExpansion; - if (!setTrailingExpansion) - result |= AllowTrailingExpansion; - return result; -} - -static inline void applyExpansionBehavior(InlineTextBox& textBox, ExpansionBehavior expansionBehavior) -{ - switch (expansionBehavior & LeadingExpansionMask) { - case ForceLeadingExpansion: - textBox.setForceLeadingExpansion(); - break; - case ForbidLeadingExpansion: - textBox.setCanHaveLeadingExpansion(false); - break; - case AllowLeadingExpansion: - textBox.setCanHaveLeadingExpansion(true); - break; - default: - ASSERT_NOT_REACHED(); - break; - } - switch (expansionBehavior & TrailingExpansionMask) { - case ForceTrailingExpansion: - textBox.setForceTrailingExpansion(); - break; - case ForbidTrailingExpansion: - textBox.setCanHaveTrailingExpansion(false); - break; - case AllowTrailingExpansion: - textBox.setCanHaveTrailingExpansion(true); - break; - default: - ASSERT_NOT_REACHED(); - break; - } + lineBox->placeBoxesInInlineDirection(lineLogicalLeft, needsWordSpacing, textBoxDataMap); } BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft, @@ -813,72 +693,56 @@ BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBo bool needsWordSpacing = false; float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth(); unsigned expansionOpportunityCount = 0; - bool isAfterExpansion = is<RenderRubyBase>(*this) ? downcast<RenderRubyBase>(*this).isAfterExpansion() : true; + bool isAfterExpansion = true; Vector<unsigned, 16> expansionOpportunities; - - BidiRun* run = firstRun; - BidiRun* previousRun = nullptr; - for (; run; run = run->next()) { - if (!run->box() || run->renderer().isOutOfFlowPositioned() || run->box()->isLineBreak()) { + RenderObject* previousObject = 0; + + BidiRun* r = firstRun; + for (; r; r = r->next()) { +#if ENABLE(CSS_SHAPES) + // Once we have reached the start of the next segment, we have finished + // computing the positions for this segment's contents. + if (r->m_startsSegment) + break; +#endif + if (!r->box() || r->renderer().isOutOfFlowPositioned() || r->box()->isLineBreak()) continue; // Positioned objects are only participating to figure out their // correct static x position. They have no effect on the width. // Similarly, line break boxes have no effect on the width. - } - if (is<RenderText>(run->renderer())) { - auto& renderText = downcast<RenderText>(run->renderer()); - auto& textBox = downcast<InlineTextBox>(*run->box()); - if (textAlign == JUSTIFY && run != trailingSpaceRun) { - ExpansionBehavior expansionBehavior = expansionBehaviorForInlineTextBox(*this, textBox, previousRun, run->next(), textAlign, isAfterExpansion); - applyExpansionBehavior(textBox, expansionBehavior); + if (r->renderer().isText()) { + RenderText& rt = toRenderText(r->renderer()); + if (textAlign == JUSTIFY && r != trailingSpaceRun) { + if (!isAfterExpansion) + toInlineTextBox(r->box())->setCanHaveLeadingExpansion(true); unsigned opportunitiesInRun; - std::tie(opportunitiesInRun, isAfterExpansion) = FontCascade::expansionOpportunityCount(renderText.stringView(run->m_start, run->m_stop), run->box()->direction(), expansionBehavior); + if (rt.is8Bit()) + opportunitiesInRun = Font::expansionOpportunityCount(rt.characters8() + r->m_start, r->m_stop - r->m_start, r->box()->direction(), isAfterExpansion); + else + opportunitiesInRun = Font::expansionOpportunityCount(rt.characters16() + r->m_start, r->m_stop - r->m_start, r->box()->direction(), isAfterExpansion); expansionOpportunities.append(opportunitiesInRun); expansionOpportunityCount += opportunitiesInRun; } - if (int length = renderText.textLength()) { - if (!run->m_start && needsWordSpacing && isSpaceOrNewline(renderText.characterAt(run->m_start))) - totalLogicalWidth += lineStyle(*renderText.parent(), lineInfo).fontCascade().wordSpacing(); - needsWordSpacing = !isSpaceOrNewline(renderText.characterAt(run->m_stop - 1)) && run->m_stop == length; + if (int length = rt.textLength()) { + if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt.characterAt(r->m_start))) + totalLogicalWidth += lineStyle(*rt.parent(), lineInfo).font().wordSpacing(); + needsWordSpacing = !isSpaceOrNewline(rt.characterAt(r->m_stop - 1)) && r->m_stop == length; } - setLogicalWidthForTextRun(lineBox, run, renderText, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements); + setLogicalWidthForTextRun(lineBox, r, &rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements); } else { - bool encounteredJustifiedRuby = false; - if (is<RenderRubyRun>(run->renderer()) && textAlign == JUSTIFY && run != trailingSpaceRun && downcast<RenderRubyRun>(run->renderer()).rubyBase()) { - auto* rubyBase = downcast<RenderRubyRun>(run->renderer()).rubyBase(); - if (rubyBase->firstRootBox() && !rubyBase->firstRootBox()->nextRootBox() && run->renderer().style().collapseWhiteSpace()) { - rubyBase->setIsAfterExpansion(isAfterExpansion); - for (auto* leafChild = rubyBase->firstRootBox()->firstLeafChild(); leafChild; leafChild = leafChild->nextLeafChild()) { - if (!is<InlineTextBox>(*leafChild)) - continue; - auto& textBox = downcast<InlineTextBox>(*leafChild); - encounteredJustifiedRuby = true; - auto& renderText = downcast<RenderText>(leafChild->renderer()); - ExpansionBehavior expansionBehavior = expansionBehaviorForInlineTextBox(*rubyBase, textBox, nullptr, nullptr, textAlign, isAfterExpansion); - applyExpansionBehavior(textBox, expansionBehavior); - unsigned opportunitiesInRun; - std::tie(opportunitiesInRun, isAfterExpansion) = FontCascade::expansionOpportunityCount(renderText.stringView(), leafChild->direction(), expansionBehavior); - expansionOpportunities.append(opportunitiesInRun); - expansionOpportunityCount += opportunitiesInRun; - } - } - } - - if (!encounteredJustifiedRuby) - isAfterExpansion = false; - - if (!is<RenderInline>(run->renderer())) { - auto& renderBox = downcast<RenderBox>(run->renderer()); - if (is<RenderRubyRun>(renderBox)) - setMarginsForRubyRun(run, downcast<RenderRubyRun>(renderBox), previousRun ? &previousRun->renderer() : nullptr, lineInfo); - run->box()->setLogicalWidth(logicalWidthForChild(renderBox)); + isAfterExpansion = false; + if (!r->renderer().isRenderInline()) { + RenderBox& renderBox = toRenderBox(r->renderer()); + if (renderBox.isRubyRun()) + setMarginsForRubyRun(r, toRenderRubyRun(renderBox), previousObject, lineInfo); + r->box()->setLogicalWidth(logicalWidthForChild(renderBox)); totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox); } } - totalLogicalWidth += run->box()->logicalWidth(); - previousRun = run; + totalLogicalWidth += r->box()->logicalWidth(); + previousObject = &r->renderer(); } if (isAfterExpansion && !expansionOpportunities.isEmpty()) { @@ -886,43 +750,11 @@ BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBo expansionOpportunityCount--; } - if (is<RenderRubyBase>(*this) && !expansionOpportunityCount) - textAlign = CENTER; - - updateLogicalWidthForAlignment(textAlign, lineBox, trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth, expansionOpportunityCount); + updateLogicalWidthForAlignment(textAlign, trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth, expansionOpportunityCount); computeExpansionForJustifiedText(firstRun, trailingSpaceRun, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth); - return run; -} - -void RenderBlockFlow::removeInlineBox(BidiRun& run, const RootInlineBox& rootLineBox) const -{ - auto* inlineBox = run.box(); -#if !ASSERT_DISABLED - auto* inlineParent = inlineBox->parent(); - while (inlineParent && inlineParent != &rootLineBox) { - ASSERT(!inlineParent->isDirty()); - inlineParent = inlineParent->parent(); - } - ASSERT(!rootLineBox.isDirty()); -#endif - auto* parent = inlineBox->parent(); - inlineBox->removeFromParent(); - - auto& renderer = run.renderer(); - if (is<RenderText>(renderer)) - downcast<RenderText>(renderer).removeTextBox(downcast<InlineTextBox>(*inlineBox)); - delete inlineBox; - run.setBox(nullptr); - // removeFromParent() unnecessarily dirties the ancestor subtree. - auto* ancestor = parent; - while (ancestor) { - ancestor->markDirty(false); - if (ancestor == &rootLineBox) - break; - ancestor = ancestor->parent(); - } + return r; } void RenderBlockFlow::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, @@ -931,33 +763,30 @@ void RenderBlockFlow::computeBlockDirectionPositionsForLine(RootInlineBox* lineB setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache)); // Now make sure we place replaced render objects correctly. - for (auto* run = firstRun; run; run = run->next()) { - ASSERT(run->box()); - if (!run->box()) + for (BidiRun* r = firstRun; r; r = r->next()) { + ASSERT(r->box()); + if (!r->box()) continue; // Skip runs with no line boxes. + InlineBox& box = *r->box(); + // Align positioned boxes with the top of the line box. This is // a reasonable approximation of an appropriate y position. - auto& renderer = run->renderer(); - if (renderer.isOutOfFlowPositioned()) - run->box()->setLogicalTop(logicalHeight()); + if (r->renderer().isOutOfFlowPositioned()) + box.setLogicalTop(logicalHeight()); // Position is used to properly position both replaced elements and // to update the static normal flow x/y of positioned elements. - bool inlineBoxIsRedundant = false; - if (is<RenderText>(renderer)) { - auto& inlineTextBox = downcast<InlineTextBox>(*run->box()); - downcast<RenderText>(renderer).positionLineBox(inlineTextBox); - inlineBoxIsRedundant = !inlineTextBox.len(); - } else if (is<RenderBox>(renderer)) { - downcast<RenderBox>(renderer).positionLineBox(downcast<InlineElementBox>(*run->box())); - inlineBoxIsRedundant = renderer.isOutOfFlowPositioned(); - } else if (is<RenderLineBreak>(renderer)) - downcast<RenderLineBreak>(renderer).replaceInlineBoxWrapper(downcast<InlineElementBox>(*run->box())); - // Check if we need to keep this box on the line at all. - if (inlineBoxIsRedundant) - removeInlineBox(*run, *lineBox); - } + if (r->renderer().isText()) + toRenderText(r->renderer()).positionLineBox(toInlineTextBox(box)); + else if (r->renderer().isBox()) + toRenderBox(r->renderer()).positionLineBox(toInlineElementBox(box)); + else if (r->renderer().isLineBreak()) + toRenderLineBreak(r->renderer()).replaceInlineBoxWrapper(toInlineElementBox(box)); + } + // Positioned objects and zero-length text nodes destroy their boxes in + // position(), which unnecessarily dirties the line. + lineBox->markDirty(false); } static inline bool isCollapsibleSpace(UChar character, const RenderText& renderer) @@ -990,14 +819,14 @@ inline BidiRun* RenderBlockFlow::handleTrailingSpaces(BidiRunList<BidiRun>& bidi if (!bidiRuns.runCount() || !bidiRuns.logicallyLastRun()->renderer().style().breakOnlyAfterWhiteSpace() || !bidiRuns.logicallyLastRun()->renderer().style().autoWrap()) - return nullptr; + return 0; BidiRun* trailingSpaceRun = bidiRuns.logicallyLastRun(); const RenderObject& lastObject = trailingSpaceRun->renderer(); - if (!is<RenderText>(lastObject)) - return nullptr; + if (!lastObject.isText()) + return 0; - const RenderText& lastText = downcast<RenderText>(lastObject); + const RenderText& lastText = toRenderText(lastObject); int firstSpace; if (lastText.is8Bit()) firstSpace = findFirstTrailingSpace(lastText, lastText.characters8(), trailingSpaceRun->start(), trailingSpaceRun->stop()); @@ -1005,7 +834,7 @@ inline BidiRun* RenderBlockFlow::handleTrailingSpaces(BidiRunList<BidiRun>& bidi firstSpace = findFirstTrailingSpace(lastText, lastText.characters16(), trailingSpaceRun->start(), trailingSpaceRun->stop()); if (firstSpace == trailingSpaceRun->stop()) - return nullptr; + return 0; TextDirection direction = style().direction(); bool shouldReorder = trailingSpaceRun != (direction == LTR ? bidiRuns.lastRun() : bidiRuns.firstRun()); @@ -1038,30 +867,20 @@ inline BidiRun* RenderBlockFlow::handleTrailingSpaces(BidiRunList<BidiRun>& bidi void RenderBlockFlow::appendFloatingObjectToLastLine(FloatingObject* floatingObject) { - ASSERT_WITH_SECURITY_IMPLICATION(!floatingObject->originatingLine()); + ASSERT(!floatingObject->originatingLine()); floatingObject->setOriginatingLine(lastRootBox()); lastRootBox()->appendFloat(floatingObject->renderer()); } -static inline void notifyResolverToResumeInIsolate(InlineBidiResolver& resolver, RenderObject* root, RenderObject* startObject) +static inline void setUpResolverToResumeInIsolate(InlineBidiResolver& resolver, RenderObject* root, RenderObject* startObject) { if (root != startObject) { RenderObject* parent = startObject->parent(); - notifyResolverToResumeInIsolate(resolver, root, parent); + setUpResolverToResumeInIsolate(resolver, root, parent); notifyObserverEnteredObject(&resolver, startObject); } } -static inline void setUpResolverToResumeInIsolate(InlineBidiResolver& resolver, InlineBidiResolver& topResolver, BidiRun& isolatedRun, RenderObject* root, RenderObject* startObject) -{ - // Set up m_midpointState - resolver.midpointState() = topResolver.midpointState(); - resolver.midpointState().setCurrentMidpoint(topResolver.midpointForIsolatedRun(isolatedRun)); - - // Set up m_nestedIsolateCount - notifyResolverToResumeInIsolate(resolver, root, startObject); -} - // FIXME: BidiResolver should have this logic. static inline void constructBidiRunsForSegment(InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfRuns, VisualDirectionOverride override, bool previousLineBrokeCleanly) { @@ -1074,37 +893,36 @@ static inline void constructBidiRunsForSegment(InlineBidiResolver& topResolver, while (!topResolver.isolatedRuns().isEmpty()) { // It does not matter which order we resolve the runs as long as we resolve them all. - auto isolatedRun = WTFMove(topResolver.isolatedRuns().last()); + BidiRun* isolatedRun = topResolver.isolatedRuns().last(); topResolver.isolatedRuns().removeLast(); - currentRoot = &isolatedRun.root; - RenderObject& startObject = isolatedRun.object; + RenderObject& startObject = isolatedRun->renderer(); // Only inlines make sense with unicode-bidi: isolate (blocks are already isolated). // FIXME: Because enterIsolate is not passed a RenderObject, we have to crawl up the // tree to see which parent inline is the isolate. We could change enterIsolate // to take a RenderObject and do this logic there, but that would be a layering // violation for BidiResolver (which knows nothing about RenderObject). - RenderInline* isolatedInline = downcast<RenderInline>(highestContainingIsolateWithinRoot(startObject, currentRoot)); + RenderInline* isolatedInline = toRenderInline(highestContainingIsolateWithinRoot(startObject, currentRoot)); ASSERT(isolatedInline); InlineBidiResolver isolatedResolver; EUnicodeBidi unicodeBidi = isolatedInline->style().unicodeBidi(); TextDirection direction; if (unicodeBidi == Plaintext) - determineDirectionality(direction, InlineIterator(isolatedInline, &isolatedRun.object, 0)); + determineDirectionality(direction, InlineIterator(isolatedInline, &isolatedRun->renderer(), 0)); else { ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride); direction = isolatedInline->style().direction(); } isolatedResolver.setStatus(BidiStatus(direction, isOverride(unicodeBidi))); - setUpResolverToResumeInIsolate(isolatedResolver, topResolver, isolatedRun.runToReplace, isolatedInline, &startObject); + setUpResolverToResumeInIsolate(isolatedResolver, isolatedInline, &startObject); // The starting position is the beginning of the first run within the isolate that was identified // during the earlier call to createBidiRunsForLine. This can be but is not necessarily the // first run within the isolate. - InlineIterator iter = InlineIterator(isolatedInline, &startObject, isolatedRun.position); + InlineIterator iter = InlineIterator(isolatedInline, &startObject, isolatedRun->m_start); isolatedResolver.setPositionIgnoringNestedIsolates(iter); // We stop at the next end of line; we may re-enter this isolate in the next call to constructBidiRuns(). @@ -1116,36 +934,74 @@ static inline void constructBidiRunsForSegment(InlineBidiResolver& topResolver, // itself to be turned into an InlineBox. We can't remove it here without potentially losing track of // the logically last run. if (isolatedResolver.runs().runCount()) - bidiRuns.replaceRunWithRuns(&isolatedRun.runToReplace, isolatedResolver.runs()); + bidiRuns.replaceRunWithRuns(isolatedRun, isolatedResolver.runs()); // If we encountered any nested isolate runs, just move them // to the top resolver's list for later processing. - while (!isolatedResolver.isolatedRuns().isEmpty()) { - auto runWithContext = WTFMove(isolatedResolver.isolatedRuns().last()); - isolatedResolver.isolatedRuns().removeLast(); - topResolver.setMidpointForIsolatedRun(runWithContext.runToReplace, isolatedResolver.midpointForIsolatedRun(runWithContext.runToReplace)); - topResolver.isolatedRuns().append(WTFMove(runWithContext)); + if (!isolatedResolver.isolatedRuns().isEmpty()) { + topResolver.isolatedRuns().appendVector(isolatedResolver.isolatedRuns()); + isolatedResolver.isolatedRuns().clear(); + currentRoot = isolatedInline; + } + } +} + +static inline void constructBidiRunsForLine(const RenderBlockFlow* block, InlineBidiResolver& topResolver, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& endOfLine, VisualDirectionOverride override, bool previousLineBrokeCleanly) +{ +#if !ENABLE(CSS_SHAPES) + UNUSED_PARAM(block); + constructBidiRunsForSegment(topResolver, bidiRuns, endOfLine, override, previousLineBrokeCleanly); +#else + ShapeInsideInfo* shapeInsideInfo = block->layoutShapeInsideInfo(); + if (!shapeInsideInfo || !shapeInsideInfo->hasSegments()) { + constructBidiRunsForSegment(topResolver, bidiRuns, endOfLine, override, previousLineBrokeCleanly); + return; + } + + const SegmentRangeList& segmentRanges = shapeInsideInfo->segmentRanges(); + ASSERT(segmentRanges.size()); + + for (size_t i = 0; i < segmentRanges.size(); i++) { + LineSegmentIterator iterator = segmentRanges[i].start; + InlineIterator segmentStart(iterator.root, iterator.object, iterator.offset); + iterator = segmentRanges[i].end; + InlineIterator segmentEnd(iterator.root, iterator.object, iterator.offset); + if (i) { + ASSERT(segmentStart.renderer()); + BidiRun* segmentMarker = createRun(segmentStart.offset(), segmentStart.offset(), segmentStart.renderer(), topResolver); + segmentMarker->m_startsSegment = true; + bidiRuns.addRun(segmentMarker); + // Do not collapse midpoints between segments + topResolver.midpointState().setBetweenMidpoints(false); } + if (segmentStart == segmentEnd) + continue; + topResolver.setPosition(segmentStart, numberOfIsolateAncestors(segmentStart)); + constructBidiRunsForSegment(topResolver, bidiRuns, segmentEnd, override, previousLineBrokeCleanly); } +#endif } // This function constructs line boxes for all of the text runs in the resolver and computes their position. -RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(unsigned bidiLevel, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements) +RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements) { if (!bidiRuns.runCount()) - return nullptr; + return 0; // FIXME: Why is this only done when we had runs? lineInfo.setLastLine(!end.renderer()); RootInlineBox* lineBox = constructLine(bidiRuns, lineInfo); if (!lineBox) - return nullptr; + return 0; - lineBox->setBidiLevel(bidiLevel); lineBox->setEndsWithBreak(lineInfo.previousLineBrokeCleanly()); - bool isSVGRootInlineBox = is<SVGRootInlineBox>(*lineBox); +#if ENABLE(SVG) + bool isSVGRootInlineBox = lineBox->isSVGRootInlineBox(); +#else + bool isSVGRootInlineBox = false; +#endif GlyphOverflowAndFallbackFontsMap textBoxDataMap; @@ -1156,6 +1012,7 @@ RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(unsigned bidiLevel, // Now position our text runs vertically. computeBlockDirectionPositionsForLine(lineBox, bidiRuns.firstRun(), textBoxDataMap, verticalPositionCache); +#if ENABLE(SVG) // SVG text layout code computes vertical & horizontal positions on its own. // Note that we still need to execute computeVerticalPositionsForLine() as // it calls InlineTextBox::positionLineBox(), which tracks whether the box @@ -1163,12 +1020,18 @@ RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(unsigned bidiLevel, // text selection in RTL boxes would not work as expected. if (isSVGRootInlineBox) { ASSERT_WITH_SECURITY_IMPLICATION(isSVGText()); - downcast<SVGRootInlineBox>(*lineBox).computePerCharacterLayoutInformation(); + toSVGRootInlineBox(lineBox)->computePerCharacterLayoutInformation(); } +#endif // Compute our overflow now. lineBox->computeOverflow(lineBox->lineTop(), lineBox->lineBottom(), textBoxDataMap); +#if PLATFORM(MAC) + // Highlight acts as an overflow inflation. + if (style().highlight() != nullAtom) + lineBox->addHighlightOverflow(); +#endif return lineBox; } @@ -1190,9 +1053,6 @@ void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState, bool has // We want to skip ahead to the first dirty line InlineBidiResolver resolver; RootInlineBox* startLine = determineStartPosition(layoutState, resolver); - - if (startLine) - marginCollapseLinesFromStart(layoutState, startLine); unsigned consecutiveHyphenatedLines = 0; if (startLine) { @@ -1210,7 +1070,7 @@ void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState, bool has // that the block really needed a full layout, we missed our chance to repaint the layer // before layout started. Luckily the layer has cached the repaint rect for its original // position and size, and so we can use that to make a repaint happen now. - repaintUsingContainer(containerForRepaint(), layer()->repaintRect()); + repaintUsingContainer(containerForRepaint(), pixelSnappedIntRect(layer()->repaintRect())); } } @@ -1241,7 +1101,7 @@ void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState, bool has if (lastObject->isBR()) { EClear clear = lastObject->style().clear(); if (clear != CNONE) - clearFloats(clear); + newLine(clear); } } } @@ -1251,6 +1111,16 @@ void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState, bool has repaintDirtyFloats(layoutState.floats()); } +RenderTextInfo::RenderTextInfo() + : m_text(0) + , m_font(0) +{ +} + +RenderTextInfo::~RenderTextInfo() +{ +} + // Before restarting the layout loop with a new logicalHeight, remove all floats that were added and reset the resolver. inline const InlineIterator& RenderBlockFlow::restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastFloatFromPreviousLine, InlineBidiResolver& resolver, const InlineIterator& oldEnd) { @@ -1260,6 +1130,170 @@ inline const InlineIterator& RenderBlockFlow::restartLayoutRunsAndFloatsInRange( return oldEnd; } +#if ENABLE(CSS_SHAPES) +static inline void pushShapeContentOverflowBelowTheContentBox(RenderBlockFlow* block, ShapeInsideInfo* shapeInsideInfo, LayoutUnit lineTop, LayoutUnit lineHeight) +{ + ASSERT(shapeInsideInfo); + + LayoutUnit logicalLineBottom = lineTop + lineHeight; + LayoutUnit shapeLogicalBottom = shapeInsideInfo->shapeLogicalBottom(); + LayoutUnit shapeContainingBlockLogicalHeight = shapeInsideInfo->shapeContainingBlockLogicalHeight(); + + bool isOverflowPositionedAlready = (shapeContainingBlockLogicalHeight - shapeInsideInfo->owner().borderAndPaddingAfter() + lineHeight) <= lineTop; + + // If the last line overlaps with the shape, we don't need the segments anymore + if (lineTop < shapeLogicalBottom && shapeLogicalBottom < logicalLineBottom) + shapeInsideInfo->clearSegments(); + + if (logicalLineBottom <= shapeLogicalBottom || !shapeContainingBlockLogicalHeight || isOverflowPositionedAlready) + return; + + LayoutUnit newLogicalHeight = block->logicalHeight() + (shapeContainingBlockLogicalHeight - (lineTop + shapeInsideInfo->owner().borderAndPaddingAfter())); + block->setLogicalHeight(newLogicalHeight); +} + +void RenderBlockFlow::updateShapeAndSegmentsForCurrentLine(ShapeInsideInfo*& shapeInsideInfo, const LayoutSize& logicalOffsetFromShapeContainer, LineLayoutState& layoutState) +{ + if (layoutState.flowThread()) + return updateShapeAndSegmentsForCurrentLineInFlowThread(shapeInsideInfo, layoutState); + + if (!shapeInsideInfo) + return; + + LayoutUnit lineTop = logicalHeight() + logicalOffsetFromShapeContainer.height(); + LayoutUnit lineLeft = logicalOffsetFromShapeContainer.width(); + LayoutUnit lineHeight = this->lineHeight(layoutState.lineInfo().isFirstLine(), isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); + + // FIXME: Bug 95361: It is possible for a line to grow beyond lineHeight, in which case these segments may be incorrect. + shapeInsideInfo->updateSegmentsForLine(LayoutSize(lineLeft, lineTop), lineHeight); + + pushShapeContentOverflowBelowTheContentBox(this, shapeInsideInfo, lineTop, lineHeight); +} + +void RenderBlockFlow::updateShapeAndSegmentsForCurrentLineInFlowThread(ShapeInsideInfo*& shapeInsideInfo, LineLayoutState& layoutState) +{ + ASSERT(layoutState.flowThread()); + + RenderRegion* currentRegion = regionAtBlockOffset(logicalHeight()); + if (!currentRegion || !currentRegion->logicalHeight()) + return; + + shapeInsideInfo = currentRegion->shapeInsideInfo(); + + RenderRegion* nextRegion = 0; + if (!currentRegion->isLastRegion()) { + RenderRegionList regionList = layoutState.flowThread()->renderRegionList(); + auto it = regionList.find(currentRegion); + nextRegion = *(++it); + } + + // We only want to deal regions with shapes, so we check if the next region has a shape + if (!shapeInsideInfo && nextRegion && !nextRegion->shapeInsideInfo()) + return; + + LayoutUnit lineHeight = this->lineHeight(layoutState.lineInfo().isFirstLine(), isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes); + LayoutUnit logicalLineTopInFlowThread = logicalHeight() + offsetFromLogicalTopOfFirstPage(); + LayoutUnit logicalLineBottomInFlowThread = logicalLineTopInFlowThread + lineHeight; + LayoutUnit logicalRegionTopInFlowThread = currentRegion->logicalTopForFlowThreadContent(); + LayoutUnit logicalRegionBottomInFlowThread = logicalRegionTopInFlowThread + currentRegion->logicalHeight() - currentRegion->borderAndPaddingBefore() - currentRegion->borderAndPaddingAfter(); + + LayoutUnit shapeBottomInFlowThread = LayoutUnit::max(); + if (shapeInsideInfo) + shapeBottomInFlowThread = shapeInsideInfo->shapeLogicalBottom() + currentRegion->logicalTopForFlowThreadContent(); + + bool lineOverLapsWithShapeBottom = shapeBottomInFlowThread < logicalLineBottomInFlowThread; + bool lineTopAdjustedIntoNextRegion = layoutState.adjustedLogicalLineTop() >= currentRegion->logicalHeight(); + bool lineOverLapsWithRegionBottom = logicalLineBottomInFlowThread > logicalRegionBottomInFlowThread || lineTopAdjustedIntoNextRegion; + bool overFlowsToNextRegion = nextRegion && (lineOverLapsWithShapeBottom || lineOverLapsWithRegionBottom); + + // If the line is between two shapes/regions we position the line to the top of the next shape/region + if (overFlowsToNextRegion) { + ASSERT(currentRegion != nextRegion); + LayoutUnit deltaToNextRegion = logicalRegionBottomInFlowThread - logicalLineTopInFlowThread; + setLogicalHeight(logicalHeight() + deltaToNextRegion); + + currentRegion = nextRegion; + shapeInsideInfo = currentRegion->shapeInsideInfo(); + + logicalLineTopInFlowThread = logicalHeight() + offsetFromLogicalTopOfFirstPage(); + logicalLineBottomInFlowThread = logicalLineTopInFlowThread + lineHeight; + logicalRegionTopInFlowThread = currentRegion->logicalTopForFlowThreadContent(); + logicalRegionBottomInFlowThread = logicalRegionTopInFlowThread + currentRegion->logicalHeight() - currentRegion->borderAndPaddingBefore() - currentRegion->borderAndPaddingAfter(); + + if (lineTopAdjustedIntoNextRegion) + layoutState.setAdjustedLogicalLineTop(0); + } + + if (!shapeInsideInfo) + return; + + bool isFirstLineInRegion = logicalLineBottomInFlowThread <= (logicalRegionTopInFlowThread + lineHeight); + bool isFirstLineAdjusted = (logicalLineTopInFlowThread - logicalRegionTopInFlowThread) < (layoutState.adjustedLogicalLineTop() - currentRegion->borderAndPaddingBefore()); + // We position the first line to the top of the shape in the region or to the previously adjusted position in the shape + if (isFirstLineInRegion || isFirstLineAdjusted) { + LayoutUnit shapeTopOffset = layoutState.adjustedLogicalLineTop(); + + if (!shapeTopOffset && (shapeInsideInfo->shapeLogicalTop() > 0)) + shapeTopOffset = shapeInsideInfo->shapeLogicalTop(); + + LayoutUnit shapePositionInFlowThread = currentRegion->logicalTopForFlowThreadContent() + shapeTopOffset; + LayoutUnit shapeTopLineTopDelta = shapePositionInFlowThread - logicalLineTopInFlowThread - currentRegion->borderAndPaddingBefore(); + + setLogicalHeight(logicalHeight() + shapeTopLineTopDelta); + logicalLineTopInFlowThread += shapeTopLineTopDelta; + layoutState.setAdjustedLogicalLineTop(0); + } + + LayoutUnit lineTop = logicalLineTopInFlowThread - currentRegion->logicalTopForFlowThreadContent() + currentRegion->borderAndPaddingBefore(); + + // FIXME: 118571 - Shape inside on a region does not yet take into account its padding for nested flow blocks + shapeInsideInfo->updateSegmentsForLine(LayoutSize(0, lineTop), lineHeight); + + if (currentRegion->isLastRegion()) + pushShapeContentOverflowBelowTheContentBox(this, shapeInsideInfo, lineTop, lineHeight); +} + +static inline LayoutUnit adjustLogicalLineTop(ShapeInsideInfo* shapeInsideInfo, InlineIterator start, InlineIterator end, const WordMeasurements& wordMeasurements) +{ + if (!shapeInsideInfo || end != start) + return 0; + + float minWidth = firstPositiveWidth(wordMeasurements); + ASSERT(minWidth || wordMeasurements.isEmpty()); + if (minWidth > 0 && shapeInsideInfo->adjustLogicalLineTop(minWidth)) + return shapeInsideInfo->logicalLineTop(); + + return shapeInsideInfo->shapeLogicalBottom(); +} + +bool RenderBlockFlow::adjustLogicalLineTopAndLogicalHeightIfNeeded(ShapeInsideInfo* shapeInsideInfo, LayoutUnit absoluteLogicalTop, LineLayoutState& layoutState, InlineBidiResolver& resolver, FloatingObject* lastFloatFromPreviousLine, InlineIterator& end, WordMeasurements& wordMeasurements) +{ + LayoutUnit adjustedLogicalLineTop = adjustLogicalLineTop(shapeInsideInfo, resolver.position(), end, wordMeasurements); + + if (shapeInsideInfo && containsFloats()) { + lastFloatFromPreviousLine = m_floatingObjects->set().last().get(); + if (!wordMeasurements.size()) { + LayoutUnit floatLogicalTopOffset = shapeInsideInfo->computeFirstFitPositionForFloat(logicalSizeForFloat(lastFloatFromPreviousLine)); + if (logicalHeight() < floatLogicalTopOffset) + adjustedLogicalLineTop = floatLogicalTopOffset; + } + } + + if (!adjustedLogicalLineTop) + return false; + + LayoutUnit newLogicalHeight = adjustedLogicalLineTop - absoluteLogicalTop; + + if (layoutState.flowThread()) { + layoutState.setAdjustedLogicalLineTop(adjustedLogicalLineTop); + newLogicalHeight = logicalHeight(); + } + + end = restartLayoutRunsAndFloatsInRange(logicalHeight(), newLogicalHeight, lastFloatFromPreviousLine, resolver, end); + return true; +} +#endif + void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, InlineBidiResolver& resolver, const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus, unsigned consecutiveHyphenatedLines) { const RenderStyle& styleToUse = style(); @@ -1272,13 +1306,32 @@ void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, I LineBreaker lineBreaker(*this); +#if ENABLE(CSS_SHAPES) + LayoutSize logicalOffsetFromShapeContainer; + ShapeInsideInfo* shapeInsideInfo = layoutShapeInsideInfo(); + if (shapeInsideInfo) { + ASSERT(&shapeInsideInfo->owner() == this || allowsShapeInsideInfoSharing()); + if (shapeInsideInfo != this->shapeInsideInfo()) { + // FIXME Bug 100284: If subsequent LayoutStates are pushed, we will have to add + // their offsets from the original shape-inside container. + logicalOffsetFromShapeContainer = logicalOffsetFromShapeAncestorContainer(&shapeInsideInfo->owner()); + } + // Begin layout at the logical top of our shape inside. + if (logicalHeight() + logicalOffsetFromShapeContainer.height() < shapeInsideInfo->shapeLogicalTop()) { + LayoutUnit logicalHeight = shapeInsideInfo->shapeLogicalTop() - logicalOffsetFromShapeContainer.height(); + if (layoutState.flowThread()) + logicalHeight -= shapeInsideInfo->owner().borderAndPaddingBefore(); + setLogicalHeight(logicalHeight); + } + } +#endif + while (!end.atEnd()) { // FIXME: Is this check necessary before the first iteration or can it be moved to the end? if (checkForEndLineMatch) { layoutState.setEndLineMatched(matchedEndLine(layoutState, resolver, cleanLineStart, cleanLineBidiStatus)); if (layoutState.endLineMatched()) { resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0); - layoutState.marginInfo().clearMargin(); break; } } @@ -1290,12 +1343,14 @@ void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, I const InlineIterator oldEnd = end; bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly(); - FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last().get() : nullptr; + FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last().get() : 0; +#if ENABLE(CSS_SHAPES) + updateShapeAndSegmentsForCurrentLine(shapeInsideInfo, logicalOffsetFromShapeContainer, layoutState); +#endif WordMeasurements wordMeasurements; - end = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), layoutState, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements); - cachePriorCharactersIfNeeded(renderTextInfo.lineBreakIterator); - renderTextInfo.lineBreakIterator.resetPriorContext(); + end = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements); + renderTextInfo.m_lineBreakIterator.resetPriorContext(); if (resolver.position().atEnd()) { // FIXME: We shouldn't be creating any runs in nextLineBreak to begin with! // Once BidiRunList is separated from BidiResolver this will not be needed. @@ -1306,6 +1361,10 @@ void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, I break; } +#if ENABLE(CSS_SHAPES) + if (adjustLogicalLineTopAndLogicalHeightIfNeeded(shapeInsideInfo, logicalOffsetFromShapeContainer.height(), layoutState, resolver, lastFloatFromPreviousLine, end, wordMeasurements)) + continue; +#endif ASSERT(end != resolver.position()); // This is a short-cut for empty lines. @@ -1322,10 +1381,10 @@ void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, I } // FIXME: This ownership is reversed. We should own the BidiRunList and pass it to createBidiRunsForLine. BidiRunList<BidiRun>& bidiRuns = resolver.runs(); - constructBidiRunsForSegment(resolver, bidiRuns, end, override, layoutState.lineInfo().previousLineBrokeCleanly()); + constructBidiRunsForLine(this, resolver, bidiRuns, end, override, layoutState.lineInfo().previousLineBrokeCleanly()); ASSERT(resolver.position() == end); - BidiRun* trailingSpaceRun = !layoutState.lineInfo().previousLineBrokeCleanly() ? handleTrailingSpaces(bidiRuns, resolver.context()) : nullptr; + BidiRun* trailingSpaceRun = !layoutState.lineInfo().previousLineBrokeCleanly() ? handleTrailingSpaces(bidiRuns, resolver.context()) : 0; if (bidiRuns.runCount() && lineBreaker.lineWasHyphenated()) { bidiRuns.logicallyLastRun()->m_hasHyphen = true; @@ -1338,7 +1397,7 @@ void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, I // inline flow boxes. LayoutUnit oldLogicalHeight = logicalHeight(); - RootInlineBox* lineBox = createLineBoxesFromBidiRuns(resolver.status().context->level(), bidiRuns, end, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, wordMeasurements); + RootInlineBox* lineBox = createLineBoxesFromBidiRuns(bidiRuns, end, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, wordMeasurements); bidiRuns.deleteRuns(); resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed). @@ -1347,54 +1406,24 @@ void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, I lineBox->setLineBreakInfo(end.renderer(), end.offset(), resolver.status()); if (layoutState.usesRepaintBounds()) layoutState.updateRepaintRangeFromBox(lineBox); - - LayoutUnit adjustment = 0; - bool overflowsRegion = false; - - // If our previous line was an anonymous block and we are not an anonymous block, - // simulate a margin collapse now so that we get the proper - // increased height. We also have to simulate a margin collapse to propagate margins - // through to the top of our block. - if (!lineBox->hasAnonymousInlineBlock()) { - RootInlineBox* prevRoot = lineBox->prevRootBox(); - if (prevRoot && prevRoot->hasAnonymousInlineBlock()) { - LayoutUnit currentLogicalHeight = logicalHeight(); - setLogicalHeight(oldLogicalHeight); - collapseMarginsWithChildInfo(nullptr, nullptr, layoutState.marginInfo()); - adjustment = logicalHeight() - oldLogicalHeight; - setLogicalHeight(currentLogicalHeight); - } - layoutState.marginInfo().setAtBeforeSideOfBlock(false); - } - - if (paginated) - adjustLinePositionForPagination(lineBox, adjustment, overflowsRegion, layoutState.flowThread()); - if (adjustment) { - IndentTextOrNot shouldIndentText = layoutState.lineInfo().isFirstLine() ? IndentText : DoNotIndentText; - LayoutUnit oldLineWidth = availableLogicalWidthForLine(oldLogicalHeight, shouldIndentText); - lineBox->adjustBlockDirectionPosition(adjustment); - if (layoutState.usesRepaintBounds()) - layoutState.updateRepaintRangeFromBox(lineBox); - - if (availableLogicalWidthForLine(oldLogicalHeight + adjustment, shouldIndentText) != oldLineWidth) { - // We have to delete this line, remove all floats that got added, and let line layout re-run. - lineBox->deleteLine(); - end = restartLayoutRunsAndFloatsInRange(oldLogicalHeight, oldLogicalHeight + adjustment, lastFloatFromPreviousLine, resolver, oldEnd); - continue; - } - setLogicalHeight(lineBox->lineBottomWithLeading()); - } - if (paginated) { - if (RenderFlowThread* flowThread = flowThreadContainingBlock()) { - if (flowThread->isRenderNamedFlowThread() && overflowsRegion && hasNextPage(lineBox->lineTop())) { - // Limit the height of this block to the end of the current region because - // it is also fragmented into the next region. - LayoutUnit remainingLogicalHeight = pageRemainingLogicalHeightForOffset(logicalTop(), ExcludePageBoundary); - if (logicalHeight() > remainingLogicalHeight) - setLogicalHeight(remainingLogicalHeight); + LayoutUnit adjustment = 0; + adjustLinePositionForPagination(lineBox, adjustment, layoutState.flowThread()); + if (adjustment) { + LayoutUnit oldLineWidth = availableLogicalWidthForLine(oldLogicalHeight, layoutState.lineInfo().isFirstLine()); + lineBox->adjustBlockDirectionPosition(adjustment); + if (layoutState.usesRepaintBounds()) + layoutState.updateRepaintRangeFromBox(lineBox); + + if (availableLogicalWidthForLine(oldLogicalHeight + adjustment, layoutState.lineInfo().isFirstLine()) != oldLineWidth) { + // We have to delete this line, remove all floats that got added, and let line layout re-run. + lineBox->deleteLine(); + end = restartLayoutRunsAndFloatsInRange(oldLogicalHeight, oldLogicalHeight + adjustment, lastFloatFromPreviousLine, resolver, oldEnd); + continue; } + + setLogicalHeight(lineBox->lineBottomWithLeading()); } if (layoutState.flowThread()) @@ -1404,11 +1433,11 @@ void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, I } for (size_t i = 0; i < lineBreaker.positionedObjects().size(); ++i) - setStaticPositions(*this, *lineBreaker.positionedObjects()[i], DoNotIndentText); + setStaticPositions(*this, *lineBreaker.positionedObjects()[i]); if (!layoutState.lineInfo().isEmpty()) { layoutState.lineInfo().setFirstLine(false); - clearFloats(lineBreaker.clear()); + newLine(lineBreaker.clear()); } if (m_floatingObjects && lastRootBox()) { @@ -1496,44 +1525,22 @@ void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, I markLinesDirtyInBlockRange(lastRootBox()->lineBottomWithLeading(), lineBox->lineBottomWithLeading(), lineBox); } } - clearDidBreakAtLineToAvoidWidow(); -} - -void RenderBlockFlow::reattachCleanLineFloats(RootInlineBox& cleanLine, LayoutUnit delta, bool isFirstCleanLine) -{ - auto* cleanLineFloats = cleanLine.floatsPtr(); - if (!cleanLineFloats) - return; - for (auto* floatingBox : *cleanLineFloats) { - auto* floatingObject = insertFloatingObject(*floatingBox); - if (isFirstCleanLine && floatingObject->originatingLine()) { - // Float box does not belong to this line anymore. - ASSERT_WITH_SECURITY_IMPLICATION(cleanLine.prevRootBox() == floatingObject->originatingLine()); - cleanLine.removeFloat(*floatingBox); - continue; - } - ASSERT_WITH_SECURITY_IMPLICATION(!floatingObject->originatingLine()); - floatingObject->setOriginatingLine(&cleanLine); - setLogicalHeight(logicalTopForChild(*floatingBox) - marginBeforeForChild(*floatingBox) + delta); - positionNewFloats(); - } + clearDidBreakAtLineToAvoidWidow(); } void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState) { - auto* firstCleanLine = layoutState.endLine(); - if (firstCleanLine) { + if (layoutState.endLine()) { if (layoutState.endLineMatched()) { bool paginated = view().layoutState() && view().layoutState()->isPaginated(); // Attach all the remaining lines, and then adjust their y-positions as needed. LayoutUnit delta = logicalHeight() - layoutState.endLineLogicalTop(); - for (auto* line = firstCleanLine; line; line = line->nextRootBox()) { + for (RootInlineBox* line = layoutState.endLine(); line; line = line->nextRootBox()) { line->attachLine(); if (paginated) { delta -= line->paginationStrut(); - bool overflowsRegion; - adjustLinePositionForPagination(line, delta, overflowsRegion, layoutState.flowThread()); + adjustLinePositionForPagination(line, delta, layoutState.flowThread()); } if (delta) { layoutState.updateRepaintRangeFromBox(line, delta); @@ -1541,7 +1548,16 @@ void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState) } if (layoutState.flowThread()) updateRegionForLine(line); - reattachCleanLineFloats(*line, delta, line == firstCleanLine); + if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) { + for (auto it = cleanLineFloats->begin(), end = cleanLineFloats->end(); it != end; ++it) { + RenderBox* floatingBox = *it; + FloatingObject* floatingObject = insertFloatingObject(*floatingBox); + ASSERT(!floatingObject->originatingLine()); + floatingObject->setOriginatingLine(line); + setLogicalHeight(logicalTopForChild(*floatingBox) - marginBeforeForChild(*floatingBox) + delta); + positionNewFloats(); + } + } } setLogicalHeight(lastRootBox()->lineBottomWithLeading()); } else { @@ -1559,7 +1575,7 @@ void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState) LayoutUnit bottomLayoutOverflow = lastRootBox()->logicalBottomLayoutOverflow(); auto newLineBox = std::make_unique<TrailingFloatsRootInlineBox>(*this); auto trailingFloatsLineBox = newLineBox.get(); - m_lineBoxes.appendLineBox(WTFMove(newLineBox)); + m_lineBoxes.appendLineBox(std::move(newLineBox)); trailingFloatsLineBox->setConstructed(); GlyphOverflowAndFallbackFontsMap textBoxDataMap; VerticalPositionCache verticalPositionCache; @@ -1620,7 +1636,7 @@ void RenderBlockFlow::layoutLineBoxes(bool relayoutChildren, LayoutUnit& repaint // Figure out if we should clear out our line boxes. // FIXME: Handle resize eventually! bool isFullLayout = !firstRootBox() || selfNeedsLayout() || relayoutChildren || clearLinesForPagination; - LineLayoutState layoutState(*this, isFullLayout, repaintLogicalTop, repaintLogicalBottom, flowThread); + LineLayoutState layoutState(isFullLayout, repaintLogicalTop, repaintLogicalBottom, flowThread); if (isFullLayout) lineBoxes().deleteLineBoxes(); @@ -1653,7 +1669,7 @@ void RenderBlockFlow::layoutLineBoxes(bool relayoutChildren, LayoutUnit& repaint hasInlineChild = true; if (o.isReplaced() || o.isFloating() || o.isOutOfFlowPositioned()) { - RenderBox& box = downcast<RenderBox>(o); + RenderBox& box = toRenderBox(o); if (relayoutChildren || box.hasRelativeDimensions()) box.setChildNeedsLayout(MarkOnlyThis); @@ -1669,16 +1685,14 @@ void RenderBlockFlow::layoutLineBoxes(bool relayoutChildren, LayoutUnit& repaint else if (isFullLayout || box.needsLayout()) { // Replaced element. box.dirtyLineBoxes(isFullLayout); - if (!o.isAnonymousInlineBlock()) { - if (isFullLayout) - replacedChildren.append(&box); - else - box.layoutIfNeeded(); - } + if (isFullLayout) + replacedChildren.append(&box); + else + box.layoutIfNeeded(); } - } else if (o.isTextOrLineBreak() || (is<RenderInline>(o) && !walker.atEndOfInline())) { - if (is<RenderInline>(o)) - downcast<RenderInline>(o).updateAlwaysCreateLineBoxes(layoutState.isFullLayout()); + } else if (o.isTextOrLineBreak() || (o.isRenderInline() && !walker.atEndOfInline())) { + if (o.isRenderInline()) + toRenderInline(o).updateAlwaysCreateLineBoxes(layoutState.isFullLayout()); if (layoutState.isFullLayout() || o.selfNeedsLayout()) dirtyLineBoxesForRenderer(o, layoutState.isFullLayout()); o.clearNeedsLayout(); @@ -1700,17 +1714,9 @@ void RenderBlockFlow::layoutLineBoxes(bool relayoutChildren, LayoutUnit& repaint else lastLineAnnotationsAdjustment = lastRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition); } - - // Now do the handling of the bottom of the block, adding in our bottom border/padding and - // determining the correct collapsed bottom margin information. This collapse is only necessary - // if our last child was an anonymous inline block that might need to propagate margin information out to - // us. - LayoutUnit beforeEdge = borderAndPaddingBefore(); - LayoutUnit afterEdge = borderAndPaddingAfter() + scrollbarLogicalHeight() + lastLineAnnotationsAdjustment; - if (lastRootBox() && lastRootBox()->hasAnonymousInlineBlock()) - handleAfterSideOfBlock(beforeEdge, afterEdge, layoutState.marginInfo()); - else - setLogicalHeight(logicalHeight() + afterEdge); + + // Now add in the bottom border/padding. + setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + borderAndPaddingAfter() + scrollbarLogicalHeight()); if (!firstRootBox() && hasLineIfEmpty()) setLogicalHeight(logicalHeight() + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); @@ -1735,16 +1741,14 @@ void RenderBlockFlow::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWi for (auto it = cleanLineFloats->begin(), end = cleanLineFloats->end(); it != end; ++it) { RenderBox* floatingBox = *it; floatingBox->layoutIfNeeded(); - LayoutSize newSize(floatingBox->width() + floatingBox->horizontalMarginExtent(), floatingBox->height() + floatingBox->verticalMarginExtent()); + LayoutSize newSize(floatingBox->width() + floatingBox->marginWidth(), floatingBox->height() + floatingBox->marginHeight()); ASSERT_WITH_SECURITY_IMPLICATION(floatIndex < floats.size()); if (&floats[floatIndex].object != floatingBox) { encounteredNewFloat = true; return; } - - // We have to reset the cap-height alignment done by the first-letter floats when initial-letter is set, so just always treat first-letter floats - // as dirty. - if (floats[floatIndex].rect.size() != newSize || (floatingBox->style().styleType() == FIRST_LETTER && floatingBox->style().initialLetterDrop() > 0)) { + + if (floats[floatIndex].rect.size() != newSize) { LayoutUnit floatTop = isHorizontalWritingMode() ? floats[floatIndex].rect.y() : floats[floatIndex].rect.x(); LayoutUnit floatHeight = isHorizontalWritingMode() ? std::max(floats[floatIndex].rect.height(), newSize.height()) : std::max(floats[floatIndex].rect.width(), newSize.width()); floatHeight = std::min(floatHeight, LayoutUnit::max() - floatTop); @@ -1776,8 +1780,7 @@ RootInlineBox* RenderBlockFlow::determineStartPosition(LineLayoutState& layoutSt break; } paginationDelta -= curr->paginationStrut(); - bool overflowsRegion; - adjustLinePositionForPagination(curr, paginationDelta, overflowsRegion, layoutState.flowThread()); + adjustLinePositionForPagination(curr, paginationDelta, layoutState.flowThread()); if (paginationDelta) { if (containsFloats() || !layoutState.floats().isEmpty()) { // FIXME: Do better eventually. For now if we ever shift because of pagination and floats are present just go to a full layout. @@ -1816,13 +1819,18 @@ RootInlineBox* RenderBlockFlow::determineStartPosition(LineLayoutState& layoutSt // We have a dirty line. if (RootInlineBox* prevRootBox = curr->prevRootBox()) { // We have a previous line. - if (!dirtiedByFloat && !curr->hasAnonymousInlineBlock() && (!prevRootBox->endsWithBreak() || !prevRootBox->lineBreakObj() || (is<RenderText>(*prevRootBox->lineBreakObj()) && prevRootBox->lineBreakPos() >= downcast<RenderText>(*prevRootBox->lineBreakObj()).textLength()))) { + if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || !prevRootBox->lineBreakObj() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength()))) // The previous line didn't break cleanly or broke at a newline // that has been deleted, so treat it as dirty too. curr = prevRootBox; - } } + } else { + // No dirty lines were found. + // If the last line didn't break cleanly, treat it as dirty. + if (lastRootBox() && !lastRootBox()->endsWithBreak()) + curr = lastRootBox(); } + // If we have no dirty lines, then last is just the last root box. last = curr ? curr->prevRootBox() : lastRootBox(); } @@ -1837,7 +1845,7 @@ RootInlineBox* RenderBlockFlow::determineStartPosition(LineLayoutState& layoutSt for (auto it = cleanLineFloats->begin(), end = cleanLineFloats->end(); it != end; ++it) { RenderBox* floatingBox = *it; FloatingObject* floatingObject = insertFloatingObject(*floatingBox); - ASSERT_WITH_SECURITY_IMPLICATION(!floatingObject->originatingLine()); + ASSERT(!floatingObject->originatingLine()); floatingObject->setOriginatingLine(line); setLogicalHeight(logicalTopForChild(*floatingBox) - marginBeforeForChild(*floatingBox)); positionNewFloats(); @@ -1920,9 +1928,8 @@ bool RenderBlockFlow::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutS // This isn't the real move we're going to do, so don't update the line box's pagination // strut yet. LayoutUnit oldPaginationStrut = lineBox->paginationStrut(); - bool overflowsRegion; lineDelta -= oldPaginationStrut; - adjustLinePositionForPagination(lineBox, lineDelta, overflowsRegion, layoutState.flowThread()); + adjustLinePositionForPagination(lineBox, lineDelta, layoutState.flowThread()); lineBox->setPaginationStrut(oldPaginationStrut); } if (lineWidthForPaginatedLineChanged(lineBox, lineDelta, layoutState.flowThread())) @@ -1945,7 +1952,7 @@ bool RenderBlockFlow::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutS const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); auto end = floatingObjectSet.end(); for (auto it = floatingObjectSet.begin(); it != end; ++it) { - const auto& floatingObject = *it->get(); + FloatingObject* floatingObject = it->get(); if (logicalBottomForFloat(floatingObject) >= logicalTop && logicalBottomForFloat(floatingObject) < logicalBottom) return false; } @@ -1975,11 +1982,11 @@ bool RenderBlockFlow::matchedEndLine(LineLayoutState& layoutState, const InlineB // The first clean line doesn't match, but we can check a handful of following lines to try // to match back up. - static const int numLines = 8; // The # of lines we're willing to match against. + static int numLines = 8; // The # of lines we're willing to match against. RootInlineBox* originalEndLine = layoutState.endLine(); RootInlineBox* line = originalEndLine; for (int i = 0; i < numLines && line; i++, line = line->nextRootBox()) { - if (line->lineBreakObj() == resolver.position().renderer() && line->lineBreakPos() == resolver.position().offset() && !line->hasAnonymousInlineBlock()) { + if (line->lineBreakObj() == resolver.position().renderer() && line->lineBreakPos() == resolver.position().offset()) { // We have a match. if (line->lineBreakBidiStatus() != resolver.status()) return false; // ...but the bidi state doesn't match. @@ -2026,14 +2033,13 @@ void RenderBlockFlow::addOverflowFromInlineChildren() endPadding = 1; for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding)); - RenderRegion* region = flowThreadContainingBlock() ? curr->containingRegion() : nullptr; + RenderRegion* region = curr->containingRegion(); if (region) region->addLayoutOverflowForBox(this, curr->paddedLayoutOverflowRect(endPadding)); if (!hasOverflowClip()) { - LayoutRect childVisualOverflowRect = curr->visualOverflowRect(curr->lineTop(), curr->lineBottom()); - addVisualOverflow(childVisualOverflowRect); + addVisualOverflow(curr->visualOverflowRect(curr->lineTop(), curr->lineBottom())); if (region) - region->addVisualOverflowForBox(this, childVisualOverflowRect); + region->addVisualOverflowForBox(this, curr->visualOverflowRect(curr->lineTop(), curr->lineBottom())); } } } @@ -2042,23 +2048,23 @@ void RenderBlockFlow::deleteEllipsisLineBoxes() { ETextAlign textAlign = style().textAlign(); bool ltr = style().isLeftToRightDirection(); - IndentTextOrNot shouldIndentText = IndentText; + bool firstLine = true; for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { if (curr->hasEllipsisBox()) { curr->clearTruncation(); // Shift the line back where it belongs if we cannot accomodate an ellipsis. - float logicalLeft = logicalLeftOffsetForLine(curr->lineTop(), shouldIndentText); - float availableLogicalWidth = logicalRightOffsetForLine(curr->lineTop(), DoNotIndentText) - logicalLeft; + float logicalLeft = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine); + float availableLogicalWidth = logicalRightOffsetForLine(curr->lineTop(), false) - logicalLeft; float totalLogicalWidth = curr->logicalWidth(); - updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0); + updateLogicalWidthForAlignment(textAlign, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0); if (ltr) curr->adjustLogicalPosition((logicalLeft - curr->logicalLeft()), 0); else curr->adjustLogicalPosition(-(curr->logicalLeft() - logicalLeft), 0); } - shouldIndentText = DoNotIndentText; + firstLine = false; } } @@ -2066,11 +2072,11 @@ void RenderBlockFlow::checkLinesForTextOverflow() { // Determine the width of the ellipsis using the current font. // FIXME: CSS3 says this is configurable, also need to use 0x002E (FULL STOP) if horizontal ellipsis is "not renderable" - const FontCascade& font = style().fontCascade(); - static NeverDestroyed<AtomicString> ellipsisStr(&horizontalEllipsis, 1); - const FontCascade& firstLineFont = firstLineStyle().fontCascade(); - float firstLineEllipsisWidth = firstLineFont.width(constructTextRun(this, firstLineFont, &horizontalEllipsis, 1, firstLineStyle())); - float ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(constructTextRun(this, font, &horizontalEllipsis, 1, style())); + const Font& font = style().font(); + DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1)); + const Font& firstLineFont = firstLineStyle().font(); + int firstLineEllipsisWidth = firstLineFont.width(constructTextRun(this, firstLineFont, &horizontalEllipsis, 1, firstLineStyle())); + int ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(constructTextRun(this, font, &horizontalEllipsis, 1, style())); // For LTR text truncation, we want to get the right edge of our padding box, and then we want to see // if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and @@ -2080,23 +2086,25 @@ void RenderBlockFlow::checkLinesForTextOverflow() ETextAlign textAlign = style().textAlign(); bool firstLine = true; for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) { - IndentTextOrNot shouldIndentText = firstLine ? IndentText : DoNotIndentText; - LayoutUnit blockRightEdge = logicalRightOffsetForLine(curr->lineTop(), shouldIndentText); - LayoutUnit blockLeftEdge = logicalLeftOffsetForLine(curr->lineTop(), shouldIndentText); - LayoutUnit lineBoxEdge = ltr ? curr->x() + curr->logicalWidth() : curr->x(); + // FIXME: Use pixelSnappedLogicalRightOffsetForLine instead of snapping it ourselves once the column workaround in said method has been fixed. + // https://bugs.webkit.org/show_bug.cgi?id=105461 + int blockRightEdge = snapSizeToPixel(logicalRightOffsetForLine(curr->lineTop(), firstLine), curr->x()); + int blockLeftEdge = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine); + int lineBoxEdge = ltr ? snapSizeToPixel(curr->x() + curr->logicalWidth(), curr->x()) : snapSizeToPixel(curr->x(), 0); if ((ltr && lineBoxEdge > blockRightEdge) || (!ltr && lineBoxEdge < blockLeftEdge)) { // This line spills out of our box in the appropriate direction. Now we need to see if the line // can be truncated. In order for truncation to be possible, the line must have sufficient space to // accommodate our truncation string, and no replaced elements (images, tables) can overlap the ellipsis // space. + LayoutUnit width = firstLine ? firstLineEllipsisWidth : ellipsisWidth; LayoutUnit blockEdge = ltr ? blockRightEdge : blockLeftEdge; if (curr->lineCanAccommodateEllipsis(ltr, blockEdge, lineBoxEdge, width)) { float totalLogicalWidth = curr->placeEllipsis(ellipsisStr, ltr, blockLeftEdge, blockRightEdge, width); float logicalLeft = 0; // We are only interested in the delta from the base position. - float truncatedWidth = availableLogicalWidthForLine(curr->lineTop(), shouldIndentText); - updateLogicalWidthForAlignment(textAlign, curr, nullptr, logicalLeft, totalLogicalWidth, truncatedWidth, 0); + float truncatedWidth = pixelSnappedLogicalRightOffsetForLine(curr->lineTop(), firstLine); + updateLogicalWidthForAlignment(textAlign, 0, logicalLeft, totalLogicalWidth, truncatedWidth, 0); if (ltr) curr->adjustLogicalPosition(logicalLeft, 0); else @@ -2107,7 +2115,7 @@ void RenderBlockFlow::checkLinesForTextOverflow() } } -bool RenderBlockFlow::positionNewFloatOnLine(const FloatingObject& newFloat, FloatingObject* lastFloatFromPreviousLine, LineInfo& lineInfo, LineWidth& width) +bool RenderBlockFlow::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine, LineInfo& lineInfo, LineWidth& width) { if (!positionNewFloats()) return false; @@ -2117,14 +2125,14 @@ bool RenderBlockFlow::positionNewFloatOnLine(const FloatingObject& newFloat, Flo // We only connect floats to lines for pagination purposes if the floats occur at the start of // the line and the previous line had a hard break (so this line is either the first in the block // or follows a <br>). - if (!newFloat.paginationStrut() || !lineInfo.previousLineBrokeCleanly() || !lineInfo.isEmpty()) + if (!newFloat->paginationStrut() || !lineInfo.previousLineBrokeCleanly() || !lineInfo.isEmpty()) return true; const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set(); - ASSERT(floatingObjectSet.last().get() == &newFloat); + ASSERT(floatingObjectSet.last().get() == newFloat); LayoutUnit floatLogicalTop = logicalTopForFloat(newFloat); - LayoutUnit paginationStrut = newFloat.paginationStrut(); + int paginationStrut = newFloat->paginationStrut(); if (floatLogicalTop - paginationStrut != logicalHeight() + lineInfo.floatPaginationStrut()) return true; @@ -2134,26 +2142,26 @@ bool RenderBlockFlow::positionNewFloatOnLine(const FloatingObject& newFloat, Flo auto begin = floatingObjectSet.begin(); while (it != begin) { --it; - auto& floatingObject = *it->get(); - if (&floatingObject == lastFloatFromPreviousLine) + FloatingObject* floatingObject = it->get(); + if (floatingObject == lastFloatFromPreviousLine) break; if (logicalTopForFloat(floatingObject) == logicalHeight() + lineInfo.floatPaginationStrut()) { - floatingObject.setPaginationStrut(paginationStrut + floatingObject.paginationStrut()); - RenderBox& floatBox = floatingObject.renderer(); + floatingObject->setPaginationStrut(paginationStrut + floatingObject->paginationStrut()); + RenderBox& floatBox = floatingObject->renderer(); setLogicalTopForChild(floatBox, logicalTopForChild(floatBox) + marginBeforeForChild(floatBox) + paginationStrut); - if (updateRegionRangeForBoxChild(floatBox)) + if (updateRegionRangeForBoxChild(floatingObject->renderer())) floatBox.setNeedsLayout(MarkOnlyThis); - else if (is<RenderBlock>(floatBox)) - downcast<RenderBlock>(floatBox).setChildNeedsLayout(MarkOnlyThis); + else if (floatBox.isRenderBlock()) + toRenderBlock(floatBox).setChildNeedsLayout(MarkOnlyThis); floatBox.layoutIfNeeded(); // Save the old logical top before calling removePlacedObject which will set // isPlaced to false. Otherwise it will trigger an assert in logicalTopForFloat. LayoutUnit oldLogicalTop = logicalTopForFloat(floatingObject); - m_floatingObjects->removePlacedObject(&floatingObject); + m_floatingObjects->removePlacedObject(floatingObject); setLogicalTopForFloat(floatingObject, oldLogicalTop + paginationStrut); - m_floatingObjects->addPlacedObject(&floatingObject); + m_floatingObjects->addPlacedObject(floatingObject); } } @@ -2163,39 +2171,22 @@ bool RenderBlockFlow::positionNewFloatOnLine(const FloatingObject& newFloat, Flo return true; } -LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(LayoutUnit position, IndentTextOrNot shouldIndentText) +LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(LayoutUnit position, bool firstLine) { ETextAlign textAlign = style().textAlign(); - bool shouldApplyIndentText = false; - switch (textAlign) { - case LEFT: - case WEBKIT_LEFT: - shouldApplyIndentText = style().isLeftToRightDirection(); - break; - case RIGHT: - case WEBKIT_RIGHT: - shouldApplyIndentText = !style().isLeftToRightDirection(); - break; - case TASTART: - shouldApplyIndentText = true; - break; - default: - shouldApplyIndentText = false; - } + // <rdar://problem/15427571> // https://bugs.webkit.org/show_bug.cgi?id=124522 // This quirk is for legacy content that doesn't work properly with the center positioning scheme // being honored (e.g., epubs). - if (shouldApplyIndentText || document().settings()->useLegacyTextAlignPositionedElementBehavior()) // FIXME: Handle TAEND here - return startOffsetForLine(position, shouldIndentText); + if (textAlign == TASTART || document().settings()->useLegacyTextAlignPositionedElementBehavior()) // FIXME: Handle TAEND here + return startOffsetForLine(position, firstLine); // updateLogicalWidthForAlignment() handles the direction of the block so no need to consider it here float totalLogicalWidth = 0; - float logicalLeft = logicalLeftOffsetForLine(logicalHeight(), DoNotIndentText); - float availableLogicalWidth = logicalRightOffsetForLine(logicalHeight(), DoNotIndentText) - logicalLeft; - - // FIXME: Bug 129311: We need to pass a valid RootInlineBox here, considering the bidi level used to construct the line. - updateLogicalWidthForAlignment(textAlign, 0, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0); + float logicalLeft = logicalLeftOffsetForLine(logicalHeight(), false); + float availableLogicalWidth = logicalRightOffsetForLine(logicalHeight(), false) - logicalLeft; + updateLogicalWidthForAlignment(textAlign, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0); if (!style().isLeftToRightDirection()) return logicalWidth() - logicalLeft; @@ -2206,14 +2197,10 @@ void RenderBlockFlow::updateRegionForLine(RootInlineBox* lineBox) const { ASSERT(lineBox); - if (!hasRegionRangeInFlowThread()) + if (auto containingRegion = regionAtBlockOffset(lineBox->lineTopWithLeading())) + lineBox->setContainingRegion(*containingRegion); + else lineBox->clearContainingRegion(); - else { - if (auto containingRegion = regionAtBlockOffset(lineBox->lineTopWithLeading())) - lineBox->setContainingRegion(*containingRegion); - else - lineBox->clearContainingRegion(); - } RootInlineBox* prevLineBox = lineBox->prevRootBox(); if (!prevLineBox) @@ -2226,42 +2213,4 @@ void RenderBlockFlow::updateRegionForLine(RootInlineBox* lineBox) const lineBox->setIsFirstAfterPageBreak(true); } -void RenderBlockFlow::marginCollapseLinesFromStart(LineLayoutState& layoutState, RootInlineBox* stopLine) -{ - // We have to handle an anonymous inline block streak at the start of the block in order to make sure our own margins are - // correct. We only have to do this if the children can propagate margins out to us. - bool resetLogicalHeight = false; - if (layoutState.marginInfo().canCollapseWithMarginBefore()) { - RootInlineBox* startLine = firstRootBox(); - RootInlineBox* curr; - for (curr = startLine; curr && curr->hasAnonymousInlineBlock() && layoutState.marginInfo().canCollapseWithMarginBefore(); curr = curr->nextRootBox()) { - if (curr == stopLine) - return; - if (!resetLogicalHeight) { - setLogicalHeight(borderAndPaddingBefore()); - resetLogicalHeight = true; - } - layoutBlockChild(*curr->anonymousInlineBlock(), layoutState.marginInfo(), - layoutState.prevFloatBottomFromAnonymousInlineBlock(), layoutState.maxFloatBottomFromAnonymousInlineBlock()); - } - } - - // Now that we've handled the top of the block, if the stopLine isn't an anonymous block, then we're done. - if (!stopLine->hasAnonymousInlineBlock()) - return; - - // Re-run margin collapsing on the block sequence that stopLine is a part of. - // First go backwards to get the entire sequence. - RootInlineBox* prev = stopLine; - for ( ; prev->hasAnonymousInlineBlock(); prev = prev->prevRootBox()) { - }; - - // Update the block height to the correct state. - setLogicalHeight(prev->lineBottomWithLeading()); - - // Now run margin collapsing on those lines. - for (prev = prev->nextRootBox(); prev != stopLine; prev = prev->nextRootBox()) - layoutBlockChild(*prev->anonymousInlineBlock(), layoutState.marginInfo(), layoutState.prevFloatBottomFromAnonymousInlineBlock(), layoutState.maxFloatBottomFromAnonymousInlineBlock()); -} - } |