summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/RenderBlockLineLayout.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/rendering/RenderBlockLineLayout.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/rendering/RenderBlockLineLayout.cpp')
-rw-r--r--Source/WebCore/rendering/RenderBlockLineLayout.cpp1616
1 files changed, 874 insertions, 742 deletions
diff --git a/Source/WebCore/rendering/RenderBlockLineLayout.cpp b/Source/WebCore/rendering/RenderBlockLineLayout.cpp
index 3e546abec..085937b57 100644
--- a/Source/WebCore/rendering/RenderBlockLineLayout.cpp
+++ b/Source/WebCore/rendering/RenderBlockLineLayout.cpp
@@ -26,29 +26,28 @@
#include "AXObjectCache.h"
#include "BidiResolver.h"
-#include "BreakingContextInlineHeaders.h"
+#include "BreakingContext.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"
#include "VerticalPositionCache.h"
-#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,50 +70,50 @@ static void determineDirectionality(TextDirection& dir, InlineIterator iter)
}
}
-inline BidiRun* createRun(int start, int end, RenderObject* obj, InlineBidiResolver& resolver)
+inline std::unique_ptr<BidiRun> createRun(int start, int end, RenderObject& obj, InlineBidiResolver& resolver)
{
- ASSERT(obj);
- return new BidiRun(start, end, *obj, resolver.context(), resolver.dir());
+ return std::make_unique<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;
- LineMidpointState& lineMidpointState = resolver.midpointState();
- bool haveNextMidpoint = (lineMidpointState.currentMidpoint() < lineMidpointState.numMidpoints());
- InlineIterator nextMidpoint;
- if (haveNextMidpoint)
- nextMidpoint = lineMidpointState.midpoints()[lineMidpointState.currentMidpoint()];
- if (lineMidpointState.betweenMidpoints()) {
- if (!(haveNextMidpoint && nextMidpoint.renderer() == obj))
+ LineWhitespaceCollapsingState& lineWhitespaceCollapsingState = resolver.whitespaceCollapsingState();
+ bool haveNextTransition = (lineWhitespaceCollapsingState.currentTransition() < lineWhitespaceCollapsingState.numTransitions());
+ InlineIterator nextTransition;
+ if (haveNextTransition)
+ nextTransition = lineWhitespaceCollapsingState.transitions()[lineWhitespaceCollapsingState.currentTransition()];
+ if (lineWhitespaceCollapsingState.betweenTransitions()) {
+ if (!haveNextTransition || (&obj != nextTransition.renderer()))
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)
- return appendRunsForObject(runs, start, end, obj, resolver);
+ start = nextTransition.offset();
+ lineWhitespaceCollapsingState.incrementCurrentTransition();
+ if (start < end) {
+ appendRunsForObject(runs, start, end, obj, resolver);
+ return;
+ }
} else {
- if (!haveNextMidpoint || (obj != nextMidpoint.renderer())) {
- runs.addRun(createRun(start, end, obj, resolver));
+ if (!haveNextTransition || (&obj != nextTransition.renderer())) {
+ if (runs)
+ runs->appendRun(createRun(start, end, obj, resolver));
return;
}
- // 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();
- 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));
+ // An end transition has been encountered within our object. We need to append a run with our endpoint.
+ if (static_cast<int>(nextTransition.offset() + 1) <= end) {
+ lineWhitespaceCollapsingState.incrementCurrentTransition();
+ // The end of the line is before the object we're inspecting. Skip everything and return
+ if (nextTransition.refersToEndOfPreviousNode())
+ return;
+ if (static_cast<int>(nextTransition.offset() + 1) > start && runs)
+ runs->appendRun(createRun(start, nextTransition.offset() + 1, obj, resolver));
+ appendRunsForObject(runs, nextTransition.offset() + 1, end, obj, resolver);
+ } else if (runs)
+ runs->appendRun(createRun(start, end, obj, resolver));
}
}
@@ -127,51 +126,51 @@ RootInlineBox* RenderBlockFlow::createAndAppendRootInlineBox()
{
auto newRootBox = createRootInlineBox();
RootInlineBox* rootBox = newRootBox.get();
- m_lineBoxes.appendLineBox(std::move(newRootBox));
+ m_lineBoxes.appendLineBox(WTFMove(newRootBox));
if (UNLIKELY(AXObjectCache::accessibilityEnabled()) && firstRootBox() == rootBox) {
if (AXObjectCache* cache = document().existingAXObjectCache())
- cache->recomputeIsIgnored(this);
+ cache->recomputeDeferredIsIgnored(*this);
}
return rootBox;
}
-static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false)
+static inline InlineBox* createInlineBoxForRenderer(RenderObject* renderer, bool isRootLineBox, bool isOnlyRun = false)
{
if (isRootLineBox)
- return toRenderBlockFlow(obj)->createAndAppendRootInlineBox();
+ return downcast<RenderBlockFlow>(*renderer).createAndAppendRootInlineBox();
- if (obj->isText())
- return toRenderText(obj)->createInlineTextBox();
+ if (is<RenderText>(*renderer))
+ return downcast<RenderText>(*renderer).createInlineTextBox();
- if (obj->isBox()) {
+ if (is<RenderBox>(*renderer)) {
// FIXME: This is terrible. This branch returns an *owned* pointer!
- return toRenderBox(obj)->createInlineBox().release();
+ return downcast<RenderBox>(*renderer).createInlineBox().release();
}
- if (obj->isLineBreak()) {
+ if (is<RenderLineBreak>(*renderer)) {
// FIXME: This is terrible. This branch returns an *owned* pointer!
- auto inlineBox = toRenderLineBreak(obj)->createInlineBox().release();
+ auto inlineBox = downcast<RenderLineBreak>(*renderer).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 || obj->document().inNoQuirksMode() || obj->isLineBreakOpportunity());
+ inlineBox->setBehavesLikeText(isOnlyRun || renderer->document().inNoQuirksMode() || renderer->isLineBreakOpportunity());
return inlineBox;
}
- return toRenderInline(obj)->createAndAppendInlineFlowBox();
+ return downcast<RenderInline>(*renderer).createAndAppendInlineFlowBox();
}
static inline void dirtyLineBoxesForRenderer(RenderObject& renderer, bool fullLayout)
{
- if (renderer.isText()) {
- RenderText& renderText = toRenderText(renderer);
+ if (is<RenderText>(renderer)) {
+ RenderText& renderText = downcast<RenderText>(renderer);
updateCounterIfNeeded(renderText);
renderText.dirtyLineBoxes(fullLayout);
- } else if (renderer.isLineBreak())
- toRenderLineBreak(renderer).dirtyLineBoxes(fullLayout);
+ } else if (is<RenderLineBreak>(renderer))
+ downcast<RenderLineBreak>(renderer).dirtyLineBoxes(fullLayout);
else
- toRenderInline(renderer).dirtyLineBoxes(fullLayout);
+ downcast<RenderInline>(renderer).dirtyLineBoxes(fullLayout);
}
static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox)
@@ -184,21 +183,21 @@ static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox)
return false;
}
-InlineFlowBox* RenderBlockFlow::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox, bool startNewSegment)
+InlineFlowBox* RenderBlockFlow::createLineBoxes(RenderObject* obj, const LineInfo& lineInfo, InlineBox* childBox)
{
// 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 = 0;
- InlineFlowBox* result = 0;
+ InlineFlowBox* parentBox = nullptr;
+ InlineFlowBox* result = nullptr;
bool hasDefaultLineBoxContain = style().lineBoxContain() == RenderStyle::initialLineBoxContain();
do {
- ASSERT_WITH_SECURITY_IMPLICATION(obj->isRenderInline() || obj == this);
+ ASSERT_WITH_SECURITY_IMPLICATION(is<RenderInline>(*obj) || obj == this);
- RenderInline* inlineFlow = (obj != this) ? toRenderInline(obj) : 0;
+ RenderInline* inlineFlow = obj != this ? downcast<RenderInline>(obj) : nullptr;
// Get the last box we made for this render object.
- parentBox = inlineFlow ? inlineFlow->lastLineBox() : toRenderBlockFlow(obj)->lastRootBox();
+ parentBox = inlineFlow ? inlineFlow->lastLineBox() : downcast<RenderBlockFlow>(*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,14 +206,12 @@ 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 mustCreateBoxesToRoot = startNewSegment && !(parentBox && parentBox->isRootInlineBox());
- bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox) && !mustCreateBoxesToRoot;
+ bool canUseExistingParentBox = parentBox && !parentIsConstructedOrHaveNext(parentBox);
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);
- ASSERT_WITH_SECURITY_IMPLICATION(newBox->isInlineFlowBox());
- parentBox = toInlineFlowBox(newBox);
+ parentBox = downcast<InlineFlowBox>(newBox);
parentBox->setIsFirstLine(lineInfo.isFirstLine());
parentBox->setIsHorizontal(isHorizontalWritingMode());
if (!hasDefaultLineBoxContain)
@@ -264,10 +261,10 @@ static bool reachedEndOfTextRenderer(const BidiRunList<BidiRun>& bidiRuns)
if (!run)
return true;
unsigned pos = run->stop();
- const RenderObject& r = run->renderer();
- if (!r.isText())
+ const RenderObject& renderer = run->renderer();
+ if (!is<RenderText>(renderer))
return false;
- const RenderText& renderText = toRenderText(r);
+ const RenderText& renderText = downcast<RenderText>(renderer);
unsigned length = renderText.textLength();
if (pos >= length)
return true;
@@ -284,6 +281,7 @@ 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);
@@ -294,24 +292,20 @@ 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 ENABLE(CSS_SHAPES)
- bool runStartsSegment = r->m_startsSegment;
-#else
- bool runStartsSegment = false;
-#endif
- if (!parentBox || &parentBox->renderer() != r->renderer().parent() || runStartsSegment)
+ if (!parentBox || &parentBox->renderer() != r->renderer().parent()) {
// Create new inline boxes all the way back to the appropriate insertion point.
- parentBox = createLineBoxes(r->renderer().parent(), lineInfo, box, runStartsSegment);
- else {
+ RenderObject* parentToUse = r->renderer().parent();
+ parentBox = createLineBoxes(parentToUse, lineInfo, box);
+ } else {
// Append the inline box to this line.
parentBox->addToLine(box);
}
@@ -319,13 +313,13 @@ RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, co
bool visuallyOrdered = r->renderer().style().rtlOrdering() == VisualOrder;
box->setBidiLevel(r->level());
- 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 (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 (r->m_hasHyphen)
- text->setHasHyphen(true);
+ textBox.setHasHyphen(true);
}
}
@@ -342,7 +336,7 @@ RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, co
// paint borders/margins/padding. This knowledge will ultimately be used when
// we determine the horizontal positions and widths of all the inline boxes on
// the line.
- bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->renderer().isText() ? !reachedEndOfTextRenderer(bidiRuns) : true;
+ bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->renderer().isText() ? !reachedEndOfTextRenderer(bidiRuns) : !is<RenderInline>(bidiRuns.logicallyLastRun()->renderer());
lastRootBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogicallyLastRunWrapped, &bidiRuns.logicallyLastRun()->renderer());
// Now mark the line boxes as being constructed.
@@ -355,6 +349,12 @@ 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;
@@ -379,7 +379,7 @@ ETextAlign RenderBlockFlow::textAlignmentForLine(bool endsWithSoftBreak) const
case TextAlignLastJustify:
return JUSTIFY;
case TextAlignLastAuto:
- if (style().textJustify() == TextJustifyDistribute)
+ if (textJustify == TextJustifyDistribute)
return JUSTIFY;
return TASTART;
}
@@ -413,8 +413,7 @@ static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection,
totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth();
trailingSpaceRun->box()->setLogicalWidth(0);
}
- if (totalLogicalWidth < availableLogicalWidth)
- logicalLeft += availableLogicalWidth - totalLogicalWidth;
+ logicalLeft += std::max(0.f, availableLogicalWidth - totalLogicalWidth);
return;
}
@@ -441,8 +440,8 @@ static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection,
void RenderBlockFlow::setMarginsForRubyRun(BidiRun* run, RenderRubyRun& renderer, RenderObject* previousObject, const LineInfo& lineInfo)
{
- int startOverhang;
- int endOverhang;
+ float startOverhang;
+ float endOverhang;
RenderObject* nextObject = 0;
for (BidiRun* runWithNextObject = run->next(); runWithNextObject; runWithNextObject = runWithNextObject->next()) {
if (!runWithNextObject->renderer().isOutOfFlowPositioned() && !runWithNextObject->box()->isLineBreak()) {
@@ -455,13 +454,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 SimpleFontData*> fallbackFonts;
+ HashSet<const Font*> fallbackFonts;
GlyphOverflow glyphOverflow;
- const Font& font = lineStyle(*renderer->parent(), lineInfo).font();
+ const FontCascade& font = lineStyle(*renderer.parent(), lineInfo).fontCascade();
// 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,40 +476,43 @@ static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* ru
}
LayoutUnit hyphenWidth = 0;
- if (toInlineTextBox(run->box())->hasHyphen())
+ if (downcast<InlineTextBox>(*run->box()).hasHyphen())
hyphenWidth = measureHyphenWidth(renderer, font, &fallbackFonts);
float measuredWidth = 0;
- bool kerningIsEnabled = font.typesettingFeatures() & Kerning;
- bool canUseSimpleFontCodePath = renderer->canUseSimpleFontCodePath();
+ bool kerningIsEnabled = font.enableKerning();
+ 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.
-
if (!lineBox->fitsToGlyphs() && canUseSimpleFontCodePath) {
- int lastEndOffset = run->m_start;
+ unsigned lastEndOffset = run->m_start;
+ bool atFirstWordMeasurement = true;
for (size_t i = 0, size = wordMeasurements.size(); i < size && lastEndOffset < run->m_stop; ++i) {
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);
- if (i > 0 && wordLength == 1 && (c == ' ' || c == '\t'))
- measuredWidth += renderer->style().font().wordSpacing();
+ UChar c = renderer.characterAt(wordMeasurement.startOffset);
+ // renderer.width() omits word-spacing value for leading whitespace, so let's just add it back here.
+ if (!atFirstWordMeasurement && (c == ' ' || c == '\t'))
+ measuredWidth += renderer.style().fontCascade().wordSpacing();
} else
measuredWidth += wordMeasurement.width;
+ atFirstWordMeasurement = false;
+
if (!wordMeasurement.fallbackFonts.isEmpty()) {
- HashSet<const SimpleFontData*>::const_iterator end = wordMeasurement.fallbackFonts.end();
- for (HashSet<const SimpleFontData*>::const_iterator it = wordMeasurement.fallbackFonts.begin(); it != end; ++it)
+ HashSet<const Font*>::const_iterator end = wordMeasurement.fallbackFonts.end();
+ for (HashSet<const Font*>::const_iterator it = wordMeasurement.fallbackFonts.begin(); it != end; ++it)
fallbackFonts.add(*it);
}
}
@@ -522,60 +524,108 @@ 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(toInlineTextBox(run->box()), std::make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator;
+ GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(downcast<InlineTextBox>(run->box()), std::make_pair(Vector<const Font*>(), GlyphOverflow())).iterator;
ASSERT(it->value.first.isEmpty());
copyToVector(fallbackFonts, it->value.first);
run->box()->parent()->clearDescendantsHaveSameLineHeightAndBaseline();
}
- if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) {
+
+ // 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()) {
ASSERT(run->box()->behavesLikeText());
- GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->box()), std::make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator;
+ GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(downcast<InlineTextBox>(run->box()), std::make_pair(Vector<const Font*>(), GlyphOverflow())).iterator;
it->value.second = glyphOverflow;
run->box()->clearKnownToHaveNoOverflow();
}
}
-static inline void computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, Vector<unsigned, 16>& expansionOpportunities, unsigned expansionOpportunityCount, float& totalLogicalWidth, float availableLogicalWidth)
+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());
+
+ totalLogicalWidth += totalExpansion;
+ expansionOpportunityCount -= totalOpportunitiesInRun;
+}
+
+void RenderBlockFlow::computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, const Vector<unsigned, 16>& expansionOpportunities, unsigned expansionOpportunityCount, float totalLogicalWidth, float availableLogicalWidth)
{
if (!expansionOpportunityCount || availableLogicalWidth <= totalLogicalWidth)
return;
size_t i = 0;
- 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)
+ for (BidiRun* run = firstRun; run; run = run->next()) {
+ if (!run->box() || run == trailingSpaceRun)
continue;
- if (r->renderer().isText()) {
+ if (is<RenderText>(run->renderer())) {
unsigned opportunitiesInRun = expansionOpportunities[i++];
ASSERT(opportunitiesInRun <= expansionOpportunityCount);
// Only justify text if whitespace is collapsed.
- if (r->renderer().style().collapseWhiteSpace()) {
- InlineTextBox* textBox = toInlineTextBox(r->box());
- int expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount;
- textBox->setExpansion(expansion);
+ if (run->renderer().style().collapseWhiteSpace()) {
+ InlineTextBox& textBox = downcast<InlineTextBox>(*run->box());
+ float expansion = (availableLogicalWidth - totalLogicalWidth) * opportunitiesInRun / expansionOpportunityCount;
+ textBox.setExpansion(expansion);
totalLogicalWidth += expansion;
}
expansionOpportunityCount -= opportunitiesInRun;
- if (!expansionOpportunityCount)
- break;
- }
+ } else if (is<RenderRubyRun>(run->renderer()))
+ updateRubyForJustifiedText(downcast<RenderRubyRun>(run->renderer()), *run, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth, i);
+
+ if (!expansionOpportunityCount)
+ break;
}
}
-void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float& availableLogicalWidth, int expansionOpportunityCount)
+void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign, const RootInlineBox* rootInlineBox, 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
@@ -604,13 +654,13 @@ void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign
}
FALLTHROUGH;
case TASTART:
- if (style().isLeftToRightDirection())
+ if (direction == LTR)
updateLogicalWidthForLeftAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
else
updateLogicalWidthForRightAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
break;
case TAEND:
- if (style().isLeftToRightDirection())
+ if (direction == LTR)
updateLogicalWidthForRightAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
else
updateLogicalWidthForLeftAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
@@ -618,11 +668,17 @@ void RenderBlockFlow::updateLogicalWidthForAlignment(const ETextAlign& textAlign
}
}
-static void updateLogicalInlinePositions(RenderBlockFlow& block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, bool firstLine, IndentTextOrNot shouldIndentText, LayoutUnit boxLogicalHeight)
+static void updateLogicalInlinePositions(RenderBlockFlow& block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, bool firstLine,
+ IndentTextOrNot shouldIndentText, LayoutUnit boxLogicalHeight, RootInlineBox* rootBox)
{
LayoutUnit lineLogicalHeight = block.minLineHeightForReplacedRenderer(firstLine, boxLogicalHeight);
- lineLogicalLeft = block.pixelSnappedLogicalLeftOffsetForLine(block.logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight);
- lineLogicalRight = block.pixelSnappedLogicalRightOffsetForLine(block.logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight);
+ 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);
+ }
availableLogicalWidth = lineLogicalRight - lineLogicalLeft;
}
@@ -640,121 +696,295 @@ void RenderBlockFlow::computeInlineDirectionPositionsForLine(RootInlineBox* line
float lineLogicalLeft;
float lineLogicalRight;
float availableLogicalWidth;
- updateLogicalInlinePositions(*this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, 0);
+ updateLogicalInlinePositions(*this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, 0, lineBox);
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 = toRenderBox(firstRun->renderer());
- updateLogicalInlinePositions(*this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, renderBox.logicalHeight());
+ RenderBox& renderBox = downcast<RenderBox>(firstRun->renderer());
+ updateLogicalInlinePositions(*this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, renderBox.logicalHeight(), lineBox);
}
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, textBoxDataMap);
+ 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;
+ }
}
+static bool inlineAncestorHasStartBorderPaddingOrMargin(const RenderBlockFlow& block, const InlineBox& box)
+{
+ bool isLTR = block.style().isLeftToRightDirection();
+ for (auto* currentBox = box.parent(); currentBox; currentBox = currentBox->parent()) {
+ if ((isLTR && currentBox->marginBorderPaddingLogicalLeft() > 0)
+ || (!isLTR && currentBox->marginBorderPaddingLogicalRight() > 0))
+ return true;
+ }
+ return false;
+}
+
+static bool inlineAncestorHasEndBorderPaddingOrMargin(const RenderBlockFlow& block, const InlineBox& box)
+{
+ bool isLTR = block.style().isLeftToRightDirection();
+ for (auto* currentBox = box.parent(); currentBox; currentBox = currentBox->parent()) {
+ if ((isLTR && currentBox->marginBorderPaddingLogicalRight() > 0)
+ || (!isLTR && currentBox->marginBorderPaddingLogicalLeft() > 0))
+ return true;
+ }
+ return false;
+}
+
+static bool isLastInFlowRun(BidiRun& runToCheck)
+{
+ for (auto* run = runToCheck.next(); run; run = run->next()) {
+ if (!run->box() || run->renderer().isOutOfFlowPositioned() || run->box()->isLineBreak())
+ continue;
+ return false;
+ }
+ return true;
+}
+
BidiRun* RenderBlockFlow::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft,
float& availableLogicalWidth, BidiRun* firstRun, BidiRun* trailingSpaceRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache,
WordMeasurements& wordMeasurements)
{
bool needsWordSpacing = false;
+ bool canHangPunctuationAtStart = style().hangingPunctuation() & FirstHangingPunctuation;
+ bool canHangPunctuationAtEnd = style().hangingPunctuation() & LastHangingPunctuation;
+ bool isLTR = style().isLeftToRightDirection();
float totalLogicalWidth = lineBox->getFlowSpacingLogicalWidth();
unsigned expansionOpportunityCount = 0;
- bool isAfterExpansion = true;
+ bool isAfterExpansion = is<RenderRubyBase>(*this) ? downcast<RenderRubyBase>(*this).isAfterExpansion() : true;
Vector<unsigned, 16> expansionOpportunities;
- 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())
+
+ BidiRun* run = firstRun;
+ BidiRun* previousRun = nullptr;
+ for (; run; run = run->next()) {
+ auto computeExpansionOpportunities = [&expansionOpportunities, &expansionOpportunityCount, textAlign, &isAfterExpansion] (RenderBlockFlow& block,
+ InlineTextBox& textBox, BidiRun* previousRun, BidiRun* nextRun, const StringView& stringView, TextDirection direction)
+ {
+ if (stringView.isEmpty()) {
+ // Empty runs should still produce an entry in expansionOpportunities list so that the number of items matches the number of runs.
+ expansionOpportunities.append(0);
+ return;
+ }
+ ExpansionBehavior expansionBehavior = expansionBehaviorForInlineTextBox(block, textBox, previousRun, nextRun, textAlign, isAfterExpansion);
+ applyExpansionBehavior(textBox, expansionBehavior);
+ unsigned opportunitiesInRun;
+ std::tie(opportunitiesInRun, isAfterExpansion) = FontCascade::expansionOpportunityCount(stringView, direction, expansionBehavior);
+ expansionOpportunities.append(opportunitiesInRun);
+ expansionOpportunityCount += opportunitiesInRun;
+ };
+ if (!run->box() || run->renderer().isOutOfFlowPositioned() || run->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 (r->renderer().isText()) {
- RenderText& rt = toRenderText(r->renderer());
- if (textAlign == JUSTIFY && r != trailingSpaceRun) {
- if (!isAfterExpansion)
- toInlineTextBox(r->box())->setCanHaveLeadingExpansion(true);
- unsigned opportunitiesInRun;
- 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 (is<RenderText>(run->renderer())) {
+ auto& renderText = downcast<RenderText>(run->renderer());
+ auto& textBox = downcast<InlineTextBox>(*run->box());
+ if (canHangPunctuationAtStart && lineInfo.isFirstLine() && (isLTR || isLastInFlowRun(*run))
+ && !inlineAncestorHasStartBorderPaddingOrMargin(*this, *run->box())) {
+ float hangStartWidth = renderText.hangablePunctuationStartWidth(run->m_start);
+ availableLogicalWidth += hangStartWidth;
+ if (style().isLeftToRightDirection())
+ logicalLeft -= hangStartWidth;
+ canHangPunctuationAtStart = false;
}
-
- 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;
+
+ if (canHangPunctuationAtEnd && lineInfo.isLastLine() && run->m_stop > 0 && (!isLTR || isLastInFlowRun(*run))
+ && !inlineAncestorHasEndBorderPaddingOrMargin(*this, *run->box())) {
+ float hangEndWidth = renderText.hangablePunctuationEndWidth(run->m_stop - 1);
+ availableLogicalWidth += hangEndWidth;
+ if (!style().isLeftToRightDirection())
+ logicalLeft -= hangEndWidth;
+ canHangPunctuationAtEnd = false;
+ }
+
+ if (textAlign == JUSTIFY && run != trailingSpaceRun)
+ computeExpansionOpportunities(*this, textBox, previousRun, run->next(), renderText.stringView(run->m_start, run->m_stop), run->box()->direction());
+
+ if (unsigned length = renderText.textLength()) {
+ if (!run->m_start && needsWordSpacing && isSpaceOrNewline(renderText.characterAt(run->m_start)))
+ totalLogicalWidth += lineStyle(*renderText.parent(), lineInfo).fontCascade().wordSpacing();
+ // run->m_start == run->m_stop should only be true iff the run is a replaced run for bidi: isolate.
+ ASSERT(run->m_stop > 0 || run->m_start == run->m_stop);
+ needsWordSpacing = run->m_stop == length && !isSpaceOrNewline(renderText.characterAt(run->m_stop - 1));
}
- setLogicalWidthForTextRun(lineBox, r, &rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements);
+ setLogicalWidthForTextRun(lineBox, run, renderText, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements);
} else {
- 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));
+ canHangPunctuationAtStart = false;
+ 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;
+ encounteredJustifiedRuby = true;
+ computeExpansionOpportunities(*rubyBase, downcast<InlineTextBox>(*leafChild), nullptr, nullptr,
+ downcast<RenderText>(leafChild->renderer()).stringView(), leafChild->direction());
+ }
+ }
+ }
+
+ 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));
totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox);
}
}
- totalLogicalWidth += r->box()->logicalWidth();
- previousObject = &r->renderer();
+ totalLogicalWidth += run->box()->logicalWidth();
+ previousRun = run;
}
if (isAfterExpansion && !expansionOpportunities.isEmpty()) {
- expansionOpportunities.last()--;
- expansionOpportunityCount--;
+ // FIXME: see <webkit.org/b/139393#c11>
+ int lastValidExpansionOpportunitiesIndex = expansionOpportunities.size() - 1;
+ while (lastValidExpansionOpportunitiesIndex >= 0 && !expansionOpportunities.at(lastValidExpansionOpportunitiesIndex))
+ --lastValidExpansionOpportunitiesIndex;
+ if (lastValidExpansionOpportunitiesIndex >= 0) {
+ ASSERT(expansionOpportunities.at(lastValidExpansionOpportunitiesIndex));
+ expansionOpportunities.at(lastValidExpansionOpportunitiesIndex)--;
+ expansionOpportunityCount--;
+ }
}
- updateLogicalWidthForAlignment(textAlign, trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth, expansionOpportunityCount);
+ if (is<RenderRubyBase>(*this) && !expansionOpportunityCount)
+ textAlign = CENTER;
+
+ updateLogicalWidthForAlignment(textAlign, lineBox, trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth, expansionOpportunityCount);
computeExpansionForJustifiedText(firstRun, trailingSpaceRun, expansionOpportunities, expansionOpportunityCount, totalLogicalWidth, availableLogicalWidth);
- return r;
+ 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();
+ }
}
void RenderBlockFlow::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
@@ -763,30 +993,33 @@ void RenderBlockFlow::computeBlockDirectionPositionsForLine(RootInlineBox* lineB
setLogicalHeight(lineBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache));
// Now make sure we place replaced render objects correctly.
- for (BidiRun* r = firstRun; r; r = r->next()) {
- ASSERT(r->box());
- if (!r->box())
+ for (auto* run = firstRun; run; run = run->next()) {
+ ASSERT(run->box());
+ if (!run->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.
- if (r->renderer().isOutOfFlowPositioned())
- box.setLogicalTop(logicalHeight());
+ auto& renderer = run->renderer();
+ if (renderer.isOutOfFlowPositioned())
+ run->box()->setLogicalTop(logicalHeight());
// Position is used to properly position both replaced elements and
// to update the static normal flow x/y of positioned elements.
- 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);
+ 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);
+ }
}
static inline bool isCollapsibleSpace(UChar character, const RenderText& renderer)
@@ -801,9 +1034,9 @@ static inline bool isCollapsibleSpace(UChar character, const RenderText& rendere
}
template <typename CharacterType>
-static inline int findFirstTrailingSpace(const RenderText& lastText, const CharacterType* characters, int start, int stop)
+static inline unsigned findFirstTrailingSpace(const RenderText& lastText, const CharacterType* characters, unsigned start, unsigned stop)
{
- int firstSpace = stop;
+ unsigned firstSpace = stop;
while (firstSpace > start) {
UChar current = characters[firstSpace - 1];
if (!isCollapsibleSpace(current, lastText))
@@ -819,22 +1052,22 @@ inline BidiRun* RenderBlockFlow::handleTrailingSpaces(BidiRunList<BidiRun>& bidi
if (!bidiRuns.runCount()
|| !bidiRuns.logicallyLastRun()->renderer().style().breakOnlyAfterWhiteSpace()
|| !bidiRuns.logicallyLastRun()->renderer().style().autoWrap())
- return 0;
+ return nullptr;
BidiRun* trailingSpaceRun = bidiRuns.logicallyLastRun();
const RenderObject& lastObject = trailingSpaceRun->renderer();
- if (!lastObject.isText())
- return 0;
+ if (!is<RenderText>(lastObject))
+ return nullptr;
- const RenderText& lastText = toRenderText(lastObject);
- int firstSpace;
+ const RenderText& lastText = downcast<RenderText>(lastObject);
+ unsigned firstSpace;
if (lastText.is8Bit())
firstSpace = findFirstTrailingSpace(lastText, lastText.characters8(), trailingSpaceRun->start(), trailingSpaceRun->stop());
else
firstSpace = findFirstTrailingSpace(lastText, lastText.characters16(), trailingSpaceRun->start(), trailingSpaceRun->stop());
if (firstSpace == trailingSpaceRun->stop())
- return 0;
+ return nullptr;
TextDirection direction = style().direction();
bool shouldReorder = trailingSpaceRun != (direction == LTR ? bidiRuns.lastRun() : bidiRuns.firstRun());
@@ -843,13 +1076,13 @@ inline BidiRun* RenderBlockFlow::handleTrailingSpaces(BidiRunList<BidiRun>& bidi
while (BidiContext* parent = baseContext->parent())
baseContext = parent;
- BidiRun* newTrailingRun = new BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->renderer(), baseContext, U_OTHER_NEUTRAL);
+ std::unique_ptr<BidiRun> newTrailingRun = std::make_unique<BidiRun>(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->renderer(), baseContext, U_OTHER_NEUTRAL);
trailingSpaceRun->m_stop = firstSpace;
+ trailingSpaceRun = newTrailingRun.get();
if (direction == LTR)
- bidiRuns.addRun(newTrailingRun);
+ bidiRuns.appendRun(WTFMove(newTrailingRun));
else
- bidiRuns.prependRun(newTrailingRun);
- trailingSpaceRun = newTrailingRun;
+ bidiRuns.prependRun(WTFMove(newTrailingRun));
return trailingSpaceRun;
}
if (!shouldReorder)
@@ -865,22 +1098,32 @@ inline BidiRun* RenderBlockFlow::handleTrailingSpaces(BidiRunList<BidiRun>& bidi
return trailingSpaceRun;
}
-void RenderBlockFlow::appendFloatingObjectToLastLine(FloatingObject* floatingObject)
+void RenderBlockFlow::appendFloatingObjectToLastLine(FloatingObject& floatingObject)
{
- ASSERT(!floatingObject->originatingLine());
- floatingObject->setOriginatingLine(lastRootBox());
- lastRootBox()->appendFloat(floatingObject->renderer());
+ ASSERT_WITH_SECURITY_IMPLICATION(!floatingObject.originatingLine());
+ floatingObject.setOriginatingLine(lastRootBox());
+ lastRootBox()->appendFloat(floatingObject.renderer());
}
-static inline void setUpResolverToResumeInIsolate(InlineBidiResolver& resolver, RenderObject* root, RenderObject* startObject)
+static inline void notifyResolverToResumeInIsolate(InlineBidiResolver& resolver, RenderObject* root, RenderObject* startObject)
{
if (root != startObject) {
RenderObject* parent = startObject->parent();
- setUpResolverToResumeInIsolate(resolver, root, parent);
+ notifyResolverToResumeInIsolate(resolver, root, parent);
notifyObserverEnteredObject(&resolver, startObject);
}
}
+static inline void setUpResolverToResumeInIsolate(InlineBidiResolver& resolver, InlineBidiResolver& topResolver, BidiRun& isolatedRun, RenderObject* root, RenderObject* startObject)
+{
+ // Set up m_whitespaceCollapsingState
+ resolver.whitespaceCollapsingState() = topResolver.whitespaceCollapsingState();
+ resolver.whitespaceCollapsingState().setCurrentTransition(topResolver.whitespaceCollapsingTransitionForIsolatedRun(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)
{
@@ -893,36 +1136,37 @@ 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.
- BidiRun* isolatedRun = topResolver.isolatedRuns().last();
+ auto isolatedRun = WTFMove(topResolver.isolatedRuns().last());
topResolver.isolatedRuns().removeLast();
+ currentRoot = &isolatedRun.root;
- RenderObject& startObject = isolatedRun->renderer();
+ RenderObject& startObject = isolatedRun.object;
// 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 = toRenderInline(highestContainingIsolateWithinRoot(startObject, currentRoot));
+ RenderInline* isolatedInline = downcast<RenderInline>(highestContainingIsolateWithinRoot(startObject, currentRoot));
ASSERT(isolatedInline);
InlineBidiResolver isolatedResolver;
EUnicodeBidi unicodeBidi = isolatedInline->style().unicodeBidi();
TextDirection direction;
if (unicodeBidi == Plaintext)
- determineDirectionality(direction, InlineIterator(isolatedInline, &isolatedRun->renderer(), 0));
+ determineDirectionality(direction, InlineIterator(isolatedInline, &isolatedRun.object, 0));
else {
ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride);
direction = isolatedInline->style().direction();
}
isolatedResolver.setStatus(BidiStatus(direction, isOverride(unicodeBidi)));
- setUpResolverToResumeInIsolate(isolatedResolver, isolatedInline, &startObject);
+ setUpResolverToResumeInIsolate(isolatedResolver, topResolver, isolatedRun.runToReplace, 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->m_start);
+ InlineIterator iter = InlineIterator(isolatedInline, &startObject, isolatedRun.position);
isolatedResolver.setPositionIgnoringNestedIsolates(iter);
// We stop at the next end of line; we may re-enter this isolate in the next call to constructBidiRuns().
@@ -934,74 +1178,36 @@ 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, isolatedResolver.runs());
+ bidiRuns.replaceRunWithRuns(&isolatedRun.runToReplace, isolatedResolver.runs());
// If we encountered any nested isolate runs, just move them
// to the top resolver's list for later processing.
- 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);
+ while (!isolatedResolver.isolatedRuns().isEmpty()) {
+ auto runWithContext = WTFMove(isolatedResolver.isolatedRuns().last());
+ isolatedResolver.isolatedRuns().removeLast();
+ topResolver.setWhitespaceCollapsingTransitionForIsolatedRun(runWithContext.runToReplace, isolatedResolver.whitespaceCollapsingTransitionForIsolatedRun(runWithContext.runToReplace));
+ topResolver.isolatedRuns().append(WTFMove(runWithContext));
}
- 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(BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements)
+RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(unsigned bidiLevel, BidiRunList<BidiRun>& bidiRuns, const InlineIterator& end, LineInfo& lineInfo, VerticalPositionCache& verticalPositionCache, BidiRun* trailingSpaceRun, WordMeasurements& wordMeasurements)
{
if (!bidiRuns.runCount())
- return 0;
+ return nullptr;
// FIXME: Why is this only done when we had runs?
lineInfo.setLastLine(!end.renderer());
RootInlineBox* lineBox = constructLine(bidiRuns, lineInfo);
if (!lineBox)
- return 0;
+ return nullptr;
+ lineBox->setBidiLevel(bidiLevel);
lineBox->setEndsWithBreak(lineInfo.previousLineBrokeCleanly());
-#if ENABLE(SVG)
- bool isSVGRootInlineBox = lineBox->isSVGRootInlineBox();
-#else
- bool isSVGRootInlineBox = false;
-#endif
+ bool isSVGRootInlineBox = is<SVGRootInlineBox>(*lineBox);
GlyphOverflowAndFallbackFontsMap textBoxDataMap;
@@ -1012,7 +1218,6 @@ RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(BidiRunList<BidiRun>
// 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
@@ -1020,18 +1225,12 @@ RootInlineBox* RenderBlockFlow::createLineBoxesFromBidiRuns(BidiRunList<BidiRun>
// text selection in RTL boxes would not work as expected.
if (isSVGRootInlineBox) {
ASSERT_WITH_SECURITY_IMPLICATION(isSVGText());
- toSVGRootInlineBox(lineBox)->computePerCharacterLayoutInformation();
+ downcast<SVGRootInlineBox>(*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;
}
@@ -1048,11 +1247,28 @@ static void deleteLineRange(LineLayoutState& layoutState, RootInlineBox* startLi
}
}
+static void repaintDirtyFloats(LineLayoutState::FloatList& floats)
+{
+ // Floats that did not have layout did not repaint when we laid them out. They would have
+ // painted by now if they had moved, but if they stayed at (0, 0), they still need to be
+ // painted.
+ for (auto& floatBox : floats) {
+ if (floatBox->everHadLayout())
+ continue;
+ auto& box = floatBox->renderer();
+ if (!box.x() && !box.y() && box.checkForRepaintDuringLayout())
+ box.repaint();
+ }
+}
+
void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState, bool hasInlineChild)
{
// 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) {
@@ -1065,17 +1281,17 @@ void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState, bool has
// determineStartPosition first will break fast/repaint/line-flow-with-floats-9.html.
if (layoutState.isFullLayout() && hasInlineChild && !selfNeedsLayout()) {
setNeedsLayout(MarkOnlyThis); // Mark as needing a full layout to force us to repaint.
- if (!view().doingFullRepaint() && hasLayer()) {
+ if (!view().doingFullRepaint() && hasSelfPaintingLayer() && layer()->hasComputedRepaintRect()) {
// Because we waited until we were already inside layout to discover
// 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(), pixelSnappedIntRect(layer()->repaintRect()));
+ repaintUsingContainer(containerForRepaint(), layer()->repaintRect());
}
}
if (containsFloats())
- layoutState.setLastFloat(m_floatingObjects->set().last().get());
+ layoutState.floatList().setLastFloat(m_floatingObjects->set().last().get());
// We also find the first clean line and extract these lines. We will add them back
// if we determine that we're able to synchronize after handling all our dirty lines.
@@ -1101,24 +1317,14 @@ void RenderBlockFlow::layoutRunsAndFloats(LineLayoutState& layoutState, bool has
if (lastObject->isBR()) {
EClear clear = lastObject->style().clear();
if (clear != CNONE)
- newLine(clear);
+ clearFloats(clear);
}
}
}
layoutRunsAndFloatsInRange(layoutState, resolver, cleanLineStart, cleanLineBidiStatus, consecutiveHyphenatedLines);
linkToEndLineIfNeeded(layoutState);
- repaintDirtyFloats(layoutState.floats());
-}
-
-RenderTextInfo::RenderTextInfo()
- : m_text(0)
- , m_font(0)
-{
-}
-
-RenderTextInfo::~RenderTextInfo()
-{
+ repaintDirtyFloats(layoutState.floatList());
}
// Before restarting the layout loop with a new logicalHeight, remove all floats that were added and reset the resolver.
@@ -1130,175 +1336,11 @@ 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();
bool paginated = view().layoutState() && view().layoutState()->isPaginated();
- LineMidpointState& lineMidpointState = resolver.midpointState();
+ LineWhitespaceCollapsingState& lineWhitespaceCollapsingState = resolver.whitespaceCollapsingState();
InlineIterator end = resolver.position();
bool checkForEndLineMatch = layoutState.endLine();
RenderTextInfo renderTextInfo;
@@ -1306,65 +1348,40 @@ 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;
}
}
- lineMidpointState.reset();
+ lineWhitespaceCollapsingState.reset();
layoutState.lineInfo().setEmpty(true);
layoutState.lineInfo().resetRunsFromLeadingWhitespace();
const InlineIterator oldEnd = end;
bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly();
- FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last().get() : 0;
+ FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last().get() : nullptr;
-#if ENABLE(CSS_SHAPES)
- updateShapeAndSegmentsForCurrentLine(shapeInsideInfo, logicalOffsetFromShapeContainer, layoutState);
-#endif
WordMeasurements wordMeasurements;
- end = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
- renderTextInfo.m_lineBreakIterator.resetPriorContext();
+ end = lineBreaker.nextLineBreak(resolver, layoutState.lineInfo(), layoutState, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
+ cachePriorCharactersIfNeeded(renderTextInfo.lineBreakIterator);
+ renderTextInfo.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.
- resolver.runs().deleteRuns();
+ resolver.runs().clear();
resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed).
layoutState.setCheckForFloatsFromLastLine(true);
resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0);
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.
@@ -1381,10 +1398,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();
- constructBidiRunsForLine(this, resolver, bidiRuns, end, override, layoutState.lineInfo().previousLineBrokeCleanly());
+ constructBidiRunsForSegment(resolver, bidiRuns, end, override, layoutState.lineInfo().previousLineBrokeCleanly());
ASSERT(resolver.position() == end);
- BidiRun* trailingSpaceRun = !layoutState.lineInfo().previousLineBrokeCleanly() ? handleTrailingSpaces(bidiRuns, resolver.context()) : 0;
+ BidiRun* trailingSpaceRun = !layoutState.lineInfo().previousLineBrokeCleanly() ? handleTrailingSpaces(bidiRuns, resolver.context()) : nullptr;
if (bidiRuns.runCount() && lineBreaker.lineWasHyphenated()) {
bidiRuns.logicallyLastRun()->m_hasHyphen = true;
@@ -1397,33 +1414,63 @@ void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, I
// inline flow boxes.
LayoutUnit oldLogicalHeight = logicalHeight();
- RootInlineBox* lineBox = createLineBoxesFromBidiRuns(bidiRuns, end, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, wordMeasurements);
+ RootInlineBox* lineBox = createLineBoxesFromBidiRuns(resolver.status().context->level(), bidiRuns, end, layoutState.lineInfo(), verticalPositionCache, trailingSpaceRun, wordMeasurements);
- bidiRuns.deleteRuns();
+ bidiRuns.clear();
resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed).
if (lineBox) {
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) {
- 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;
+ 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);
}
-
- setLogicalHeight(lineBox->lineBottomWithLeading());
}
if (layoutState.flowThread())
@@ -1433,36 +1480,35 @@ void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, I
}
for (size_t i = 0; i < lineBreaker.positionedObjects().size(); ++i)
- setStaticPositions(*this, *lineBreaker.positionedObjects()[i]);
+ setStaticPositions(*this, *lineBreaker.positionedObjects()[i], DoNotIndentText);
if (!layoutState.lineInfo().isEmpty()) {
layoutState.lineInfo().setFirstLine(false);
- newLine(lineBreaker.clear());
+ clearFloats(lineBreaker.clear());
}
if (m_floatingObjects && lastRootBox()) {
const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
auto it = floatingObjectSet.begin();
auto end = floatingObjectSet.end();
- if (layoutState.lastFloat()) {
- auto lastFloatIterator = floatingObjectSet.find<FloatingObject&, FloatingObjectHashTranslator>(*layoutState.lastFloat());
+ if (auto* lastFloat = layoutState.floatList().lastFloat()) {
+ auto lastFloatIterator = floatingObjectSet.find<FloatingObject&, FloatingObjectHashTranslator>(*lastFloat);
ASSERT(lastFloatIterator != end);
++lastFloatIterator;
it = lastFloatIterator;
}
for (; it != end; ++it) {
- FloatingObject* f = it->get();
- appendFloatingObjectToLastLine(f);
- ASSERT(&f->renderer() == &layoutState.floats()[layoutState.floatIndex()].object);
+ auto& floatingObject = *it;
+ appendFloatingObjectToLastLine(*floatingObject);
// If a float's geometry has changed, give up on syncing with clean lines.
- if (layoutState.floats()[layoutState.floatIndex()].rect != f->frameRect())
+ auto* floatWithRect = layoutState.floatList().floatWithRect(floatingObject->renderer());
+ if (!floatWithRect || floatWithRect->rect() != floatingObject->frameRect())
checkForEndLineMatch = false;
- layoutState.setFloatIndex(layoutState.floatIndex() + 1);
}
- layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last().get() : nullptr);
+ layoutState.floatList().setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last().get() : nullptr);
}
- lineMidpointState.reset();
+ lineWhitespaceCollapsingState.reset();
resolver.setPosition(end, numberOfIsolateAncestors(end));
}
@@ -1525,22 +1571,44 @@ 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();
+ }
+}
+
void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState)
{
- if (layoutState.endLine()) {
+ auto* firstCleanLine = layoutState.endLine();
+ if (firstCleanLine) {
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 (RootInlineBox* line = layoutState.endLine(); line; line = line->nextRootBox()) {
+ for (auto* line = firstCleanLine; line; line = line->nextRootBox()) {
line->attachLine();
if (paginated) {
delta -= line->paginationStrut();
- adjustLinePositionForPagination(line, delta, layoutState.flowThread());
+ bool overflowsRegion;
+ adjustLinePositionForPagination(line, delta, overflowsRegion, layoutState.flowThread());
}
if (delta) {
layoutState.updateRepaintRangeFromBox(line, delta);
@@ -1548,16 +1616,7 @@ void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState)
}
if (layoutState.flowThread())
updateRegionForLine(line);
- 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();
- }
- }
+ reattachCleanLineFloats(*line, delta, line == firstCleanLine);
}
setLogicalHeight(lastRootBox()->lineBottomWithLeading());
} else {
@@ -1575,7 +1634,7 @@ void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState)
LayoutUnit bottomLayoutOverflow = lastRootBox()->logicalBottomLayoutOverflow();
auto newLineBox = std::make_unique<TrailingFloatsRootInlineBox>(*this);
auto trailingFloatsLineBox = newLineBox.get();
- m_lineBoxes.appendLineBox(std::move(newLineBox));
+ m_lineBoxes.appendLineBox(WTFMove(newLineBox));
trailingFloatsLineBox->setConstructed();
GlyphOverflowAndFallbackFontsMap textBoxDataMap;
VerticalPositionCache verticalPositionCache;
@@ -1593,30 +1652,15 @@ void RenderBlockFlow::linkToEndLineIfNeeded(LineLayoutState& layoutState)
const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
auto it = floatingObjectSet.begin();
auto end = floatingObjectSet.end();
- if (layoutState.lastFloat()) {
- auto lastFloatIterator = floatingObjectSet.find<FloatingObject&, FloatingObjectHashTranslator>(*layoutState.lastFloat());
+ if (auto* lastFloat = layoutState.floatList().lastFloat()) {
+ auto lastFloatIterator = floatingObjectSet.find<FloatingObject&, FloatingObjectHashTranslator>(*lastFloat);
ASSERT(lastFloatIterator != end);
++lastFloatIterator;
it = lastFloatIterator;
}
for (; it != end; ++it)
- appendFloatingObjectToLastLine(it->get());
- layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last().get() : nullptr);
- }
-}
-
-void RenderBlockFlow::repaintDirtyFloats(Vector<FloatWithRect>& floats)
-{
- size_t floatCount = floats.size();
- // Floats that did not have layout did not repaint when we laid them out. They would have
- // painted by now if they had moved, but if they stayed at (0, 0), they still need to be
- // painted.
- for (size_t i = 0; i < floatCount; ++i) {
- if (!floats[i].everHadLayout) {
- RenderBox& box = floats[i].object;
- if (!box.x() && !box.y() && box.checkForRepaintDuringLayout())
- box.repaint();
- }
+ appendFloatingObjectToLastLine(**it);
+ layoutState.floatList().setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last().get() : nullptr);
}
}
@@ -1636,7 +1680,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(isFullLayout, repaintLogicalTop, repaintLogicalBottom, flowThread);
+ LineLayoutState layoutState(*this, isFullLayout, repaintLogicalTop, repaintLogicalBottom, flowThread);
if (isFullLayout)
lineBoxes().deleteLineBoxes();
@@ -1669,7 +1713,7 @@ void RenderBlockFlow::layoutLineBoxes(bool relayoutChildren, LayoutUnit& repaint
hasInlineChild = true;
if (o.isReplaced() || o.isFloating() || o.isOutOfFlowPositioned()) {
- RenderBox& box = toRenderBox(o);
+ RenderBox& box = downcast<RenderBox>(o);
if (relayoutChildren || box.hasRelativeDimensions())
box.setChildNeedsLayout(MarkOnlyThis);
@@ -1681,18 +1725,20 @@ void RenderBlockFlow::layoutLineBoxes(bool relayoutChildren, LayoutUnit& repaint
if (box.isOutOfFlowPositioned())
box.containingBlock()->insertPositionedObject(box);
else if (box.isFloating())
- layoutState.floats().append(FloatWithRect(box));
+ layoutState.floatList().append(FloatWithRect::create(box));
else if (isFullLayout || box.needsLayout()) {
// Replaced element.
box.dirtyLineBoxes(isFullLayout);
- if (isFullLayout)
- replacedChildren.append(&box);
- else
- box.layoutIfNeeded();
+ if (!o.isAnonymousInlineBlock()) {
+ if (isFullLayout)
+ replacedChildren.append(&box);
+ else
+ box.layoutIfNeeded();
+ }
}
- } else if (o.isTextOrLineBreak() || (o.isRenderInline() && !walker.atEndOfInline())) {
- if (o.isRenderInline())
- toRenderInline(o).updateAlwaysCreateLineBoxes(layoutState.isFullLayout());
+ } else if (o.isTextOrLineBreak() || (is<RenderInline>(o) && !walker.atEndOfInline())) {
+ if (is<RenderInline>(o))
+ downcast<RenderInline>(o).updateAlwaysCreateLineBoxes(layoutState.isFullLayout());
if (layoutState.isFullLayout() || o.selfNeedsLayout())
dirtyLineBoxesForRenderer(o, layoutState.isFullLayout());
o.clearNeedsLayout();
@@ -1714,9 +1760,17 @@ void RenderBlockFlow::layoutLineBoxes(bool relayoutChildren, LayoutUnit& repaint
else
lastLineAnnotationsAdjustment = lastRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition);
}
-
- // Now add in the bottom border/padding.
- setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + borderAndPaddingAfter() + scrollbarLogicalHeight());
+
+ // 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);
if (!firstRootBox() && hasLineIfEmpty())
setLogicalHeight(logicalHeight() + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
@@ -1727,146 +1781,146 @@ void RenderBlockFlow::layoutLineBoxes(bool relayoutChildren, LayoutUnit& repaint
checkLinesForTextOverflow();
}
-void RenderBlockFlow::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWithRect>& floats, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByFloat)
+void RenderBlockFlow::checkFloatInCleanLine(RootInlineBox& cleanLine, RenderBox& floatBoxOnCleanLine, FloatWithRect& matchingFloatWithRect,
+ bool& encounteredNewFloat, bool& dirtiedByFloat)
{
- Vector<RenderBox*>* cleanLineFloats = line->floatsPtr();
- if (!cleanLineFloats)
- return;
-
- if (!floats.size()) {
+ ASSERT_WITH_SECURITY_IMPLICATION(!floatBoxOnCleanLine.style().deletionHasBegun());
+ if (&matchingFloatWithRect.renderer() != &floatBoxOnCleanLine) {
encounteredNewFloat = true;
return;
}
+ floatBoxOnCleanLine.layoutIfNeeded();
+ LayoutRect originalFloatRect = matchingFloatWithRect.rect();
+ LayoutSize newSize(
+ floatBoxOnCleanLine.width() + floatBoxOnCleanLine.horizontalMarginExtent(),
+ floatBoxOnCleanLine.height() + floatBoxOnCleanLine.verticalMarginExtent());
+
+ // 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 (originalFloatRect.size() == newSize && (floatBoxOnCleanLine.style().styleType() != FIRST_LETTER || !floatBoxOnCleanLine.style().initialLetterDrop()))
+ return;
- for (auto it = cleanLineFloats->begin(), end = cleanLineFloats->end(); it != end; ++it) {
- RenderBox* floatingBox = *it;
- floatingBox->layoutIfNeeded();
- 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;
- }
-
- 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);
- line->markDirty();
- markLinesDirtyInBlockRange(line->lineBottomWithLeading(), floatTop + floatHeight, line);
- floats[floatIndex].rect.setSize(newSize);
- dirtiedByFloat = true;
- }
- floatIndex++;
- }
+ LayoutUnit floatTop = isHorizontalWritingMode() ? originalFloatRect.y() : originalFloatRect.x();
+ LayoutUnit floatHeight = isHorizontalWritingMode() ? std::max(originalFloatRect.height(), newSize.height())
+ : std::max(originalFloatRect.width(), newSize.width());
+ floatHeight = std::min(floatHeight, LayoutUnit::max() - floatTop);
+ cleanLine.markDirty();
+ markLinesDirtyInBlockRange(cleanLine.lineBottomWithLeading(), floatTop + floatHeight, &cleanLine);
+ LayoutRect newFloatRect = originalFloatRect;
+ newFloatRect.setSize(newSize);
+ matchingFloatWithRect.adjustRect(newFloatRect);
+ dirtiedByFloat = true;
}
RootInlineBox* RenderBlockFlow::determineStartPosition(LineLayoutState& layoutState, InlineBidiResolver& resolver)
{
- RootInlineBox* curr = 0;
- RootInlineBox* last = 0;
+ RootInlineBox* currentLine = nullptr;
+ RootInlineBox* lastLine = nullptr;
// FIXME: This entire float-checking block needs to be broken into a new function.
+ auto& floats = layoutState.floatList();
bool dirtiedByFloat = false;
if (!layoutState.isFullLayout()) {
// Paginate all of the clean lines.
bool paginated = view().layoutState() && view().layoutState()->isPaginated();
LayoutUnit paginationDelta = 0;
- size_t floatIndex = 0;
- for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) {
+ auto floatsIterator = floats.begin();
+ auto end = floats.end();
+ for (currentLine = firstRootBox(); currentLine && !currentLine->isDirty(); currentLine = currentLine->nextRootBox()) {
if (paginated) {
- if (lineWidthForPaginatedLineChanged(curr, 0, layoutState.flowThread())) {
- curr->markDirty();
+ if (lineWidthForPaginatedLineChanged(currentLine, 0, layoutState.flowThread())) {
+ currentLine->markDirty();
break;
}
- paginationDelta -= curr->paginationStrut();
- adjustLinePositionForPagination(curr, paginationDelta, layoutState.flowThread());
+ paginationDelta -= currentLine->paginationStrut();
+ bool overflowsRegion;
+ adjustLinePositionForPagination(currentLine, paginationDelta, overflowsRegion, layoutState.flowThread());
if (paginationDelta) {
- if (containsFloats() || !layoutState.floats().isEmpty()) {
+ if (containsFloats() || !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.
layoutState.markForFullLayout();
break;
}
- layoutState.updateRepaintRangeFromBox(curr, paginationDelta);
- curr->adjustBlockDirectionPosition(paginationDelta);
+ layoutState.updateRepaintRangeFromBox(currentLine, paginationDelta);
+ currentLine->adjustBlockDirectionPosition(paginationDelta);
}
if (layoutState.flowThread())
- updateRegionForLine(curr);
+ updateRegionForLine(currentLine);
}
- // If a new float has been inserted before this line or before its last known float, just do a full layout.
- bool encounteredNewFloat = false;
- checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat);
- if (encounteredNewFloat)
- layoutState.markForFullLayout();
-
- if (dirtiedByFloat || layoutState.isFullLayout())
- break;
+ if (auto* cleanLineFloats = currentLine->floatsPtr()) {
+ // If a new float has been inserted before this line or before its last known float, just do a full layout.
+ bool encounteredNewFloat = false;
+ for (auto* floatBoxOnCleanLine : *cleanLineFloats) {
+ ASSERT(floatsIterator != end);
+ checkFloatInCleanLine(*currentLine, *floatBoxOnCleanLine, *floatsIterator, encounteredNewFloat, dirtiedByFloat);
+ ++floatsIterator;
+ if (floatsIterator == end || encounteredNewFloat) {
+ layoutState.markForFullLayout();
+ break;
+ }
+ }
+ if (dirtiedByFloat || encounteredNewFloat)
+ break;
+ }
}
// Check if a new float has been inserted after the last known float.
- if (!curr && floatIndex < layoutState.floats().size())
+ if (!currentLine && floatsIterator != end)
layoutState.markForFullLayout();
}
if (layoutState.isFullLayout()) {
m_lineBoxes.deleteLineBoxTree();
- curr = 0;
-
+ currentLine = nullptr;
ASSERT(!firstRootBox() && !lastRootBox());
} else {
- if (curr) {
+ if (currentLine) {
// We have a dirty line.
- if (RootInlineBox* prevRootBox = curr->prevRootBox()) {
+ if (RootInlineBox* prevRootBox = currentLine->prevRootBox()) {
// We have a previous line.
- if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || !prevRootBox->lineBreakObj() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength())))
+ if (!dirtiedByFloat && !currentLine->hasAnonymousInlineBlock() && (!prevRootBox->endsWithBreak()
+ || !prevRootBox->lineBreakObj()
+ || (is<RenderText>(*prevRootBox->lineBreakObj())
+ && prevRootBox->lineBreakPos() >= downcast<RenderText>(*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;
+ currentLine = 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();
+ lastLine = currentLine ? currentLine->prevRootBox() : lastRootBox();
}
- unsigned numCleanFloats = 0;
- if (!layoutState.floats().isEmpty()) {
+ if (!floats.isEmpty()) {
LayoutUnit savedLogicalHeight = logicalHeight();
// Restore floats from clean lines.
RootInlineBox* line = firstRootBox();
- while (line != curr) {
+ while (line != currentLine) {
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());
+ auto* floatingBox = *it;
+ auto* floatingObject = insertFloatingObject(*floatingBox);
+ ASSERT_WITH_SECURITY_IMPLICATION(!floatingObject->originatingLine());
floatingObject->setOriginatingLine(line);
setLogicalHeight(logicalTopForChild(*floatingBox) - marginBeforeForChild(*floatingBox));
positionNewFloats();
- ASSERT(&layoutState.floats()[numCleanFloats].object == floatingBox);
- numCleanFloats++;
+ floats.setLastCleanFloat(*floatingBox);
}
}
line = line->nextRootBox();
}
setLogicalHeight(savedLogicalHeight);
}
- layoutState.setFloatIndex(numCleanFloats);
- layoutState.lineInfo().setFirstLine(!last);
- layoutState.lineInfo().setPreviousLineBrokeCleanly(!last || last->endsWithBreak());
+ layoutState.lineInfo().setFirstLine(!lastLine);
+ layoutState.lineInfo().setPreviousLineBrokeCleanly(!lastLine || lastLine->endsWithBreak());
- if (last) {
- setLogicalHeight(last->lineBottomWithLeading());
- InlineIterator iter = InlineIterator(this, last->lineBreakObj(), last->lineBreakPos());
+ if (lastLine) {
+ setLogicalHeight(lastLine->lineBottomWithLeading());
+ InlineIterator iter = InlineIterator(this, lastLine->lineBreakObj(), lastLine->lineBreakPos());
resolver.setPosition(iter, numberOfIsolateAncestors(iter));
- resolver.setStatus(last->lineBreakBidiStatus());
+ resolver.setStatus(lastLine->lineBreakBidiStatus());
} else {
TextDirection direction = style().direction();
if (style().unicodeBidi() == Plaintext)
@@ -1875,44 +1929,59 @@ RootInlineBox* RenderBlockFlow::determineStartPosition(LineLayoutState& layoutSt
InlineIterator iter = InlineIterator(this, bidiFirstSkippingEmptyInlines(*this, &resolver), 0);
resolver.setPosition(iter, numberOfIsolateAncestors(iter));
}
- return curr;
+ return currentLine;
}
void RenderBlockFlow::determineEndPosition(LineLayoutState& layoutState, RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus)
{
+ auto iteratorForFirstDirtyFloat = [](LineLayoutState::FloatList& floats) {
+ auto lastCleanFloat = floats.lastCleanFloat();
+ if (!lastCleanFloat)
+ return floats.begin();
+ auto* lastCleanFloatWithRect = floats.floatWithRect(*lastCleanFloat);
+ ASSERT(lastCleanFloatWithRect);
+ return ++floats.find(*lastCleanFloatWithRect);
+ };
+
ASSERT(!layoutState.endLine());
- size_t floatIndex = layoutState.floatIndex();
- RootInlineBox* last = 0;
- for (RootInlineBox* curr = startLine->nextRootBox(); curr; curr = curr->nextRootBox()) {
- if (!curr->isDirty()) {
- bool encounteredNewFloat = false;
- bool dirtiedByFloat = false;
- checkFloatsInCleanLine(curr, layoutState.floats(), floatIndex, encounteredNewFloat, dirtiedByFloat);
- if (encounteredNewFloat)
- return;
+ auto floatsIterator = iteratorForFirstDirtyFloat(layoutState.floatList());
+ auto end = layoutState.floatList().end();
+ RootInlineBox* lastLine = nullptr;
+ for (RootInlineBox* currentLine = startLine->nextRootBox(); currentLine; currentLine = currentLine->nextRootBox()) {
+ if (!currentLine->isDirty()) {
+ if (auto* cleanLineFloats = currentLine->floatsPtr()) {
+ bool encounteredNewFloat = false;
+ bool dirtiedByFloat = false;
+ for (auto* floatBoxOnCleanLine : *cleanLineFloats) {
+ ASSERT(floatsIterator != end);
+ checkFloatInCleanLine(*currentLine, *floatBoxOnCleanLine, *floatsIterator, encounteredNewFloat, dirtiedByFloat);
+ ++floatsIterator;
+ if (floatsIterator == end || encounteredNewFloat)
+ return;
+ }
+ }
}
- if (curr->isDirty())
- last = 0;
- else if (!last)
- last = curr;
+ if (currentLine->isDirty())
+ lastLine = nullptr;
+ else if (!lastLine)
+ lastLine = currentLine;
}
- if (!last)
+ if (!lastLine)
return;
// At this point, |last| is the first line in a run of clean lines that ends with the last line
// in the block.
+ RootInlineBox* previousLine = lastLine->prevRootBox();
+ cleanLineStart = InlineIterator(this, previousLine->lineBreakObj(), previousLine->lineBreakPos());
+ cleanLineBidiStatus = previousLine->lineBreakBidiStatus();
+ layoutState.setEndLineLogicalTop(previousLine->lineBottomWithLeading());
- RootInlineBox* prev = last->prevRootBox();
- cleanLineStart = InlineIterator(this, prev->lineBreakObj(), prev->lineBreakPos());
- cleanLineBidiStatus = prev->lineBreakBidiStatus();
- layoutState.setEndLineLogicalTop(prev->lineBottomWithLeading());
-
- for (RootInlineBox* line = last; line; line = line->nextRootBox())
- line->extractLine(); // Disconnect all line boxes from their render objects while preserving
- // their connections to one another.
-
- layoutState.setEndLine(last);
+ for (RootInlineBox* line = lastLine; line; line = line->nextRootBox()) {
+ // Disconnect all line boxes from their render objects while preserving their connections to one another.
+ line->extractLine();
+ }
+ layoutState.setEndLine(lastLine);
}
bool RenderBlockFlow::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState)
@@ -1928,8 +1997,9 @@ 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, layoutState.flowThread());
+ adjustLinePositionForPagination(lineBox, lineDelta, overflowsRegion, layoutState.flowThread());
lineBox->setPaginationStrut(oldPaginationStrut);
}
if (lineWidthForPaginatedLineChanged(lineBox, lineDelta, layoutState.flowThread()))
@@ -1952,7 +2022,7 @@ bool RenderBlockFlow::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutS
const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
auto end = floatingObjectSet.end();
for (auto it = floatingObjectSet.begin(); it != end; ++it) {
- FloatingObject* floatingObject = it->get();
+ const auto& floatingObject = *it->get();
if (logicalBottomForFloat(floatingObject) >= logicalTop && logicalBottomForFloat(floatingObject) < logicalBottom)
return false;
}
@@ -1982,11 +2052,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 int numLines = 8; // The # of lines we're willing to match against.
+ static const 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()) {
+ if (line->lineBreakObj() == resolver.position().renderer() && line->lineBreakPos() == resolver.position().offset() && !line->hasAnonymousInlineBlock()) {
// We have a match.
if (line->lineBreakBidiStatus() != resolver.status())
return false; // ...but the bidi state doesn't match.
@@ -2033,13 +2103,14 @@ void RenderBlockFlow::addOverflowFromInlineChildren()
endPadding = 1;
for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding));
- RenderRegion* region = curr->containingRegion();
+ RenderRegion* region = flowThreadContainingBlock() ? curr->containingRegion() : nullptr;
if (region)
region->addLayoutOverflowForBox(this, curr->paddedLayoutOverflowRect(endPadding));
if (!hasOverflowClip()) {
- addVisualOverflow(curr->visualOverflowRect(curr->lineTop(), curr->lineBottom()));
+ LayoutRect childVisualOverflowRect = curr->visualOverflowRect(curr->lineTop(), curr->lineBottom());
+ addVisualOverflow(childVisualOverflowRect);
if (region)
- region->addVisualOverflowForBox(this, curr->visualOverflowRect(curr->lineTop(), curr->lineBottom()));
+ region->addVisualOverflowForBox(this, childVisualOverflowRect);
}
}
}
@@ -2048,23 +2119,23 @@ void RenderBlockFlow::deleteEllipsisLineBoxes()
{
ETextAlign textAlign = style().textAlign();
bool ltr = style().isLeftToRightDirection();
- bool firstLine = true;
+ IndentTextOrNot shouldIndentText = IndentText;
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 = pixelSnappedLogicalLeftOffsetForLine(curr->lineTop(), firstLine);
- float availableLogicalWidth = logicalRightOffsetForLine(curr->lineTop(), false) - logicalLeft;
+ float logicalLeft = logicalLeftOffsetForLine(curr->lineTop(), shouldIndentText);
+ float availableLogicalWidth = logicalRightOffsetForLine(curr->lineTop(), DoNotIndentText) - logicalLeft;
float totalLogicalWidth = curr->logicalWidth();
- updateLogicalWidthForAlignment(textAlign, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
+ updateLogicalWidthForAlignment(textAlign, curr, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 0);
if (ltr)
curr->adjustLogicalPosition((logicalLeft - curr->logicalLeft()), 0);
else
curr->adjustLogicalPosition(-(curr->logicalLeft() - logicalLeft), 0);
}
- firstLine = false;
+ shouldIndentText = DoNotIndentText;
}
}
@@ -2072,11 +2143,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 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()));
+ const FontCascade& font = style().fontCascade();
+ static NeverDestroyed<AtomicString> ellipsisStr(&horizontalEllipsis, 1);
+ const FontCascade& firstLineFont = firstLineStyle().fontCascade();
+ float firstLineEllipsisWidth = firstLineFont.width(constructTextRun(&horizontalEllipsis, 1, firstLineStyle()));
+ float ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(constructTextRun(&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
@@ -2086,25 +2157,23 @@ void RenderBlockFlow::checkLinesForTextOverflow()
ETextAlign textAlign = style().textAlign();
bool firstLine = true;
for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
- // 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);
+ 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();
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 = pixelSnappedLogicalRightOffsetForLine(curr->lineTop(), firstLine);
- updateLogicalWidthForAlignment(textAlign, 0, logicalLeft, totalLogicalWidth, truncatedWidth, 0);
+ float truncatedWidth = availableLogicalWidthForLine(curr->lineTop(), shouldIndentText);
+ updateLogicalWidthForAlignment(textAlign, curr, nullptr, logicalLeft, totalLogicalWidth, truncatedWidth, 0);
if (ltr)
curr->adjustLogicalPosition(logicalLeft, 0);
else
@@ -2115,7 +2184,7 @@ void RenderBlockFlow::checkLinesForTextOverflow()
}
}
-bool RenderBlockFlow::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObject* lastFloatFromPreviousLine, LineInfo& lineInfo, LineWidth& width)
+bool RenderBlockFlow::positionNewFloatOnLine(const FloatingObject& newFloat, FloatingObject* lastFloatFromPreviousLine, LineInfo& lineInfo, LineWidth& width)
{
if (!positionNewFloats())
return false;
@@ -2125,14 +2194,14 @@ bool RenderBlockFlow::positionNewFloatOnLine(FloatingObject* newFloat, FloatingO
// 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);
- int paginationStrut = newFloat->paginationStrut();
+ LayoutUnit paginationStrut = newFloat.paginationStrut();
if (floatLogicalTop - paginationStrut != logicalHeight() + lineInfo.floatPaginationStrut())
return true;
@@ -2142,26 +2211,26 @@ bool RenderBlockFlow::positionNewFloatOnLine(FloatingObject* newFloat, FloatingO
auto begin = floatingObjectSet.begin();
while (it != begin) {
--it;
- FloatingObject* floatingObject = it->get();
- if (floatingObject == lastFloatFromPreviousLine)
+ auto& 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(floatingObject->renderer()))
+ if (updateRegionRangeForBoxChild(floatBox))
floatBox.setNeedsLayout(MarkOnlyThis);
- else if (floatBox.isRenderBlock())
- toRenderBlock(floatBox).setChildNeedsLayout(MarkOnlyThis);
+ else if (is<RenderBlock>(floatBox))
+ downcast<RenderBlock>(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);
}
}
@@ -2171,22 +2240,39 @@ bool RenderBlockFlow::positionNewFloatOnLine(FloatingObject* newFloat, FloatingO
return true;
}
-LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(LayoutUnit position, bool firstLine)
+LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(LayoutUnit position, IndentTextOrNot shouldIndentText)
{
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 (textAlign == TASTART || document().settings()->useLegacyTextAlignPositionedElementBehavior()) // FIXME: Handle TAEND here
- return startOffsetForLine(position, firstLine);
+ if (shouldApplyIndentText || settings().useLegacyTextAlignPositionedElementBehavior()) // FIXME: Handle TAEND here
+ return startOffsetForLine(position, shouldIndentText);
// updateLogicalWidthForAlignment() handles the direction of the block so no need to consider it here
float totalLogicalWidth = 0;
- float logicalLeft = logicalLeftOffsetForLine(logicalHeight(), false);
- float availableLogicalWidth = logicalRightOffsetForLine(logicalHeight(), false) - logicalLeft;
- updateLogicalWidthForAlignment(textAlign, 0, logicalLeft, totalLogicalWidth, availableLogicalWidth, 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);
if (!style().isLeftToRightDirection())
return logicalWidth() - logicalLeft;
@@ -2197,10 +2283,14 @@ void RenderBlockFlow::updateRegionForLine(RootInlineBox* lineBox) const
{
ASSERT(lineBox);
- if (auto containingRegion = regionAtBlockOffset(lineBox->lineTopWithLeading()))
- lineBox->setContainingRegion(*containingRegion);
- else
+ if (!hasRegionRangeInFlowThread())
lineBox->clearContainingRegion();
+ else {
+ if (auto containingRegion = regionAtBlockOffset(lineBox->lineTopWithLeading()))
+ lineBox->setContainingRegion(*containingRegion);
+ else
+ lineBox->clearContainingRegion();
+ }
RootInlineBox* prevLineBox = lineBox->prevRootBox();
if (!prevLineBox)
@@ -2213,4 +2303,46 @@ 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;
+
+ // We already handled top of block with startLine.
+ if (stopLine == firstRootBox())
+ 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());
+}
+
}