summaryrefslogtreecommitdiff
path: root/Source/WebCore/rendering/RenderBlockLineLayout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Source/WebCore/rendering/RenderBlockLineLayout.cpp')
-rw-r--r--Source/WebCore/rendering/RenderBlockLineLayout.cpp3376
1 files changed, 960 insertions, 2416 deletions
diff --git a/Source/WebCore/rendering/RenderBlockLineLayout.cpp b/Source/WebCore/rendering/RenderBlockLineLayout.cpp
index c6f283a73..451de09c9 100644
--- a/Source/WebCore/rendering/RenderBlockLineLayout.cpp
+++ b/Source/WebCore/rendering/RenderBlockLineLayout.cpp
@@ -3,6 +3,7 @@
* Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
* Copyright (C) 2010 Google Inc. All rights reserved.
* Copyright (C) 2013 ChangSeok Oh <shivamidow@gmail.com>
+ * Copyright (C) 2013 Adobe Systems Inc. All right reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
@@ -23,383 +24,45 @@
#include "config.h"
+#include "AXObjectCache.h"
#include "BidiResolver.h"
-#include "Hyphenation.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 "RenderArena.h"
-#include "RenderCombineText.h"
-#include "RenderCounter.h"
+#include "RenderBlockFlow.h"
#include "RenderFlowThread.h"
-#include "RenderInline.h"
-#include "RenderLayer.h"
-#include "RenderListMarker.h"
+#include "RenderLineBreak.h"
#include "RenderRegion.h"
-#include "RenderRubyRun.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 "break_lines.h"
#include <wtf/RefCountedLeakCounter.h>
#include <wtf/StdLibExtras.h>
-#include <wtf/Vector.h>
-#include <wtf/unicode/CharacterNames.h>
-
-#if ENABLE(CSS_SHAPES)
-#include "ShapeInsideInfo.h"
-#endif
-
-#if ENABLE(SVG)
-#include "RenderSVGInlineText.h"
-#include "SVGRootInlineBox.h"
-#endif
-
-using namespace std;
-using namespace WTF;
-using namespace Unicode;
namespace WebCore {
-// We don't let our line box tree for a single line get any deeper than this.
-const unsigned cMaxLineDepth = 200;
-
-static LayoutUnit logicalHeightForLine(const RenderBlock* block, bool isFirstLine, LayoutUnit replacedHeight = 0)
-{
- if (!block->document()->inNoQuirksMode() && replacedHeight)
- return replacedHeight;
-
- if (!(block->style(isFirstLine)->lineBoxContain() & LineBoxContainBlock))
- return 0;
-
- return max<LayoutUnit>(replacedHeight, block->lineHeight(isFirstLine, block->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
-}
-
-#if ENABLE(CSS_SHAPES)
-ShapeInsideInfo* RenderBlock::layoutShapeInsideInfo() const
-{
- ShapeInsideInfo* shapeInsideInfo = view()->layoutState()->shapeInsideInfo();
-
- if (!shapeInsideInfo && flowThreadContainingBlock() && allowsShapeInsideInfoSharing()) {
- // regionAtBlockOffset returns regions like an array first={0,N-1}, second={N,M-1}, ...
- LayoutUnit offset = logicalHeight() + logicalHeightForLine(this, false) - LayoutUnit(1);
- RenderRegion* region = regionAtBlockOffset(offset);
- if (region)
- shapeInsideInfo = region->shapeInsideInfo();
- }
-
- return shapeInsideInfo;
-}
-#endif
-
-enum IndentTextOrNot { DoNotIndentText, IndentText };
-
-class LineWidth {
-public:
- LineWidth(RenderBlock* block, bool isFirstLine, IndentTextOrNot shouldIndentText)
- : m_block(block)
- , m_uncommittedWidth(0)
- , m_committedWidth(0)
- , m_overhangWidth(0)
- , m_trailingWhitespaceWidth(0)
- , m_trailingCollapsedWhitespaceWidth(0)
- , m_left(0)
- , m_right(0)
- , m_availableWidth(0)
-#if ENABLE(CSS_SHAPES)
- , m_segment(0)
-#endif
- , m_isFirstLine(isFirstLine)
- , m_shouldIndentText(shouldIndentText)
- {
- ASSERT(block);
-#if ENABLE(CSS_SHAPES)
- if (ShapeInsideInfo* shapeInsideInfo = m_block->layoutShapeInsideInfo())
- m_segment = shapeInsideInfo->currentSegment();
-#endif
- updateAvailableWidth();
- }
- bool fitsOnLine(bool ignoringTrailingSpace = false)
- {
- return ignoringTrailingSpace ? fitsOnLineExcludingTrailingCollapsedWhitespace() : fitsOnLineIncludingExtraWidth(0);
- }
- bool fitsOnLineIncludingExtraWidth(float extra) const { return currentWidth() + extra <= m_availableWidth; }
- bool fitsOnLineExcludingTrailingWhitespace(float extra) const { return currentWidth() - m_trailingWhitespaceWidth + extra <= m_availableWidth; }
-
- float currentWidth() const { return m_committedWidth + m_uncommittedWidth; }
- // FIXME: We should eventually replace these three functions by ones that work on a higher abstraction.
- float uncommittedWidth() const { return m_uncommittedWidth; }
- float committedWidth() const { return m_committedWidth; }
- float availableWidth() const { return m_availableWidth; }
-
- void updateAvailableWidth(LayoutUnit minimumHeight = 0);
- void shrinkAvailableWidthForNewFloatIfNeeded(RenderBlock::FloatingObject*);
- void addUncommittedWidth(float delta) { m_uncommittedWidth += delta; }
- void commit()
- {
- m_committedWidth += m_uncommittedWidth;
- m_uncommittedWidth = 0;
- }
- void applyOverhang(RenderRubyRun*, RenderObject* startRenderer, RenderObject* endRenderer);
- void fitBelowFloats();
- void setTrailingWhitespaceWidth(float collapsedWhitespace, float borderPaddingMargin = 0) { m_trailingCollapsedWhitespaceWidth = collapsedWhitespace; m_trailingWhitespaceWidth = collapsedWhitespace + borderPaddingMargin; }
-
- bool shouldIndentText() const { return m_shouldIndentText == IndentText; }
-
-private:
- void computeAvailableWidthFromLeftAndRight()
- {
- m_availableWidth = max(0.0f, m_right - m_left) + m_overhangWidth;
- }
- bool fitsOnLineExcludingTrailingCollapsedWhitespace() const { return currentWidth() - m_trailingCollapsedWhitespaceWidth <= m_availableWidth; }
-
-private:
- RenderBlock* m_block;
- float m_uncommittedWidth;
- float m_committedWidth;
- float m_overhangWidth; // The amount by which |m_availableWidth| has been inflated to account for possible contraction due to ruby overhang.
- float m_trailingWhitespaceWidth;
- float m_trailingCollapsedWhitespaceWidth;
- float m_left;
- float m_right;
- float m_availableWidth;
-#if ENABLE(CSS_SHAPES)
- const LineSegment* m_segment;
-#endif
- bool m_isFirstLine;
- IndentTextOrNot m_shouldIndentText;
-};
-
-inline void LineWidth::updateAvailableWidth(LayoutUnit replacedHeight)
-{
- LayoutUnit height = m_block->logicalHeight();
- LayoutUnit logicalHeight = logicalHeightForLine(m_block, m_isFirstLine, replacedHeight);
- m_left = m_block->logicalLeftOffsetForLine(height, shouldIndentText(), logicalHeight);
- m_right = m_block->logicalRightOffsetForLine(height, shouldIndentText(), logicalHeight);
-
-#if ENABLE(CSS_SHAPES)
- if (m_segment) {
- m_left = max<float>(m_segment->logicalLeft, m_left);
- m_right = min<float>(m_segment->logicalRight, m_right);
- }
-#endif
-
- computeAvailableWidthFromLeftAndRight();
-}
-
-inline void LineWidth::shrinkAvailableWidthForNewFloatIfNeeded(RenderBlock::FloatingObject* newFloat)
-{
- LayoutUnit height = m_block->logicalHeight();
- if (height < m_block->logicalTopForFloat(newFloat) || height >= m_block->logicalBottomForFloat(newFloat))
- return;
-
-#if ENABLE(CSS_SHAPES)
- // When floats with shape outside are stacked, the floats are positioned based on the margin box of the float,
- // not the shape's contour. Since we computed the width based on the shape contour when we added the float,
- // when we add a subsequent float on the same line, we need to undo the shape delta in order to position
- // based on the margin box. In order to do this, we need to walk back through the floating object list to find
- // the first previous float that is on the same side as our newFloat.
- ShapeOutsideInfo* previousShapeOutsideInfo = 0;
- const RenderBlock::FloatingObjectSet& floatingObjectSet = m_block->m_floatingObjects->set();
- RenderBlock::FloatingObjectSetIterator it = floatingObjectSet.end();
- RenderBlock::FloatingObjectSetIterator begin = floatingObjectSet.begin();
- for (--it; it != begin; --it) {
- RenderBlock::FloatingObject* previousFloat = *it;
- if (previousFloat != newFloat && previousFloat->type() == newFloat->type()) {
- previousShapeOutsideInfo = previousFloat->renderer()->shapeOutsideInfo();
- if (previousShapeOutsideInfo) {
- previousShapeOutsideInfo->computeSegmentsForContainingBlockLine(m_block->logicalHeight(), m_block->logicalTopForFloat(previousFloat), logicalHeightForLine(m_block, m_isFirstLine));
- }
- break;
- }
- }
-
- ShapeOutsideInfo* shapeOutsideInfo = newFloat->renderer()->shapeOutsideInfo();
- if (shapeOutsideInfo) {
- shapeOutsideInfo->computeSegmentsForContainingBlockLine(m_block->logicalHeight(), m_block->logicalTopForFloat(newFloat), logicalHeightForLine(m_block, m_isFirstLine));
- }
-#endif
-
- if (newFloat->type() == RenderBlock::FloatingObject::FloatLeft) {
- float newLeft = m_block->logicalRightForFloat(newFloat);
-#if ENABLE(CSS_SHAPES)
- if (previousShapeOutsideInfo)
- newLeft -= previousShapeOutsideInfo->rightSegmentMarginBoxDelta();
- if (shapeOutsideInfo)
- newLeft += shapeOutsideInfo->rightSegmentMarginBoxDelta();
-#endif
-
- if (shouldIndentText() && m_block->style()->isLeftToRightDirection())
- newLeft += floorToInt(m_block->textIndentOffset());
- m_left = max<float>(m_left, newLeft);
- } else {
- float newRight = m_block->logicalLeftForFloat(newFloat);
-#if ENABLE(CSS_SHAPES)
- if (previousShapeOutsideInfo)
- newRight -= previousShapeOutsideInfo->leftSegmentMarginBoxDelta();
- if (shapeOutsideInfo)
- newRight += shapeOutsideInfo->leftSegmentMarginBoxDelta();
-#endif
-
- if (shouldIndentText() && !m_block->style()->isLeftToRightDirection())
- newRight -= floorToInt(m_block->textIndentOffset());
- m_right = min<float>(m_right, newRight);
- }
-
- computeAvailableWidthFromLeftAndRight();
-}
-
-void LineWidth::applyOverhang(RenderRubyRun* rubyRun, RenderObject* startRenderer, RenderObject* endRenderer)
-{
- int startOverhang;
- int endOverhang;
- rubyRun->getOverhang(m_isFirstLine, startRenderer, endRenderer, startOverhang, endOverhang);
-
- startOverhang = min<int>(startOverhang, m_committedWidth);
- m_availableWidth += startOverhang;
-
- endOverhang = max(min<int>(endOverhang, m_availableWidth - currentWidth()), 0);
- m_availableWidth += endOverhang;
- m_overhangWidth += startOverhang + endOverhang;
-}
-
-void LineWidth::fitBelowFloats()
-{
- ASSERT(!m_committedWidth);
- ASSERT(!fitsOnLine());
-
- LayoutUnit floatLogicalBottom;
- LayoutUnit lastFloatLogicalBottom = m_block->logicalHeight();
- float newLineWidth = m_availableWidth;
- float newLineLeft = m_left;
- float newLineRight = m_right;
- while (true) {
- floatLogicalBottom = m_block->nextFloatLogicalBottomBelow(lastFloatLogicalBottom);
- if (floatLogicalBottom <= lastFloatLogicalBottom)
- break;
-
- newLineLeft = m_block->logicalLeftOffsetForLine(floatLogicalBottom, shouldIndentText());
- newLineRight = m_block->logicalRightOffsetForLine(floatLogicalBottom, shouldIndentText());
- newLineWidth = max(0.0f, newLineRight - newLineLeft);
- lastFloatLogicalBottom = floatLogicalBottom;
- if (newLineWidth >= m_uncommittedWidth)
- break;
- }
-
- if (newLineWidth > m_availableWidth) {
- m_block->setLogicalHeight(lastFloatLogicalBottom);
- m_availableWidth = newLineWidth + m_overhangWidth;
- m_left = newLineLeft;
- m_right = newLineRight;
- }
-}
-
-class LineInfo {
-public:
- LineInfo()
- : m_isFirstLine(true)
- , m_isLastLine(false)
- , m_isEmpty(true)
- , m_previousLineBrokeCleanly(true)
- , m_floatPaginationStrut(0)
- , m_runsFromLeadingWhitespace(0)
- { }
-
- bool isFirstLine() const { return m_isFirstLine; }
- bool isLastLine() const { return m_isLastLine; }
- bool isEmpty() const { return m_isEmpty; }
- bool previousLineBrokeCleanly() const { return m_previousLineBrokeCleanly; }
- LayoutUnit floatPaginationStrut() const { return m_floatPaginationStrut; }
- unsigned runsFromLeadingWhitespace() const { return m_runsFromLeadingWhitespace; }
- void resetRunsFromLeadingWhitespace() { m_runsFromLeadingWhitespace = 0; }
- void incrementRunsFromLeadingWhitespace() { m_runsFromLeadingWhitespace++; }
-
- void setFirstLine(bool firstLine) { m_isFirstLine = firstLine; }
- void setLastLine(bool lastLine) { m_isLastLine = lastLine; }
- void setEmpty(bool empty, RenderBlock* block = 0, LineWidth* lineWidth = 0)
- {
- if (m_isEmpty == empty)
- return;
- m_isEmpty = empty;
- if (!empty && block && floatPaginationStrut()) {
- block->setLogicalHeight(block->logicalHeight() + floatPaginationStrut());
- setFloatPaginationStrut(0);
- lineWidth->updateAvailableWidth();
- }
- }
-
- void setPreviousLineBrokeCleanly(bool previousLineBrokeCleanly) { m_previousLineBrokeCleanly = previousLineBrokeCleanly; }
- void setFloatPaginationStrut(LayoutUnit strut) { m_floatPaginationStrut = strut; }
-
-private:
- bool m_isFirstLine;
- bool m_isLastLine;
- bool m_isEmpty;
- bool m_previousLineBrokeCleanly;
- LayoutUnit m_floatPaginationStrut;
- unsigned m_runsFromLeadingWhitespace;
-};
-
-static inline LayoutUnit borderPaddingMarginStart(RenderInline* child)
-{
- return child->marginStart() + child->paddingStart() + child->borderStart();
-}
-
-static inline LayoutUnit borderPaddingMarginEnd(RenderInline* child)
-{
- return child->marginEnd() + child->paddingEnd() + child->borderEnd();
-}
-
-static inline bool shouldAddBorderPaddingMargin(RenderObject* child)
-{
- // When deciding whether we're at the edge of an inline, adjacent collapsed whitespace is the same as no sibling at all.
- return !child || (child->isText() && !toRenderText(child)->textLength());
-}
-
-static RenderObject* previousInFlowSibling(RenderObject* child)
-{
- child = child->previousSibling();
- while (child && child->isOutOfFlowPositioned())
- child = child->previousSibling();
- return child;
-}
-
-static LayoutUnit inlineLogicalWidth(RenderObject* child, bool checkStartEdge = true, bool checkEndEdge = true)
-{
- unsigned lineDepth = 1;
- LayoutUnit extraWidth = 0;
- RenderObject* parent = child->parent();
- while (parent->isRenderInline() && lineDepth++ < cMaxLineDepth) {
- RenderInline* parentAsRenderInline = toRenderInline(parent);
- if (!isEmptyInline(parentAsRenderInline)) {
- checkStartEdge = checkStartEdge && shouldAddBorderPaddingMargin(previousInFlowSibling(child));
- if (checkStartEdge)
- extraWidth += borderPaddingMarginStart(parentAsRenderInline);
- checkEndEdge = checkEndEdge && shouldAddBorderPaddingMargin(child->nextSibling());
- if (checkEndEdge)
- extraWidth += borderPaddingMarginEnd(parentAsRenderInline);
- if (!checkStartEdge && !checkEndEdge)
- return extraWidth;
- }
- child = parent;
- parent = child->parent();
- }
- return extraWidth;
-}
-
static void determineDirectionality(TextDirection& dir, InlineIterator iter)
{
while (!iter.atEnd()) {
if (iter.atParagraphSeparator())
return;
if (UChar current = iter.current()) {
- Direction charDirection = direction(current);
- if (charDirection == LeftToRight) {
+ UCharDirection charDirection = u_charDirection(current);
+ if (charDirection == U_LEFT_TO_RIGHT) {
dir = LTR;
return;
}
- if (charDirection == RightToLeft || charDirection == RightToLeftArabic) {
+ if (charDirection == U_RIGHT_TO_LEFT || charDirection == U_RIGHT_TO_LEFT_ARABIC) {
dir = RTL;
return;
}
@@ -408,149 +71,107 @@ static void determineDirectionality(TextDirection& dir, InlineIterator iter)
}
}
-static void checkMidpoints(LineMidpointState& lineMidpointState, InlineIterator& lBreak)
+inline BidiRun* createRun(int start, int end, RenderObject& obj, InlineBidiResolver& resolver)
{
- // Check to see if our last midpoint is a start point beyond the line break. If so,
- // shave it off the list, and shave off a trailing space if the previous end point doesn't
- // preserve whitespace.
- if (lBreak.m_obj && lineMidpointState.numMidpoints && !(lineMidpointState.numMidpoints % 2)) {
- InlineIterator* midpoints = lineMidpointState.midpoints.data();
- InlineIterator& endpoint = midpoints[lineMidpointState.numMidpoints - 2];
- const InlineIterator& startpoint = midpoints[lineMidpointState.numMidpoints - 1];
- InlineIterator currpoint = endpoint;
- while (!currpoint.atEnd() && currpoint != startpoint && currpoint != lBreak)
- currpoint.increment();
- if (currpoint == lBreak) {
- // We hit the line break before the start point. Shave off the start point.
- lineMidpointState.numMidpoints--;
- if (endpoint.m_obj->style()->collapseWhiteSpace() && endpoint.m_obj->isText())
- endpoint.m_pos--;
- }
- }
-}
-
-// Don't call this directly. Use one of the descriptive helper functions below.
-static void deprecatedAddMidpoint(LineMidpointState& lineMidpointState, const InlineIterator& midpoint)
-{
- if (lineMidpointState.midpoints.size() <= lineMidpointState.numMidpoints)
- lineMidpointState.midpoints.grow(lineMidpointState.numMidpoints + 10);
-
- InlineIterator* midpoints = lineMidpointState.midpoints.data();
- midpoints[lineMidpointState.numMidpoints++] = midpoint;
-}
-
-static inline void startIgnoringSpaces(LineMidpointState& lineMidpointState, const InlineIterator& midpoint)
-{
- ASSERT(!(lineMidpointState.numMidpoints % 2));
- deprecatedAddMidpoint(lineMidpointState, midpoint);
-}
-
-static inline void stopIgnoringSpaces(LineMidpointState& lineMidpointState, const InlineIterator& midpoint)
-{
- ASSERT(lineMidpointState.numMidpoints % 2);
- deprecatedAddMidpoint(lineMidpointState, midpoint);
+ return new BidiRun(start, end, obj, resolver.context(), resolver.dir());
}
-// When ignoring spaces, this needs to be called for objects that need line boxes such as RenderInlines or
-// hard line breaks to ensure that they're not ignored.
-static inline void ensureLineBoxInsideIgnoredSpaces(LineMidpointState& lineMidpointState, RenderObject* renderer)
-{
- InlineIterator midpoint(0, renderer, 0);
- stopIgnoringSpaces(lineMidpointState, midpoint);
- startIgnoringSpaces(lineMidpointState, midpoint);
-}
-
-// Adding a pair of midpoints before a character will split it out into a new line box.
-static inline void ensureCharacterGetsLineBox(LineMidpointState& lineMidpointState, InlineIterator& textParagraphSeparator)
-{
- InlineIterator midpoint(0, textParagraphSeparator.m_obj, textParagraphSeparator.m_pos);
- startIgnoringSpaces(lineMidpointState, InlineIterator(0, textParagraphSeparator.m_obj, textParagraphSeparator.m_pos - 1));
- stopIgnoringSpaces(lineMidpointState, InlineIterator(0, textParagraphSeparator.m_obj, textParagraphSeparator.m_pos));
-}
-
-static inline BidiRun* createRun(int start, int end, RenderObject* obj, InlineBidiResolver& resolver)
-{
- return new (obj->renderArena()) BidiRun(start, end, obj, resolver.context(), resolver.dir());
-}
-
-void RenderBlock::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);
+ bool haveNextMidpoint = (lineMidpointState.currentMidpoint() < lineMidpointState.numMidpoints());
InlineIterator nextMidpoint;
if (haveNextMidpoint)
- nextMidpoint = lineMidpointState.midpoints[lineMidpointState.currentMidpoint];
- if (lineMidpointState.betweenMidpoints) {
- if (!(haveNextMidpoint && nextMidpoint.m_obj == obj))
+ nextMidpoint = lineMidpointState.midpoints()[lineMidpointState.currentMidpoint()];
+ if (lineMidpointState.betweenMidpoints()) {
+ if (!haveNextMidpoint || (&obj != nextMidpoint.renderer()))
return;
// This is a new start point. Stop ignoring objects and
// adjust our start.
- lineMidpointState.betweenMidpoints = false;
- start = nextMidpoint.m_pos;
- lineMidpointState.currentMidpoint++;
- if (start < end)
- return appendRunsForObject(runs, start, end, obj, resolver);
+ start = nextMidpoint.offset();
+ lineMidpointState.incrementCurrentMidpoint();
+ if (start < end) {
+ appendRunsForObject(runs, start, end, obj, resolver);
+ return;
+ }
} else {
- if (!haveNextMidpoint || (obj != nextMidpoint.m_obj)) {
- runs.addRun(createRun(start, end, obj, resolver));
+ if (!haveNextMidpoint || (&obj != nextMidpoint.renderer())) {
+ if (runs)
+ runs->addRun(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.m_pos + 1) <= end) {
- lineMidpointState.betweenMidpoints = true;
- lineMidpointState.currentMidpoint++;
- if (nextMidpoint.m_pos != UINT_MAX) { // UINT_MAX means stop at the object and don't include any of it.
- if (static_cast<int>(nextMidpoint.m_pos + 1) > start)
- runs.addRun(createRun(start, nextMidpoint.m_pos + 1, obj, resolver));
- return appendRunsForObject(runs, nextMidpoint.m_pos + 1, end, obj, resolver);
- }
- } else
- runs.addRun(createRun(start, end, obj, resolver));
+ // An end midpoint has been encountered within our object. We need to append a run with our endpoint.
+ if (static_cast<int>(nextMidpoint.offset() + 1) <= end) {
+ lineMidpointState.incrementCurrentMidpoint();
+ // The end of the line is before the object we're inspecting. Skip everything and return
+ if (nextMidpoint.refersToEndOfPreviousNode())
+ return;
+ if (static_cast<int>(nextMidpoint.offset() + 1) > start && runs)
+ runs->addRun(createRun(start, nextMidpoint.offset() + 1, obj, resolver));
+ appendRunsForObject(runs, nextMidpoint.offset() + 1, end, obj, resolver);
+ } else if (runs)
+ runs->addRun(createRun(start, end, obj, resolver));
}
}
-static inline InlineBox* createInlineBoxForRenderer(RenderObject* obj, bool isRootLineBox, bool isOnlyRun = false)
+std::unique_ptr<RootInlineBox> RenderBlockFlow::createRootInlineBox()
{
- if (isRootLineBox)
- return toRenderBlock(obj)->createAndAppendRootInlineBox();
+ return std::make_unique<RootInlineBox>(*this);
+}
- if (obj->isText()) {
- InlineTextBox* textBox = toRenderText(obj)->createInlineTextBox();
- // 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.)
- if (obj->isBR())
- textBox->setIsText(isOnlyRun || obj->document()->inNoQuirksMode());
- return textBox;
- }
+RootInlineBox* RenderBlockFlow::createAndAppendRootInlineBox()
+{
+ auto newRootBox = createRootInlineBox();
+ RootInlineBox* rootBox = newRootBox.get();
+ m_lineBoxes.appendLineBox(WTFMove(newRootBox));
- if (obj->isBox())
- return toRenderBox(obj)->createInlineBox();
+ if (UNLIKELY(AXObjectCache::accessibilityEnabled()) && firstRootBox() == rootBox) {
+ if (AXObjectCache* cache = document().existingAXObjectCache())
+ cache->recomputeIsIgnored(this);
+ }
- return toRenderInline(obj)->createAndAppendInlineFlowBox();
+ return rootBox;
}
-// FIXME: Don't let counters mark themselves as needing pref width recalcs during layout
-// so we don't need this hack.
-static inline void updateCounterIfNeeded(RenderText* o)
+static inline InlineBox* createInlineBoxForRenderer(RenderObject* renderer, bool isRootLineBox, bool isOnlyRun = false)
{
- if (!o->preferredLogicalWidthsDirty() || !o->isCounter())
- return;
- toRenderCounter(o)->updateCounter();
+ if (isRootLineBox)
+ return downcast<RenderBlockFlow>(*renderer).createAndAppendRootInlineBox();
+
+ if (is<RenderText>(*renderer))
+ return downcast<RenderText>(*renderer).createInlineTextBox();
+
+ if (is<RenderBox>(*renderer)) {
+ // FIXME: This is terrible. This branch returns an *owned* pointer!
+ return downcast<RenderBox>(*renderer).createInlineBox().release();
+ }
+
+ if (is<RenderLineBreak>(*renderer)) {
+ // FIXME: This is terrible. This branch returns an *owned* pointer!
+ 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 || renderer->document().inNoQuirksMode() || renderer->isLineBreakOpportunity());
+ return inlineBox;
+ }
+
+ return downcast<RenderInline>(*renderer).createAndAppendInlineFlowBox();
}
-static inline void dirtyLineBoxesForRenderer(RenderObject* o, bool fullLayout)
+static inline void dirtyLineBoxesForRenderer(RenderObject& renderer, bool fullLayout)
{
- if (o->isText()) {
- RenderText* renderText = toRenderText(o);
+ if (is<RenderText>(renderer)) {
+ RenderText& renderText = downcast<RenderText>(renderer);
updateCounterIfNeeded(renderText);
- renderText->dirtyLineBoxes(fullLayout);
- } else
- toRenderInline(o)->dirtyLineBoxes(fullLayout);
+ renderText.dirtyLineBoxes(fullLayout);
+ } else if (is<RenderLineBreak>(renderer))
+ downcast<RenderLineBreak>(renderer).dirtyLineBoxes(fullLayout);
+ else
+ downcast<RenderInline>(renderer).dirtyLineBoxes(fullLayout);
}
static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox)
@@ -563,21 +184,21 @@ static bool parentIsConstructedOrHaveNext(InlineFlowBox* parentBox)
return false;
}
-InlineFlowBox* RenderBlock::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;
- bool hasDefaultLineBoxContain = style()->lineBoxContain() == RenderStyle::initialLineBoxContain();
+ 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() : toRenderBlock(obj)->lastLineBox();
+ 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
@@ -586,15 +207,13 @@ InlineFlowBox* RenderBlock::createLineBoxes(RenderObject* obj, const LineInfo& l
// 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->setFirstLineStyleBit(lineInfo.isFirstLine());
+ parentBox = downcast<InlineFlowBox>(newBox);
+ parentBox->setIsFirstLine(lineInfo.isFirstLine());
parentBox->setIsHorizontal(isHorizontalWritingMode());
if (!hasDefaultLineBoxContain)
parentBox->clearDescendantsHaveSameLineHeightAndBaseline();
@@ -643,105 +262,130 @@ static bool reachedEndOfTextRenderer(const BidiRunList<BidiRun>& bidiRuns)
if (!run)
return true;
unsigned pos = run->stop();
- RenderObject* r = run->m_object;
- if (!r->isText() || r->isBR())
+ const RenderObject& renderer = run->renderer();
+ if (!is<RenderText>(renderer))
return false;
- RenderText* renderText = toRenderText(r);
- unsigned length = renderText->textLength();
+ const RenderText& renderText = downcast<RenderText>(renderer);
+ unsigned length = renderText.textLength();
if (pos >= length)
return true;
- if (renderText->is8Bit())
- return endsWithASCIISpaces(renderText->characters8(), pos, length);
- return endsWithASCIISpaces(renderText->characters16(), pos, length);
+ if (renderText.is8Bit())
+ return endsWithASCIISpaces(renderText.characters8(), pos, length);
+ return endsWithASCIISpaces(renderText.characters16(), pos, length);
}
-RootInlineBox* RenderBlock::constructLine(BidiRunList<BidiRun>& bidiRuns, const LineInfo& lineInfo)
+RootInlineBox* RenderBlockFlow::constructLine(BidiRunList<BidiRun>& bidiRuns, const LineInfo& lineInfo)
{
ASSERT(bidiRuns.firstRun());
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);
- if (runCount == 2 && !r->m_object->isListMarker())
- isOnlyRun = (!style()->isLeftToRightDirection() ? bidiRuns.lastRun() : bidiRuns.firstRun())->m_object->isListMarker();
+ if (runCount == 2 && !r->renderer().isListMarker())
+ isOnlyRun = (!style().isLeftToRightDirection() ? bidiRuns.lastRun() : bidiRuns.firstRun())->renderer().isListMarker();
if (lineInfo.isEmpty())
continue;
- InlineBox* box = createInlineBoxForRenderer(r->m_object, false, isOnlyRun);
- r->m_box = box;
-
- ASSERT(box);
- if (!box)
- continue;
+ InlineBox* box = createInlineBoxForRenderer(&r->renderer(), false, isOnlyRun);
+ r->setBox(box);
- if (!rootHasSelectedChildren && box->renderer()->selectionState() != RenderObject::SelectionNone)
+ 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->m_object->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->m_object->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);
}
- bool visuallyOrdered = r->m_object->style()->rtlOrdering() == VisualOrder;
+ 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);
}
}
// We should have a root inline box. It should be unconstructed and
// be the last continuation of our line list.
- ASSERT(lastLineBox() && !lastLineBox()->isConstructed());
+ ASSERT(lastRootBox() && !lastRootBox()->isConstructed());
// Set the m_selectedChildren flag on the root inline box if one of the leaf inline box
// from the bidi runs walk above has a selection state.
if (rootHasSelectedChildren)
- lastLineBox()->root()->setHasSelectedChildren(true);
+ lastRootBox()->root().setHasSelectedChildren(true);
// Set bits on our inline flow boxes that indicate which sides should
// 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()->m_object && bidiRuns.logicallyLastRun()->m_object->isText() ? !reachedEndOfTextRenderer(bidiRuns) : true;
- lastLineBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogicallyLastRunWrapped, bidiRuns.logicallyLastRun()->m_object);
+ bool isLogicallyLastRunWrapped = bidiRuns.logicallyLastRun()->renderer().isText() ? !reachedEndOfTextRenderer(bidiRuns) : true;
+ lastRootBox()->determineSpacingForFlowBoxes(lineInfo.isLastLine(), isLogicallyLastRunWrapped, &bidiRuns.logicallyLastRun()->renderer());
// Now mark the line boxes as being constructed.
- lastLineBox()->setConstructed();
+ lastRootBox()->setConstructed();
// Return the last line.
return lastRootBox();
}
-ETextAlign RenderBlock::textAlignmentForLine(bool endsWithSoftBreak) const
+ETextAlign RenderBlockFlow::textAlignmentForLine(bool endsWithSoftBreak) const
{
- ETextAlign alignment = style()->textAlign();
- if (!endsWithSoftBreak && alignment == JUSTIFY)
- alignment = TASTART;
+ 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;
+#if !ENABLE(CSS3_TEXT)
+ return (alignment == JUSTIFY) ? TASTART : alignment;
+#else
+ if (alignment != JUSTIFY)
+ return alignment;
+
+ TextAlignLast alignmentLast = style().textAlignLast();
+ switch (alignmentLast) {
+ case TextAlignLastStart:
+ return TASTART;
+ case TextAlignLastEnd:
+ return TAEND;
+ case TextAlignLastLeft:
+ return LEFT;
+ case TextAlignLastRight:
+ return RIGHT;
+ case TextAlignLastCenter:
+ return CENTER;
+ case TextAlignLastJustify:
+ return JUSTIFY;
+ case TextAlignLastAuto:
+ if (textJustify == TextJustifyDistribute)
+ return JUSTIFY;
+ return TASTART;
+ }
return alignment;
+#endif
}
static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, BidiRun* trailingSpaceRun, float& logicalLeft, float& totalLogicalWidth, float availableLogicalWidth)
@@ -750,12 +394,12 @@ static void updateLogicalWidthForLeftAlignedBlock(bool isLeftToRightDirection, B
// In particular with RTL blocks, wide lines should still spill out to the left.
if (isLeftToRightDirection) {
if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun)
- trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
+ trailingSpaceRun->box()->setLogicalWidth(std::max<float>(0, trailingSpaceRun->box()->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
return;
}
if (trailingSpaceRun)
- trailingSpaceRun->m_box->setLogicalWidth(0);
+ trailingSpaceRun->box()->setLogicalWidth(0);
else if (totalLogicalWidth > availableLogicalWidth)
logicalLeft -= (totalLogicalWidth - availableLogicalWidth);
}
@@ -767,17 +411,16 @@ static void updateLogicalWidthForRightAlignedBlock(bool isLeftToRightDirection,
// side of the block.
if (isLeftToRightDirection) {
if (trailingSpaceRun) {
- totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
- trailingSpaceRun->m_box->setLogicalWidth(0);
+ totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth();
+ trailingSpaceRun->box()->setLogicalWidth(0);
}
- if (totalLogicalWidth < availableLogicalWidth)
- logicalLeft += availableLogicalWidth - totalLogicalWidth;
+ logicalLeft += std::max(0.f, availableLogicalWidth - totalLogicalWidth);
return;
}
if (totalLogicalWidth > availableLogicalWidth && trailingSpaceRun) {
- trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceRun->m_box->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
- totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
+ trailingSpaceRun->box()->setLogicalWidth(std::max<float>(0, trailingSpaceRun->box()->logicalWidth() - totalLogicalWidth + availableLogicalWidth));
+ totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth();
} else
logicalLeft += availableLogicalWidth - totalLogicalWidth;
}
@@ -786,68 +429,45 @@ static void updateLogicalWidthForCenterAlignedBlock(bool isLeftToRightDirection,
{
float trailingSpaceWidth = 0;
if (trailingSpaceRun) {
- totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
- trailingSpaceWidth = min(trailingSpaceRun->m_box->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2);
- trailingSpaceRun->m_box->setLogicalWidth(max<float>(0, trailingSpaceWidth));
+ totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth();
+ trailingSpaceWidth = std::min(trailingSpaceRun->box()->logicalWidth(), (availableLogicalWidth - totalLogicalWidth + 1) / 2);
+ trailingSpaceRun->box()->setLogicalWidth(std::max<float>(0, trailingSpaceWidth));
}
if (isLeftToRightDirection)
- logicalLeft += max<float>((availableLogicalWidth - totalLogicalWidth) / 2, 0);
+ logicalLeft += std::max<float>((availableLogicalWidth - totalLogicalWidth) / 2, 0);
else
logicalLeft += totalLogicalWidth > availableLogicalWidth ? (availableLogicalWidth - totalLogicalWidth) : (availableLogicalWidth - totalLogicalWidth) / 2 - trailingSpaceWidth;
}
-void RenderBlock::setMarginsForRubyRun(BidiRun* run, RenderRubyRun* renderer, RenderObject* previousObject, const LineInfo& lineInfo)
+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->m_object->isOutOfFlowPositioned() && !runWithNextObject->m_box->isLineBreak()) {
- nextObject = runWithNextObject->m_object;
+ if (!runWithNextObject->renderer().isOutOfFlowPositioned() && !runWithNextObject->box()->isLineBreak()) {
+ nextObject = &runWithNextObject->renderer();
break;
}
}
- renderer->getOverhang(lineInfo.isFirstLine(), renderer->style()->isLeftToRightDirection() ? previousObject : nextObject, renderer->style()->isLeftToRightDirection() ? nextObject : previousObject, startOverhang, endOverhang);
+ renderer.getOverhang(lineInfo.isFirstLine(), renderer.style().isLeftToRightDirection() ? previousObject : nextObject, renderer.style().isLeftToRightDirection() ? nextObject : previousObject, startOverhang, endOverhang);
setMarginStartForChild(renderer, -startOverhang);
setMarginEndForChild(renderer, -endOverhang);
}
-static inline float measureHyphenWidth(RenderText* renderer, const Font& font, HashSet<const SimpleFontData*>* fallbackFonts = 0)
-{
- RenderStyle* style = renderer->style();
- return font.width(RenderBlock::constructTextRun(renderer, font, style->hyphenString().string(), style), fallbackFonts);
-}
-
-class WordMeasurement {
-public:
- WordMeasurement()
- : renderer(0)
- , width(0)
- , startOffset(0)
- , endOffset(0)
- {
- }
-
- RenderText* renderer;
- float width;
- int startOffset;
- int endOffset;
- HashSet<const SimpleFontData*> fallbackFonts;
-};
-
-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 = renderer->style(lineInfo.isFirstLine())->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
// will keep us from computing glyph bounds in nearly all cases.
bool includeRootLine = lineBox->includesRootLineBoxFontOrLeading();
- int baselineShift = lineBox->verticalPositionForBox(run->m_box, verticalPositionCache);
+ int baselineShift = lineBox->verticalPositionForBox(run->box(), verticalPositionCache);
int rootDescent = includeRootLine ? font.fontMetrics().descent() : 0;
int rootAscent = includeRootLine ? font.fontMetrics().ascent() : 0;
int boxAscent = font.fontMetrics().ascent() - baselineShift;
@@ -857,14 +477,13 @@ static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* ru
}
LayoutUnit hyphenWidth = 0;
- if (toInlineTextBox(run->m_box)->hasHyphen()) {
- const Font& font = renderer->style(lineInfo.isFirstLine())->font();
+ 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.
@@ -875,23 +494,23 @@ static inline void setLogicalWidthForTextRun(RootInlineBox* lineBox, BidiRun* ru
WordMeasurement& wordMeasurement = wordMeasurements[i];
if (wordMeasurement.width <= 0 || wordMeasurement.startOffset == wordMeasurement.endOffset)
continue;
- if (wordMeasurement.renderer != renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop)
+ if (wordMeasurement.renderer != &renderer || wordMeasurement.startOffset != lastEndOffset || wordMeasurement.endOffset > run->m_stop)
continue;
lastEndOffset = wordMeasurement.endOffset;
if (kerningIsEnabled && lastEndOffset == run->m_stop) {
int wordLength = lastEndOffset - wordMeasurement.startOffset;
GlyphOverflow overflow;
- measuredWidth += renderer->width(wordMeasurement.startOffset, wordLength, xPos + measuredWidth, lineInfo.isFirstLine(),
+ measuredWidth += renderer.width(wordMeasurement.startOffset, wordLength, xPos + measuredWidth, lineInfo.isFirstLine(),
&wordMeasurement.fallbackFonts, &overflow);
- UChar c = renderer->characterAt(wordMeasurement.startOffset);
+ UChar c = renderer.characterAt(wordMeasurement.startOffset);
if (i > 0 && wordLength == 1 && (c == ' ' || c == '\t'))
- measuredWidth += renderer->style()->wordSpacing();
+ measuredWidth += renderer.style().fontCascade().wordSpacing();
} else
measuredWidth += wordMeasurement.width;
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);
}
}
@@ -903,60 +522,114 @@ 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->m_box->setLogicalWidth(measuredWidth + hyphenWidth);
+ run->box()->setLogicalWidth(measuredWidth + hyphenWidth);
if (!fallbackFonts.isEmpty()) {
- ASSERT(run->m_box->isText());
- GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator;
+ ASSERT(run->box()->behavesLikeText());
+ 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->m_box->parent()->clearDescendantsHaveSameLineHeightAndBaseline();
+ run->box()->parent()->clearDescendantsHaveSameLineHeightAndBaseline();
}
- if ((glyphOverflow.top || glyphOverflow.bottom || glyphOverflow.left || glyphOverflow.right)) {
- ASSERT(run->m_box->isText());
- GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.add(toInlineTextBox(run->m_box), make_pair(Vector<const SimpleFontData*>(), GlyphOverflow())).iterator;
+
+ // 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(downcast<InlineTextBox>(run->box()), std::make_pair(Vector<const Font*>(), GlyphOverflow())).iterator;
it->value.second = glyphOverflow;
- run->m_box->clearKnownToHaveNoOverflow();
+ 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());
+
+ // This relayout caused the size of the RenderRubyText and the RenderRubyBase to change, dependent on the line's current expansion. Next time we relayout the
+ // RenderRubyRun, make sure that we relayout the RenderRubyBase and RenderRubyText as well.
+ rubyBase.setNeedsLayout(MarkOnlyThis);
+ if (RenderRubyText* rubyText = rubyRun.rubyText())
+ rubyText->setNeedsLayout(MarkOnlyThis);
+
+ totalLogicalWidth += totalExpansion;
+ expansionOpportunityCount -= totalOpportunitiesInRun;
+}
+
+void RenderBlockFlow::computeExpansionForJustifiedText(BidiRun* firstRun, BidiRun* trailingSpaceRun, const Vector<unsigned, 16>& expansionOpportunities, unsigned expansionOpportunityCount, float totalLogicalWidth, float availableLogicalWidth)
{
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->m_box || r == trailingSpaceRun)
+ for (BidiRun* run = firstRun; run; run = run->next()) {
+ if (!run->box() || run == trailingSpaceRun)
continue;
- if (r->m_object->isText()) {
+ if (is<RenderText>(run->renderer())) {
unsigned opportunitiesInRun = expansionOpportunities[i++];
ASSERT(opportunitiesInRun <= expansionOpportunityCount);
// Only justify text if whitespace is collapsed.
- if (r->m_object->style()->collapseWhiteSpace()) {
- InlineTextBox* textBox = toInlineTextBox(r->m_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 RenderBlock::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
@@ -964,69 +637,56 @@ void RenderBlock::updateLogicalWidthForAlignment(const ETextAlign& textAlign, Bi
switch (textAlign) {
case LEFT:
case WEBKIT_LEFT:
- updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
+ updateLogicalWidthForLeftAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
break;
case RIGHT:
case WEBKIT_RIGHT:
- updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
+ updateLogicalWidthForRightAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
break;
case CENTER:
case WEBKIT_CENTER:
- updateLogicalWidthForCenterAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
+ updateLogicalWidthForCenterAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
break;
case JUSTIFY:
adjustInlineDirectionLineBounds(expansionOpportunityCount, logicalLeft, availableLogicalWidth);
if (expansionOpportunityCount) {
if (trailingSpaceRun) {
- totalLogicalWidth -= trailingSpaceRun->m_box->logicalWidth();
- trailingSpaceRun->m_box->setLogicalWidth(0);
+ totalLogicalWidth -= trailingSpaceRun->box()->logicalWidth();
+ trailingSpaceRun->box()->setLogicalWidth(0);
}
break;
}
- // Fall through
+ FALLTHROUGH;
case TASTART:
- if (style()->isLeftToRightDirection())
- updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
+ if (direction == LTR)
+ updateLogicalWidthForLeftAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
else
- updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
+ updateLogicalWidthForRightAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
break;
case TAEND:
- if (style()->isLeftToRightDirection())
- updateLogicalWidthForRightAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
+ if (direction == LTR)
+ updateLogicalWidthForRightAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
else
- updateLogicalWidthForLeftAlignedBlock(style()->isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
+ updateLogicalWidthForLeftAlignedBlock(style().isLeftToRightDirection(), trailingSpaceRun, logicalLeft, totalLogicalWidth, availableLogicalWidth);
break;
}
}
-static IndentTextOrNot requiresIndent(bool isFirstLine, bool isAfterHardLineBreak, RenderStyle* style)
-{
- IndentTextOrNot shouldIndentText = DoNotIndentText;
- if (isFirstLine)
- shouldIndentText = IndentText;
-#if ENABLE(CSS3_TEXT)
- else if (isAfterHardLineBreak && style->textIndentLine() == TextIndentEachLine)
- shouldIndentText = IndentText;
-
- if (style->textIndentType() == TextIndentHanging)
- shouldIndentText = shouldIndentText == IndentText ? DoNotIndentText : IndentText;
-#else
- UNUSED_PARAM(isAfterHardLineBreak);
- UNUSED_PARAM(style);
-#endif
- return shouldIndentText;
-}
-
-static void updateLogicalInlinePositions(RenderBlock* 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 = logicalHeightForLine(block, firstLine, boxLogicalHeight);
- lineLogicalLeft = block->pixelSnappedLogicalLeftOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight);
- lineLogicalRight = block->pixelSnappedLogicalRightOffsetForLine(block->logicalHeight(), shouldIndentText == IndentText, lineLogicalHeight);
+ LayoutUnit lineLogicalHeight = block.minLineHeightForReplacedRenderer(firstLine, boxLogicalHeight);
+ if (rootBox->hasAnonymousInlineBlock()) {
+ lineLogicalLeft = block.logicalLeftOffsetForContent(block.logicalHeight());
+ lineLogicalRight = block.logicalRightOffsetForContent(block.logicalHeight());
+ } else {
+ lineLogicalLeft = block.logicalLeftOffsetForLine(block.logicalHeight(), shouldIndentText, lineLogicalHeight);
+ lineLogicalRight = block.logicalRightOffsetForLine(block.logicalHeight(), shouldIndentText, lineLogicalHeight);
+ }
availableLogicalWidth = lineLogicalRight - lineLogicalLeft;
}
-void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd,
- GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements)
+void RenderBlockFlow::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox, const LineInfo& lineInfo, BidiRun* firstRun, BidiRun* trailingSpaceRun, bool reachedEnd, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, VerticalPositionCache& verticalPositionCache, WordMeasurements& wordMeasurements)
{
ETextAlign textAlign = textAlignmentForLine(!reachedEnd && !lineBox->endsWithBreak());
@@ -1040,109 +700,185 @@ void RenderBlock::computeInlineDirectionPositionsForLine(RootInlineBox* lineBox,
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 = max<float>(roundToInt(segments[0].logicalLeft), lineLogicalLeft);
- float logicalRight = 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 = max<float>(roundToInt(segments[i].logicalLeft), lineLogicalLeft);
- logicalRight = 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->m_box, newSegmentStart ? newSegmentStart->m_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->m_object->isReplaced()) {
- RenderBox* renderBox = toRenderBox(firstRun->m_object);
- updateLogicalInlinePositions(this, lineLogicalLeft, lineLogicalRight, availableLogicalWidth, isFirstLine, shouldIndentText, renderBox->logicalHeight());
+ if (firstRun && firstRun->renderer().isReplaced()) {
+ 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;
}
-BidiRun* RenderBlock::computeInlineDirectionPositionsForSegment(RootInlineBox* lineBox, const LineInfo& lineInfo, ETextAlign textAlign, float& logicalLeft,
+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;
+ }
+}
+
+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;
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->m_box || r->m_object->isOutOfFlowPositioned() || r->m_box->isLineBreak())
+
+ BidiRun* run = firstRun;
+ BidiRun* previousRun = nullptr;
+ for (; run; run = run->next()) {
+ 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->m_object->isText()) {
- RenderText* rt = toRenderText(r->m_object);
- if (textAlign == JUSTIFY && r != trailingSpaceRun) {
- if (!isAfterExpansion)
- toInlineTextBox(r->m_box)->setCanHaveLeadingExpansion(true);
+ }
+ if (is<RenderText>(run->renderer())) {
+ auto& renderText = downcast<RenderText>(run->renderer());
+ auto& textBox = downcast<InlineTextBox>(*run->box());
+ if (textAlign == JUSTIFY && run != trailingSpaceRun) {
+ ExpansionBehavior expansionBehavior = expansionBehaviorForInlineTextBox(*this, textBox, previousRun, run->next(), textAlign, isAfterExpansion);
+ applyExpansionBehavior(textBox, expansionBehavior);
unsigned opportunitiesInRun;
- if (rt->is8Bit())
- opportunitiesInRun = Font::expansionOpportunityCount(rt->characters8() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion);
- else
- opportunitiesInRun = Font::expansionOpportunityCount(rt->characters16() + r->m_start, r->m_stop - r->m_start, r->m_box->direction(), isAfterExpansion);
+ std::tie(opportunitiesInRun, isAfterExpansion) = FontCascade::expansionOpportunityCount(renderText.stringView(run->m_start, run->m_stop), run->box()->direction(), expansionBehavior);
expansionOpportunities.append(opportunitiesInRun);
expansionOpportunityCount += opportunitiesInRun;
}
- if (int length = rt->textLength()) {
- if (!r->m_start && needsWordSpacing && isSpaceOrNewline(rt->characterAt(r->m_start)))
- totalLogicalWidth += rt->style(lineInfo.isFirstLine())->font().wordSpacing();
- needsWordSpacing = !isSpaceOrNewline(rt->characterAt(r->m_stop - 1)) && r->m_stop == length;
+ if (int length = renderText.textLength()) {
+ if (!run->m_start && needsWordSpacing && isSpaceOrNewline(renderText.characterAt(run->m_start)))
+ totalLogicalWidth += lineStyle(*renderText.parent(), lineInfo).fontCascade().wordSpacing();
+ needsWordSpacing = !isSpaceOrNewline(renderText.characterAt(run->m_stop - 1)) && run->m_stop == length;
}
- setLogicalWidthForTextRun(lineBox, r, rt, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements);
+ setLogicalWidthForTextRun(lineBox, run, renderText, totalLogicalWidth, lineInfo, textBoxDataMap, verticalPositionCache, wordMeasurements);
} else {
- isAfterExpansion = false;
- if (!r->m_object->isRenderInline()) {
- RenderBox* renderBox = toRenderBox(r->m_object);
- if (renderBox->isRubyRun())
- setMarginsForRubyRun(r, toRenderRubyRun(renderBox), previousObject, lineInfo);
- r->m_box->setLogicalWidth(logicalWidthForChild(renderBox));
+ bool encounteredJustifiedRuby = false;
+ if (is<RenderRubyRun>(run->renderer()) && textAlign == JUSTIFY && run != trailingSpaceRun && downcast<RenderRubyRun>(run->renderer()).rubyBase()) {
+ auto* rubyBase = downcast<RenderRubyRun>(run->renderer()).rubyBase();
+ if (rubyBase->firstRootBox() && !rubyBase->firstRootBox()->nextRootBox() && run->renderer().style().collapseWhiteSpace()) {
+ rubyBase->setIsAfterExpansion(isAfterExpansion);
+ for (auto* leafChild = rubyBase->firstRootBox()->firstLeafChild(); leafChild; leafChild = leafChild->nextLeafChild()) {
+ if (!is<InlineTextBox>(*leafChild))
+ continue;
+ auto& textBox = downcast<InlineTextBox>(*leafChild);
+ encounteredJustifiedRuby = true;
+ auto& renderText = downcast<RenderText>(leafChild->renderer());
+ ExpansionBehavior expansionBehavior = expansionBehaviorForInlineTextBox(*rubyBase, textBox, nullptr, nullptr, textAlign, isAfterExpansion);
+ applyExpansionBehavior(textBox, expansionBehavior);
+ unsigned opportunitiesInRun;
+ std::tie(opportunitiesInRun, isAfterExpansion) = FontCascade::expansionOpportunityCount(renderText.stringView(), leafChild->direction(), expansionBehavior);
+ expansionOpportunities.append(opportunitiesInRun);
+ expansionOpportunityCount += opportunitiesInRun;
+ }
+ }
+ }
+
+ if (!encounteredJustifiedRuby)
+ isAfterExpansion = false;
+
+ if (!is<RenderInline>(run->renderer())) {
+ auto& renderBox = downcast<RenderBox>(run->renderer());
+ if (is<RenderRubyRun>(renderBox))
+ setMarginsForRubyRun(run, downcast<RenderRubyRun>(renderBox), previousRun ? &previousRun->renderer() : nullptr, lineInfo);
+ run->box()->setLogicalWidth(logicalWidthForChild(renderBox));
totalLogicalWidth += marginStartForChild(renderBox) + marginEndForChild(renderBox);
}
}
- totalLogicalWidth += r->m_box->logicalWidth();
- previousObject = r->m_object;
+ totalLogicalWidth += run->box()->logicalWidth();
+ previousRun = run;
}
if (isAfterExpansion && !expansionOpportunities.isEmpty()) {
@@ -1150,72 +886,93 @@ BidiRun* RenderBlock::computeInlineDirectionPositionsForSegment(RootInlineBox* l
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 RenderBlock::computeBlockDirectionPositionsForLine(RootInlineBox* lineBox, BidiRun* firstRun, GlyphOverflowAndFallbackFontsMap& textBoxDataMap,
+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,
VerticalPositionCache& verticalPositionCache)
{
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->m_box);
- if (!r->m_box)
+ for (auto* run = firstRun; run; run = run->next()) {
+ ASSERT(run->box());
+ if (!run->box())
continue; // Skip runs with no line boxes.
// Align positioned boxes with the top of the line box. This is
// a reasonable approximation of an appropriate y position.
- if (r->m_object->isOutOfFlowPositioned())
- r->m_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->m_object->isText())
- toRenderText(r->m_object)->positionLineBox(r->m_box);
- else if (r->m_object->isBox())
- toRenderBox(r->m_object)->positionLineBox(r->m_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, RenderText* renderer)
+static inline bool isCollapsibleSpace(UChar character, const RenderText& renderer)
{
if (character == ' ' || character == '\t' || character == softHyphen)
return true;
if (character == '\n')
- return !renderer->style()->preserveNewline();
+ return !renderer.style().preserveNewline();
if (character == noBreakSpace)
- return renderer->style()->nbspMode() == SPACE;
+ return renderer.style().nbspMode() == SPACE;
return false;
}
-
-static void setStaticPositions(RenderBlock* block, RenderBox* child)
-{
- // FIXME: The math here is actually not really right. It's a best-guess approximation that
- // will work for the common cases
- RenderObject* containerBlock = child->container();
- LayoutUnit blockHeight = block->logicalHeight();
- if (containerBlock->isRenderInline()) {
- // A relative positioned inline encloses us. In this case, we also have to determine our
- // position as though we were an inline. Set |staticInlinePosition| and |staticBlockPosition| on the relative positioned
- // inline so that we can obtain the value later.
- toRenderInline(containerBlock)->layer()->setStaticInlinePosition(block->startAlignedOffsetForLine(blockHeight, false));
- toRenderInline(containerBlock)->layer()->setStaticBlockPosition(blockHeight);
- }
- block->updateStaticInlinePositionForChild(child, blockHeight);
- child->layer()->setStaticBlockPosition(blockHeight);
-}
-
template <typename CharacterType>
-static inline int findFirstTrailingSpace(RenderText* lastText, const CharacterType* characters, int start, int stop)
+static inline int findFirstTrailingSpace(const RenderText& lastText, const CharacterType* characters, int start, int stop)
{
int firstSpace = stop;
while (firstSpace > start) {
@@ -1228,36 +985,36 @@ static inline int findFirstTrailingSpace(RenderText* lastText, const CharacterTy
return firstSpace;
}
-inline BidiRun* RenderBlock::handleTrailingSpaces(BidiRunList<BidiRun>& bidiRuns, BidiContext* currentContext)
+inline BidiRun* RenderBlockFlow::handleTrailingSpaces(BidiRunList<BidiRun>& bidiRuns, BidiContext* currentContext)
{
if (!bidiRuns.runCount()
- || !bidiRuns.logicallyLastRun()->m_object->style()->breakOnlyAfterWhiteSpace()
- || !bidiRuns.logicallyLastRun()->m_object->style()->autoWrap())
- return 0;
+ || !bidiRuns.logicallyLastRun()->renderer().style().breakOnlyAfterWhiteSpace()
+ || !bidiRuns.logicallyLastRun()->renderer().style().autoWrap())
+ return nullptr;
BidiRun* trailingSpaceRun = bidiRuns.logicallyLastRun();
- RenderObject* lastObject = trailingSpaceRun->m_object;
- if (!lastObject->isText())
- return 0;
+ const RenderObject& lastObject = trailingSpaceRun->renderer();
+ if (!is<RenderText>(lastObject))
+ return nullptr;
- RenderText* lastText = toRenderText(lastObject);
+ const RenderText& lastText = downcast<RenderText>(lastObject);
int firstSpace;
- if (lastText->is8Bit())
- firstSpace = findFirstTrailingSpace(lastText, lastText->characters8(), trailingSpaceRun->start(), trailingSpaceRun->stop());
+ if (lastText.is8Bit())
+ firstSpace = findFirstTrailingSpace(lastText, lastText.characters8(), trailingSpaceRun->start(), trailingSpaceRun->stop());
else
- firstSpace = findFirstTrailingSpace(lastText, lastText->characters16(), trailingSpaceRun->start(), trailingSpaceRun->stop());
+ firstSpace = findFirstTrailingSpace(lastText, lastText.characters16(), trailingSpaceRun->start(), trailingSpaceRun->stop());
if (firstSpace == trailingSpaceRun->stop())
- return 0;
+ return nullptr;
- TextDirection direction = style()->direction();
+ TextDirection direction = style().direction();
bool shouldReorder = trailingSpaceRun != (direction == LTR ? bidiRuns.lastRun() : bidiRuns.firstRun());
if (firstSpace != trailingSpaceRun->start()) {
BidiContext* baseContext = currentContext;
while (BidiContext* parent = baseContext->parent())
baseContext = parent;
- BidiRun* newTrailingRun = new (renderArena()) BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->m_object, baseContext, OtherNeutral);
+ BidiRun* newTrailingRun = new BidiRun(firstSpace, trailingSpaceRun->m_stop, trailingSpaceRun->renderer(), baseContext, U_OTHER_NEUTRAL);
trailingSpaceRun->m_stop = firstSpace;
if (direction == LTR)
bidiRuns.addRun(newTrailingRun);
@@ -1279,21 +1036,30 @@ inline BidiRun* RenderBlock::handleTrailingSpaces(BidiRunList<BidiRun>& bidiRuns
return trailingSpaceRun;
}
-void RenderBlock::appendFloatingObjectToLastLine(FloatingObject* floatingObject)
+void RenderBlockFlow::appendFloatingObjectToLastLine(FloatingObject* floatingObject)
{
- ASSERT(!floatingObject->m_originatingLine);
- floatingObject->m_originatingLine = lastRootBox();
+ ASSERT_WITH_SECURITY_IMPLICATION(!floatingObject->originatingLine());
+ floatingObject->setOriginatingLine(lastRootBox());
lastRootBox()->appendFloat(floatingObject->renderer());
}
-// FIXME: This should be a BidiStatus constructor or create method.
-static inline BidiStatus statusWithDirection(TextDirection textDirection, bool isOverride)
+static inline void notifyResolverToResumeInIsolate(InlineBidiResolver& resolver, RenderObject* root, RenderObject* startObject)
{
- WTF::Unicode::Direction direction = textDirection == LTR ? LeftToRight : RightToLeft;
- RefPtr<BidiContext> context = BidiContext::create(textDirection == LTR ? 0 : 1, direction, isOverride, FromStyleOrDOM);
+ if (root != startObject) {
+ RenderObject* parent = startObject->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_midpointState
+ resolver.midpointState() = topResolver.midpointState();
+ resolver.midpointState().setCurrentMidpoint(topResolver.midpointForIsolatedRun(isolatedRun));
- // This copies BidiStatus and may churn the ref on BidiContext. I doubt it matters.
- return BidiStatus(direction, direction, direction, context.release());
+ // Set up m_nestedIsolateCount
+ notifyResolverToResumeInIsolate(resolver, root, startObject);
}
// FIXME: BidiResolver should have this logic.
@@ -1308,37 +1074,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* startObj = isolatedRun->object();
+ 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(containingIsolate(startObj, currentRoot));
+ RenderInline* isolatedInline = downcast<RenderInline>(highestContainingIsolateWithinRoot(startObject, currentRoot));
+ ASSERT(isolatedInline);
+
InlineBidiResolver isolatedResolver;
- EUnicodeBidi unicodeBidi = isolatedInline->style()->unicodeBidi();
+ EUnicodeBidi unicodeBidi = isolatedInline->style().unicodeBidi();
TextDirection direction;
if (unicodeBidi == Plaintext)
- determineDirectionality(direction, InlineIterator(isolatedInline, isolatedRun->object(), 0));
+ determineDirectionality(direction, InlineIterator(isolatedInline, &isolatedRun.object, 0));
else {
ASSERT(unicodeBidi == Isolate || unicodeBidi == IsolateOverride);
- direction = isolatedInline->style()->direction();
+ direction = isolatedInline->style().direction();
}
- isolatedResolver.setStatus(statusWithDirection(direction, isOverride(unicodeBidi)));
+ isolatedResolver.setStatus(BidiStatus(direction, isOverride(unicodeBidi)));
- // FIXME: The fact that we have to construct an Iterator here
- // currently prevents this code from moving into BidiResolver.
- if (!bidiFirstSkippingEmptyInlines(isolatedInline, &isolatedResolver))
- continue;
+ 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, startObj, 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().
@@ -1350,73 +1116,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();
- }
- }
-}
-
-static inline void constructBidiRunsForLine(const RenderBlock* 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.m_obj);
- BidiRun* segmentMarker = createRun(segmentStart.m_pos, segmentStart.m_pos, segmentStart.m_obj, topResolver);
- segmentMarker->m_startsSegment = true;
- bidiRuns.addRun(segmentMarker);
- // Do not collapse midpoints between segments
- topResolver.midpointState().betweenMidpoints = false;
+ while (!isolatedResolver.isolatedRuns().isEmpty()) {
+ auto runWithContext = WTFMove(isolatedResolver.isolatedRuns().last());
+ isolatedResolver.isolatedRuns().removeLast();
+ topResolver.setMidpointForIsolatedRun(runWithContext.runToReplace, isolatedResolver.midpointForIsolatedRun(runWithContext.runToReplace));
+ topResolver.isolatedRuns().append(WTFMove(runWithContext));
}
- if (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* RenderBlock::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.m_obj);
+ 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;
@@ -1427,136 +1156,43 @@ RootInlineBox* RenderBlock::createLineBoxesFromBidiRuns(BidiRunList<BidiRun>& bi
// 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
// contains reversed text or not. If we wouldn't do that editing and thus
// text selection in RTL boxes would not work as expected.
if (isSVGRootInlineBox) {
- ASSERT(isSVGText());
- static_cast<SVGRootInlineBox*>(lineBox)->computePerCharacterLayoutInformation();
+ ASSERT_WITH_SECURITY_IMPLICATION(isSVGText());
+ 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;
}
-// Like LayoutState for layout(), LineLayoutState keeps track of global information
-// during an entire linebox tree layout pass (aka layoutInlineChildren).
-class LineLayoutState {
-public:
- LineLayoutState(bool fullLayout, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom, RenderFlowThread* flowThread)
- : m_lastFloat(0)
- , m_endLine(0)
- , m_floatIndex(0)
- , m_endLineLogicalTop(0)
- , m_endLineMatched(false)
- , m_checkForFloatsFromLastLine(false)
- , m_isFullLayout(fullLayout)
- , m_repaintLogicalTop(repaintLogicalTop)
- , m_repaintLogicalBottom(repaintLogicalBottom)
- , m_adjustedLogicalLineTop(0)
- , m_usesRepaintBounds(false)
- , m_flowThread(flowThread)
- { }
-
- void markForFullLayout() { m_isFullLayout = true; }
- bool isFullLayout() const { return m_isFullLayout; }
-
- bool usesRepaintBounds() const { return m_usesRepaintBounds; }
-
- void setRepaintRange(LayoutUnit logicalHeight)
- {
- m_usesRepaintBounds = true;
- m_repaintLogicalTop = m_repaintLogicalBottom = logicalHeight;
- }
-
- void updateRepaintRangeFromBox(RootInlineBox* box, LayoutUnit paginationDelta = 0)
- {
- m_usesRepaintBounds = true;
- m_repaintLogicalTop = min(m_repaintLogicalTop, box->logicalTopVisualOverflow() + min<LayoutUnit>(paginationDelta, 0));
- m_repaintLogicalBottom = max(m_repaintLogicalBottom, box->logicalBottomVisualOverflow() + max<LayoutUnit>(paginationDelta, 0));
- }
-
- bool endLineMatched() const { return m_endLineMatched; }
- void setEndLineMatched(bool endLineMatched) { m_endLineMatched = endLineMatched; }
-
- bool checkForFloatsFromLastLine() const { return m_checkForFloatsFromLastLine; }
- void setCheckForFloatsFromLastLine(bool check) { m_checkForFloatsFromLastLine = check; }
-
- LineInfo& lineInfo() { return m_lineInfo; }
- const LineInfo& lineInfo() const { return m_lineInfo; }
-
- LayoutUnit endLineLogicalTop() const { return m_endLineLogicalTop; }
- void setEndLineLogicalTop(LayoutUnit logicalTop) { m_endLineLogicalTop = logicalTop; }
-
- RootInlineBox* endLine() const { return m_endLine; }
- void setEndLine(RootInlineBox* line) { m_endLine = line; }
-
- RenderBlock::FloatingObject* lastFloat() const { return m_lastFloat; }
- void setLastFloat(RenderBlock::FloatingObject* lastFloat) { m_lastFloat = lastFloat; }
-
- Vector<RenderBlock::FloatWithRect>& floats() { return m_floats; }
-
- unsigned floatIndex() const { return m_floatIndex; }
- void setFloatIndex(unsigned floatIndex) { m_floatIndex = floatIndex; }
-
- LayoutUnit adjustedLogicalLineTop() const { return m_adjustedLogicalLineTop; }
- void setAdjustedLogicalLineTop(LayoutUnit value) { m_adjustedLogicalLineTop = value; }
-
- RenderFlowThread* flowThread() const { return m_flowThread; }
- void setFlowThread(RenderFlowThread* thread) { m_flowThread = thread; }
-
-private:
- Vector<RenderBlock::FloatWithRect> m_floats;
- RenderBlock::FloatingObject* m_lastFloat;
- RootInlineBox* m_endLine;
- LineInfo m_lineInfo;
- unsigned m_floatIndex;
- LayoutUnit m_endLineLogicalTop;
- bool m_endLineMatched;
- bool m_checkForFloatsFromLastLine;
-
- bool m_isFullLayout;
-
- // FIXME: Should this be a range object instead of two ints?
- LayoutUnit& m_repaintLogicalTop;
- LayoutUnit& m_repaintLogicalBottom;
-
- LayoutUnit m_adjustedLogicalLineTop;
-
- bool m_usesRepaintBounds;
-
- RenderFlowThread* m_flowThread;
-};
-
-static void deleteLineRange(LineLayoutState& layoutState, RenderArena* arena, RootInlineBox* startLine, RootInlineBox* stopLine = 0)
+static void deleteLineRange(LineLayoutState& layoutState, RootInlineBox* startLine, RootInlineBox* stopLine = 0)
{
RootInlineBox* boxToDelete = startLine;
while (boxToDelete && boxToDelete != stopLine) {
layoutState.updateRepaintRangeFromBox(boxToDelete);
- // Note: deleteLineRange(renderArena(), firstRootBox()) is not identical to deleteLineBoxTree().
+ // Note: deleteLineRange(firstRootBox()) is not identical to deleteLineBoxTree().
// deleteLineBoxTree uses nextLineBox() instead of nextRootBox() when traversing.
RootInlineBox* next = boxToDelete->nextRootBox();
- boxToDelete->deleteLine(arena);
+ boxToDelete->deleteLine();
boxToDelete = next;
}
}
-void RenderBlock::layoutRunsAndFloats(LineLayoutState& layoutState, bool hasInlineChild)
+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) {
@@ -1568,19 +1204,18 @@ void RenderBlock::layoutRunsAndFloats(LineLayoutState& layoutState, bool hasInli
// determineStartPosition can change the fullLayout flag we have to do this here. Failure to call
// determineStartPosition first will break fast/repaint/line-flow-with-floats-9.html.
if (layoutState.isFullLayout() && hasInlineChild && !selfNeedsLayout()) {
- setNeedsLayout(true, MarkOnlyThis); // Mark as needing a full layout to force us to repaint.
- RenderView* v = view();
- if (v && !v->doingFullRepaint() && hasLayer()) {
+ setNeedsLayout(MarkOnlyThis); // Mark as needing a full layout to force us to repaint.
+ if (!view().doingFullRepaint() && hasLayer()) {
// 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());
+ layoutState.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.
@@ -1592,7 +1227,7 @@ void RenderBlock::layoutRunsAndFloats(LineLayoutState& layoutState, bool hasInli
if (startLine) {
if (!layoutState.usesRepaintBounds())
layoutState.setRepaintRange(logicalHeight());
- deleteLineRange(layoutState, renderArena(), startLine);
+ deleteLineRange(layoutState, startLine);
}
if (!layoutState.isFullLayout() && lastRootBox() && lastRootBox()->endsWithBreak()) {
@@ -1600,13 +1235,13 @@ void RenderBlock::layoutRunsAndFloats(LineLayoutState& layoutState, bool hasInli
// adjust the height accordingly.
// A line break can be either the first or the last object on a line, depending on its direction.
if (InlineBox* lastLeafChild = lastRootBox()->lastLeafChild()) {
- RenderObject* lastObject = lastLeafChild->renderer();
+ RenderObject* lastObject = &lastLeafChild->renderer();
if (!lastObject->isBR())
- lastObject = lastRootBox()->firstLeafChild()->renderer();
+ lastObject = &lastRootBox()->firstLeafChild()->renderer();
if (lastObject->isBR()) {
- EClear clear = lastObject->style()->clear();
+ EClear clear = lastObject->style().clear();
if (clear != CNONE)
- newLine(clear);
+ clearFloats(clear);
}
}
}
@@ -1616,18 +1251,8 @@ void RenderBlock::layoutRunsAndFloats(LineLayoutState& layoutState, bool hasInli
repaintDirtyFloats(layoutState.floats());
}
-RenderBlock::RenderTextInfo::RenderTextInfo()
- : m_text(0)
- , m_font(0)
-{
-}
-
-RenderBlock::RenderTextInfo::~RenderTextInfo()
-{
-}
-
// Before restarting the layout loop with a new logicalHeight, remove all floats that were added and reset the resolver.
-inline const InlineIterator& RenderBlock::restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastFloatFromPreviousLine, InlineBidiResolver& resolver, const InlineIterator& oldEnd)
+inline const InlineIterator& RenderBlockFlow::restartLayoutRunsAndFloatsInRange(LayoutUnit oldLogicalHeight, LayoutUnit newLogicalHeight, FloatingObject* lastFloatFromPreviousLine, InlineBidiResolver& resolver, const InlineIterator& oldEnd)
{
removeFloatingObjectsBelow(lastFloatFromPreviousLine, oldLogicalHeight);
setLogicalHeight(newLogicalHeight);
@@ -1635,189 +1260,17 @@ inline const InlineIterator& RenderBlock::restartLayoutRunsAndFloatsInRange(Layo
return oldEnd;
}
-#if ENABLE(CSS_SHAPES)
-static inline float firstPositiveWidth(const WordMeasurements& wordMeasurements)
-{
- for (size_t i = 0; i < wordMeasurements.size(); ++i) {
- if (wordMeasurements[i].width > 0)
- return wordMeasurements[i].width;
- }
- return 0;
-}
-
-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();
-}
-
-static inline void pushShapeContentOverflowBelowTheContentBox(RenderBlock* block, ShapeInsideInfo* shapeInsideInfo, LayoutUnit lineTop, LayoutUnit lineHeight)
-{
- ASSERT(shapeInsideInfo);
-
- LayoutUnit logicalLineBottom = lineTop + lineHeight;
- LayoutUnit shapeLogicalBottom = shapeInsideInfo->shapeLogicalBottom();
- LayoutUnit shapeContainingBlockHeight = shapeInsideInfo->shapeContainingBlockHeight();
-
- bool isOverflowPositionedAlready = (shapeContainingBlockHeight - 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 || !shapeContainingBlockHeight || isOverflowPositionedAlready)
- return;
-
- LayoutUnit newLogicalHeight = block->logicalHeight() + (shapeContainingBlockHeight - (lineTop + shapeInsideInfo->owner()->borderAndPaddingAfter()));
- block->setLogicalHeight(newLogicalHeight);
-}
-
-void RenderBlock::updateShapeAndSegmentsForCurrentLine(ShapeInsideInfo*& shapeInsideInfo, LayoutUnit& absoluteLogicalTop, LineLayoutState& layoutState)
-{
- if (layoutState.flowThread())
- return updateShapeAndSegmentsForCurrentLineInFlowThread(shapeInsideInfo, layoutState);
-
- if (!shapeInsideInfo)
- return;
-
- LayoutUnit lineTop = logicalHeight() + absoluteLogicalTop;
- 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->computeSegmentsForLine(lineTop, lineHeight);
-
- pushShapeContentOverflowBelowTheContentBox(this, shapeInsideInfo, lineTop, lineHeight);
-}
-
-void RenderBlock::updateShapeAndSegmentsForCurrentLineInFlowThread(ShapeInsideInfo*& shapeInsideInfo, LineLayoutState& layoutState)
+void RenderBlockFlow::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, InlineBidiResolver& resolver, const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus, unsigned consecutiveHyphenatedLines)
{
- ASSERT(layoutState.flowThread());
-
- LayoutUnit lineHeight = this->lineHeight(layoutState.lineInfo().isFirstLine(), isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
-
- RenderRegion* currentRegion = regionAtBlockOffset(logicalHeight());
- if (!currentRegion)
- return;
-
- shapeInsideInfo = currentRegion->shapeInsideInfo();
-
- LayoutUnit logicalLineTopInFlowThread = logicalHeight() + offsetFromLogicalTopOfFirstPage();
- LayoutUnit logicalLineBottomInFlowThread = logicalLineTopInFlowThread + lineHeight;
- LayoutUnit logicalRegionTopInFlowThread = currentRegion->logicalTopForFlowThreadContent();
- LayoutUnit logicalRegionBottomInFlowThread = logicalRegionTopInFlowThread + currentRegion->logicalHeight() - currentRegion->borderAndPaddingBefore() - currentRegion->borderAndPaddingAfter();
-
- // We only want to deal regions with shapes, so we look up for the next region whether it has a shape
- if (!shapeInsideInfo && !currentRegion->isLastRegion()) {
- LayoutUnit deltaToNextRegion = logicalHeight() + logicalRegionBottomInFlowThread - logicalLineTopInFlowThread;
- RenderRegion* lookupForNextRegion = regionAtBlockOffset(logicalHeight() + deltaToNextRegion);
- if (!lookupForNextRegion->shapeInsideInfo())
- return;
- }
-
- LayoutUnit shapeBottomInFlowThread = LayoutUnit::max();
- if (shapeInsideInfo)
- shapeBottomInFlowThread = shapeInsideInfo->shapeLogicalBottom() + currentRegion->logicalTopForFlowThreadContent();
-
- // If the line is between two shapes/regions we position the line to the top of the next shape/region
- RenderRegion* nextRegion = regionAtBlockOffset(logicalHeight() + lineHeight);
- if ((currentRegion != nextRegion && (logicalLineBottomInFlowThread > logicalRegionBottomInFlowThread)) || (!currentRegion->isLastRegion() && shapeBottomInFlowThread < logicalLineBottomInFlowThread)) {
- LayoutUnit deltaToNextRegion = logicalRegionBottomInFlowThread - logicalLineTopInFlowThread;
- nextRegion = regionAtBlockOffset(logicalHeight() + deltaToNextRegion);
-
- ASSERT(currentRegion != nextRegion);
-
- shapeInsideInfo = nextRegion->shapeInsideInfo();
- setLogicalHeight(logicalHeight() + deltaToNextRegion);
-
- currentRegion = nextRegion;
-
- logicalLineTopInFlowThread = logicalHeight() + offsetFromLogicalTopOfFirstPage();
- logicalLineBottomInFlowThread = logicalLineTopInFlowThread + lineHeight;
- logicalRegionTopInFlowThread = currentRegion->logicalTopForFlowThreadContent();
- logicalRegionBottomInFlowThread = logicalRegionTopInFlowThread + currentRegion->logicalHeight() - currentRegion->borderAndPaddingBefore() - currentRegion->borderAndPaddingAfter();
- }
-
- if (!shapeInsideInfo)
- return;
-
- // We position the first line to the top of the shape in the region or to the previously adjusted position in the shape
- if (logicalLineBottomInFlowThread <= (logicalRegionTopInFlowThread + lineHeight) || (logicalLineTopInFlowThread - logicalRegionTopInFlowThread) < (layoutState.adjustedLogicalLineTop() - currentRegion->borderAndPaddingBefore())) {
- LayoutUnit shapeTopOffset = layoutState.adjustedLogicalLineTop();
- if (!shapeTopOffset)
- 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();
- shapeInsideInfo->computeSegmentsForLine(lineTop, lineHeight);
-
- if (currentRegion->isLastRegion())
- pushShapeContentOverflowBelowTheContentBox(this, shapeInsideInfo, lineTop, lineHeight);
-}
-
-bool RenderBlock::adjustLogicalLineTopAndLogicalHeightIfNeeded(ShapeInsideInfo* shapeInsideInfo, LayoutUnit absoluteLogicalTop, LineLayoutState& layoutState, InlineBidiResolver& resolver, FloatingObject* lastFloatFromPreviousLine, InlineIterator& end, WordMeasurements& wordMeasurements)
-{
- LayoutUnit adjustedLogicalLineTop = adjustLogicalLineTop(shapeInsideInfo, resolver.position(), end, wordMeasurements);
- 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 RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, InlineBidiResolver& resolver, const InlineIterator& cleanLineStart, const BidiStatus& cleanLineBidiStatus, unsigned consecutiveHyphenatedLines)
-{
- RenderStyle* styleToUse = style();
- bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
+ const RenderStyle& styleToUse = style();
+ bool paginated = view().layoutState() && view().layoutState()->isPaginated();
LineMidpointState& lineMidpointState = resolver.midpointState();
InlineIterator end = resolver.position();
bool checkForEndLineMatch = layoutState.endLine();
RenderTextInfo renderTextInfo;
VerticalPositionCache verticalPositionCache;
- LineBreaker lineBreaker(this);
-
-#if ENABLE(CSS_SHAPES)
- LayoutUnit absoluteLogicalTop;
- 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.
- absoluteLogicalTop = logicalTop();
- }
- // Begin layout at the logical top of our shape inside.
- if (logicalHeight() + absoluteLogicalTop < shapeInsideInfo->shapeLogicalTop()) {
- LayoutUnit logicalHeight = shapeInsideInfo->shapeLogicalTop() - absoluteLogicalTop;
- if (layoutState.flowThread())
- logicalHeight -= shapeInsideInfo->owner()->borderAndPaddingBefore();
- setLogicalHeight(logicalHeight);
- }
- }
-#endif
+ LineBreaker lineBreaker(*this);
while (!end.atEnd()) {
// FIXME: Is this check necessary before the first iteration or can it be moved to the end?
@@ -1825,6 +1278,7 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin
layoutState.setEndLineMatched(matchedEndLine(layoutState, resolver, cleanLineStart, cleanLineBidiStatus));
if (layoutState.endLineMatched()) {
resolver.setPosition(InlineIterator(resolver.position().root(), 0, 0), 0);
+ layoutState.marginInfo().clearMargin();
break;
}
}
@@ -1836,14 +1290,12 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin
const InlineIterator oldEnd = end;
bool isNewUBAParagraph = layoutState.lineInfo().previousLineBrokeCleanly();
- FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last() : 0;
+ FloatingObject* lastFloatFromPreviousLine = (containsFloats()) ? m_floatingObjects->set().last().get() : nullptr;
-#if ENABLE(CSS_SHAPES)
- updateShapeAndSegmentsForCurrentLine(shapeInsideInfo, absoluteLogicalTop, 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.
@@ -1854,30 +1306,26 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin
break;
}
-#if ENABLE(CSS_SHAPES)
- if (adjustLogicalLineTopAndLogicalHeightIfNeeded(shapeInsideInfo, absoluteLogicalTop, layoutState, resolver, lastFloatFromPreviousLine, end, wordMeasurements))
- continue;
-#endif
ASSERT(end != resolver.position());
// This is a short-cut for empty lines.
if (layoutState.lineInfo().isEmpty()) {
if (lastRootBox())
- lastRootBox()->setLineBreakInfo(end.m_obj, end.m_pos, resolver.status());
+ lastRootBox()->setLineBreakInfo(end.renderer(), end.offset(), resolver.status());
} else {
- VisualDirectionOverride override = (styleToUse->rtlOrdering() == VisualOrder ? (styleToUse->direction() == LTR ? VisualLeftToRightOverride : VisualRightToLeftOverride) : NoVisualOverride);
+ VisualDirectionOverride override = (styleToUse.rtlOrdering() == VisualOrder ? (styleToUse.direction() == LTR ? VisualLeftToRightOverride : VisualRightToLeftOverride) : NoVisualOverride);
- if (isNewUBAParagraph && styleToUse->unicodeBidi() == Plaintext && !resolver.context()->parent()) {
- TextDirection direction = styleToUse->direction();
+ if (isNewUBAParagraph && styleToUse.unicodeBidi() == Plaintext && !resolver.context()->parent()) {
+ TextDirection direction = styleToUse.direction();
determineDirectionality(direction, resolver.position());
- resolver.setStatus(BidiStatus(direction, isOverride(styleToUse->unicodeBidi())));
+ resolver.setStatus(BidiStatus(direction, isOverride(styleToUse.unicodeBidi())));
}
// 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;
@@ -1890,76 +1338,109 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin
// 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();
resolver.markCurrentRunEmpty(); // FIXME: This can probably be replaced by an ASSERT (or just removed).
if (lineBox) {
- lineBox->setLineBreakInfo(end.m_obj, end.m_pos, resolver.status());
+ 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(renderArena());
- 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())
- lineBox->setContainingRegion(regionAtBlockOffset(lineBox->lineTopWithLeading()));
+ updateRegionForLine(lineBox);
}
}
}
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();
- FloatingObjectSetIterator it = floatingObjectSet.begin();
- FloatingObjectSetIterator end = floatingObjectSet.end();
+ auto it = floatingObjectSet.begin();
+ auto end = floatingObjectSet.end();
if (layoutState.lastFloat()) {
- FloatingObjectSetIterator lastFloatIterator = floatingObjectSet.find(layoutState.lastFloat());
+ auto lastFloatIterator = floatingObjectSet.find<FloatingObject&, FloatingObjectHashTranslator>(*layoutState.lastFloat());
ASSERT(lastFloatIterator != end);
++lastFloatIterator;
it = lastFloatIterator;
}
for (; it != end; ++it) {
- FloatingObject* f = *it;
+ FloatingObject* f = it->get();
appendFloatingObjectToLastLine(f);
- ASSERT(f->m_renderer == layoutState.floats()[layoutState.floatIndex()].object);
+ ASSERT(&f->renderer() == &layoutState.floats()[layoutState.floatIndex()].object);
// If a float's geometry has changed, give up on syncing with clean lines.
if (layoutState.floats()[layoutState.floatIndex()].rect != f->frameRect())
checkForEndLineMatch = false;
layoutState.setFloatIndex(layoutState.floatIndex() + 1);
}
- layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last() : 0);
+ layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last().get() : nullptr);
}
lineMidpointState.reset();
resolver.setPosition(end, numberOfIsolateAncestors(end));
}
- if (paginated && !style()->hasAutoWidows()) {
+ // In case we already adjusted the line positions during this layout to avoid widows
+ // then we need to ignore the possibility of having a new widows situation.
+ // Otherwise, we risk leaving empty containers which is against the block fragmentation principles.
+ if (paginated && !style().hasAutoWidows() && !didBreakAtLineToAvoidWidow()) {
// Check the line boxes to make sure we didn't create unacceptable widows.
// However, we'll prioritize orphans - so nothing we do here should create
// a new orphan.
@@ -1979,11 +1460,11 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin
if (!lineBox || !lineBox->isFirstAfterPageBreak() || lineBox == firstLineInBlock)
return;
- if (numLinesHanging < style()->widows()) {
+ if (numLinesHanging < style().widows()) {
// We have detected a widow. Now we need to work out how many
// lines there are on the previous page, and how many we need
// to steal.
- int numLinesNeeded = style()->widows() - numLinesHanging;
+ int numLinesNeeded = style().widows() - numLinesHanging;
RootInlineBox* currentFirstLineOfNewPage = lineBox;
// Count the number of lines in the previous page.
@@ -1999,58 +1480,73 @@ void RenderBlock::layoutRunsAndFloatsInRange(LineLayoutState& layoutState, Inlin
// This means that setting widows implies we also care about orphans, but given
// the specification says the initial orphan value is non-zero, this is ok. The
// author is always free to set orphans explicitly as well.
- int orphans = style()->hasAutoOrphans() ? style()->initialOrphans() : style()->orphans();
+ int orphans = style().hasAutoOrphans() ? style().initialOrphans() : style().orphans();
int numLinesAvailable = numLinesInPreviousPage - orphans;
if (numLinesAvailable <= 0)
return;
- int numLinesToTake = min(numLinesAvailable, numLinesNeeded);
+ int numLinesToTake = std::min(numLinesAvailable, numLinesNeeded);
// Wind back from our first widowed line.
lineBox = currentFirstLineOfNewPage;
for (int i = 0; i < numLinesToTake; ++i)
lineBox = lineBox->prevRootBox();
// We now want to break at this line. Remember for next layout and trigger relayout.
- setBreakAtLineToAvoidWidow(lineBox);
+ setBreakAtLineToAvoidWidow(lineCount(lineBox));
markLinesDirtyInBlockRange(lastRootBox()->lineBottomWithLeading(), lineBox->lineBottomWithLeading(), lineBox);
}
}
+ clearDidBreakAtLineToAvoidWidow();
}
-void RenderBlock::linkToEndLineIfNeeded(LineLayoutState& layoutState)
+void RenderBlockFlow::reattachCleanLineFloats(RootInlineBox& cleanLine, LayoutUnit delta, bool isFirstCleanLine)
{
- if (layoutState.endLine()) {
+ 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)
+{
+ auto* firstCleanLine = layoutState.endLine();
+ if (firstCleanLine) {
if (layoutState.endLineMatched()) {
- bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
+ 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);
line->adjustBlockDirectionPosition(delta);
}
if (layoutState.flowThread())
- line->setContainingRegion(regionAtBlockOffset(line->lineTopWithLeading()));
- if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
- Vector<RenderBox*>::iterator end = cleanLineFloats->end();
- for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
- FloatingObject* floatingObject = insertFloatingObject(*f);
- ASSERT(!floatingObject->m_originatingLine);
- floatingObject->m_originatingLine = line;
- setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f) + delta);
- positionNewFloats();
- }
- }
+ updateRegionForLine(line);
+ reattachCleanLineFloats(*line, delta, line == firstCleanLine);
}
setLogicalHeight(lastRootBox()->lineBottomWithLeading());
} else {
// Delete all the remaining lines.
- deleteLineRange(layoutState, renderArena(), layoutState.endLine());
+ deleteLineRange(layoutState, layoutState.endLine());
}
}
@@ -2061,8 +1557,9 @@ void RenderBlock::linkToEndLineIfNeeded(LineLayoutState& layoutState)
if (layoutState.checkForFloatsFromLastLine()) {
LayoutUnit bottomVisualOverflow = lastRootBox()->logicalBottomVisualOverflow();
LayoutUnit bottomLayoutOverflow = lastRootBox()->logicalBottomLayoutOverflow();
- TrailingFloatsRootInlineBox* trailingFloatsLineBox = new (renderArena()) TrailingFloatsRootInlineBox(this);
- m_lineBoxes.appendLineBox(trailingFloatsLineBox);
+ auto newLineBox = std::make_unique<TrailingFloatsRootInlineBox>(*this);
+ auto trailingFloatsLineBox = newLineBox.get();
+ m_lineBoxes.appendLineBox(WTFMove(newLineBox));
trailingFloatsLineBox->setConstructed();
GlyphOverflowAndFallbackFontsMap textBoxDataMap;
VerticalPositionCache verticalPositionCache;
@@ -2074,25 +1571,25 @@ void RenderBlock::linkToEndLineIfNeeded(LineLayoutState& layoutState)
LayoutRect logicalVisualOverflow(0, blockLogicalHeight, 1, bottomVisualOverflow - blockLogicalHeight);
trailingFloatsLineBox->setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, trailingFloatsLineBox->lineTop(), trailingFloatsLineBox->lineBottom());
if (layoutState.flowThread())
- trailingFloatsLineBox->setContainingRegion(regionAtBlockOffset(trailingFloatsLineBox->lineTopWithLeading()));
+ updateRegionForLine(trailingFloatsLineBox);
}
const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
- FloatingObjectSetIterator it = floatingObjectSet.begin();
- FloatingObjectSetIterator end = floatingObjectSet.end();
+ auto it = floatingObjectSet.begin();
+ auto end = floatingObjectSet.end();
if (layoutState.lastFloat()) {
- FloatingObjectSetIterator lastFloatIterator = floatingObjectSet.find(layoutState.lastFloat());
+ auto lastFloatIterator = floatingObjectSet.find<FloatingObject&, FloatingObjectHashTranslator>(*layoutState.lastFloat());
ASSERT(lastFloatIterator != end);
++lastFloatIterator;
it = lastFloatIterator;
}
for (; it != end; ++it)
- appendFloatingObjectToLastLine(*it);
- layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last() : 0);
+ appendFloatingObjectToLastLine(it->get());
+ layoutState.setLastFloat(!floatingObjectSet.isEmpty() ? floatingObjectSet.last().get() : nullptr);
}
}
-void RenderBlock::repaintDirtyFloats(Vector<FloatWithRect>& floats)
+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
@@ -2100,31 +1597,33 @@ void RenderBlock::repaintDirtyFloats(Vector<FloatWithRect>& floats)
// painted.
for (size_t i = 0; i < floatCount; ++i) {
if (!floats[i].everHadLayout) {
- RenderBox* f = floats[i].object;
- if (!f->x() && !f->y() && f->checkForRepaintDuringLayout())
- f->repaint();
+ RenderBox& box = floats[i].object;
+ if (!box.x() && !box.y() && box.checkForRepaintDuringLayout())
+ box.repaint();
}
}
}
-void RenderBlock::layoutInlineChildren(bool relayoutChildren, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom)
+void RenderBlockFlow::layoutLineBoxes(bool relayoutChildren, LayoutUnit& repaintLogicalTop, LayoutUnit& repaintLogicalBottom)
{
+ ASSERT(!m_simpleLineLayout);
+
setLogicalHeight(borderAndPaddingBefore());
// Lay out our hypothetical grid line as though it occurs at the top of the block.
- if (view()->layoutState() && view()->layoutState()->lineGrid() == this)
+ if (view().layoutState() && view().layoutState()->lineGrid() == this)
layoutLineGridBox();
RenderFlowThread* flowThread = flowThreadContainingBlock();
- bool clearLinesForPagination = firstLineBox() && flowThread && !flowThread->hasRegions();
+ bool clearLinesForPagination = firstRootBox() && flowThread && !flowThread->hasRegions();
// Figure out if we should clear out our line boxes.
// FIXME: Handle resize eventually!
- bool isFullLayout = !firstLineBox() || selfNeedsLayout() || relayoutChildren || clearLinesForPagination;
- LineLayoutState layoutState(isFullLayout, repaintLogicalTop, repaintLogicalBottom, flowThread);
+ bool isFullLayout = !firstRootBox() || selfNeedsLayout() || relayoutChildren || clearLinesForPagination;
+ LineLayoutState layoutState(*this, isFullLayout, repaintLogicalTop, repaintLogicalBottom, flowThread);
if (isFullLayout)
- lineBoxes()->deleteLineBoxes(renderArena());
+ lineBoxes().deleteLineBoxes();
// Text truncation kicks in in two cases:
// 1) If your overflow isn't visible and your text-overflow-mode isn't clip.
@@ -2132,8 +1631,8 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, LayoutUnit& repain
// FIXME: CSS3 says that descendants that are clipped must also know how to truncate. This is insanely
// difficult to figure out in general (especially in the middle of doing layout), so we only handle the
// simple case of an anonymous block truncating when it's parent is clipped.
- bool hasTextOverflow = (style()->textOverflow() && hasOverflowClip())
- || (isAnonymousBlock() && parent() && parent()->isRenderBlock() && parent()->style()->textOverflow() && parent()->hasOverflowClip());
+ bool hasTextOverflow = (style().textOverflow() && hasOverflowClip())
+ || (isAnonymousBlock() && parent() && parent()->isRenderBlock() && parent()->style().textOverflow() && parent()->hasOverflowClip());
// Walk all the lines and delete our ellipsis line boxes if they exist.
if (hasTextOverflow)
@@ -2147,39 +1646,42 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, LayoutUnit& repain
// elements at the same time.
bool hasInlineChild = false;
Vector<RenderBox*> replacedChildren;
- for (InlineWalker walker(this); !walker.atEnd(); walker.advance()) {
- RenderObject* o = walker.current();
- if (!hasInlineChild && o->isInline())
+ for (InlineWalker walker(*this); !walker.atEnd(); walker.advance()) {
+ RenderObject& o = *walker.current();
+
+ if (!hasInlineChild && o.isInline())
hasInlineChild = true;
- if (o->isReplaced() || o->isFloating() || o->isOutOfFlowPositioned()) {
- RenderBox* box = toRenderBox(o);
+ if (o.isReplaced() || o.isFloating() || o.isOutOfFlowPositioned()) {
+ RenderBox& box = downcast<RenderBox>(o);
- if (relayoutChildren || box->hasRelativeDimensions())
- o->setChildNeedsLayout(true, MarkOnlyThis);
+ if (relayoutChildren || box.hasRelativeDimensions())
+ box.setChildNeedsLayout(MarkOnlyThis);
// If relayoutChildren is set and the child has percentage padding or an embedded content box, we also need to invalidate the childs pref widths.
- if (relayoutChildren && box->needsPreferredWidthsRecalculation())
- o->setPreferredLogicalWidthsDirty(true, MarkOnlyThis);
+ if (relayoutChildren && box.needsPreferredWidthsRecalculation())
+ box.setPreferredLogicalWidthsDirty(true, MarkOnlyThis);
- if (o->isOutOfFlowPositioned())
- o->containingBlock()->insertPositionedObject(box);
- else if (o->isFloating())
+ if (box.isOutOfFlowPositioned())
+ box.containingBlock()->insertPositionedObject(box);
+ else if (box.isFloating())
layoutState.floats().append(FloatWithRect(box));
- else if (isFullLayout || o->needsLayout()) {
+ else if (isFullLayout || box.needsLayout()) {
// Replaced element.
- box->dirtyLineBoxes(isFullLayout);
- if (isFullLayout)
- replacedChildren.append(box);
- else
- o->layoutIfNeeded();
+ box.dirtyLineBoxes(isFullLayout);
+ if (!o.isAnonymousInlineBlock()) {
+ if (isFullLayout)
+ replacedChildren.append(&box);
+ else
+ box.layoutIfNeeded();
+ }
}
- } else if (o->isText() || (o->isRenderInline() && !walker.atEndOfInline())) {
- if (!o->isText())
- toRenderInline(o)->updateAlwaysCreateLineBoxes(layoutState.isFullLayout());
- if (layoutState.isFullLayout() || o->selfNeedsLayout())
+ } 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->setNeedsLayout(false);
+ o.clearNeedsLayout();
}
}
@@ -2192,17 +1694,25 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, LayoutUnit& repain
// Expand the last line to accommodate Ruby and emphasis marks.
int lastLineAnnotationsAdjustment = 0;
if (lastRootBox()) {
- LayoutUnit lowestAllowedPosition = max(lastRootBox()->lineBottom(), logicalHeight() + paddingAfter());
- if (!style()->isFlippedLinesWritingMode())
+ LayoutUnit lowestAllowedPosition = std::max(lastRootBox()->lineBottom(), logicalHeight() + paddingAfter());
+ if (!style().isFlippedLinesWritingMode())
lastLineAnnotationsAdjustment = lastRootBox()->computeUnderAnnotationAdjustment(lowestAllowedPosition);
else
lastLineAnnotationsAdjustment = lastRootBox()->computeOverAnnotationAdjustment(lowestAllowedPosition);
}
+
+ // Now do the handling of the bottom of the block, adding in our bottom border/padding and
+ // determining the correct collapsed bottom margin information. This collapse is only necessary
+ // if our last child was an anonymous inline block that might need to propagate margin information out to
+ // us.
+ LayoutUnit beforeEdge = borderAndPaddingBefore();
+ LayoutUnit afterEdge = borderAndPaddingAfter() + scrollbarLogicalHeight() + lastLineAnnotationsAdjustment;
+ if (lastRootBox() && lastRootBox()->hasAnonymousInlineBlock())
+ handleAfterSideOfBlock(beforeEdge, afterEdge, layoutState.marginInfo());
+ else
+ setLogicalHeight(logicalHeight() + afterEdge);
- // Now add in the bottom border/padding.
- setLogicalHeight(logicalHeight() + lastLineAnnotationsAdjustment + borderAndPaddingAfter() + scrollbarLogicalHeight());
-
- if (!firstLineBox() && hasLineIfEmpty())
+ if (!firstRootBox() && hasLineIfEmpty())
setLogicalHeight(logicalHeight() + lineHeight(true, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes));
// See if we have any lines that spill out of our block. If we do, then we will possibly need to
@@ -2211,28 +1721,33 @@ void RenderBlock::layoutInlineChildren(bool relayoutChildren, LayoutUnit& repain
checkLinesForTextOverflow();
}
-void RenderBlock::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWithRect>& floats, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByFloat)
+void RenderBlockFlow::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWithRect>& floats, size_t& floatIndex, bool& encounteredNewFloat, bool& dirtiedByFloat)
{
Vector<RenderBox*>* cleanLineFloats = line->floatsPtr();
if (!cleanLineFloats)
return;
+
+ if (!floats.size()) {
+ encounteredNewFloat = true;
+ return;
+ }
- Vector<RenderBox*>::iterator end = cleanLineFloats->end();
- for (Vector<RenderBox*>::iterator it = cleanLineFloats->begin(); it != end; ++it) {
+ 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());
+ LayoutSize newSize(floatingBox->width() + floatingBox->horizontalMarginExtent(), floatingBox->height() + floatingBox->verticalMarginExtent());
ASSERT_WITH_SECURITY_IMPLICATION(floatIndex < floats.size());
- if (floats[floatIndex].object != floatingBox) {
+ if (&floats[floatIndex].object != floatingBox) {
encounteredNewFloat = true;
return;
}
-
- if (floats[floatIndex].rect.size() != newSize) {
+
+ // We have to reset the cap-height alignment done by the first-letter floats when initial-letter is set, so just always treat first-letter floats
+ // as dirty.
+ if (floats[floatIndex].rect.size() != newSize || (floatingBox->style().styleType() == FIRST_LETTER && floatingBox->style().initialLetterDrop() > 0)) {
LayoutUnit floatTop = isHorizontalWritingMode() ? floats[floatIndex].rect.y() : floats[floatIndex].rect.x();
- LayoutUnit floatHeight = isHorizontalWritingMode() ? max(floats[floatIndex].rect.height(), newSize.height())
- : max(floats[floatIndex].rect.width(), newSize.width());
- floatHeight = min(floatHeight, LayoutUnit::max() - floatTop);
+ 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);
@@ -2242,7 +1757,7 @@ void RenderBlock::checkFloatsInCleanLine(RootInlineBox* line, Vector<FloatWithRe
}
}
-RootInlineBox* RenderBlock::determineStartPosition(LineLayoutState& layoutState, InlineBidiResolver& resolver)
+RootInlineBox* RenderBlockFlow::determineStartPosition(LineLayoutState& layoutState, InlineBidiResolver& resolver)
{
RootInlineBox* curr = 0;
RootInlineBox* last = 0;
@@ -2251,7 +1766,7 @@ RootInlineBox* RenderBlock::determineStartPosition(LineLayoutState& layoutState,
bool dirtiedByFloat = false;
if (!layoutState.isFullLayout()) {
// Paginate all of the clean lines.
- bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
+ bool paginated = view().layoutState() && view().layoutState()->isPaginated();
LayoutUnit paginationDelta = 0;
size_t floatIndex = 0;
for (curr = firstRootBox(); curr && !curr->isDirty(); curr = curr->nextRootBox()) {
@@ -2261,7 +1776,8 @@ RootInlineBox* RenderBlock::determineStartPosition(LineLayoutState& layoutState,
break;
}
paginationDelta -= curr->paginationStrut();
- adjustLinePositionForPagination(curr, paginationDelta, layoutState.flowThread());
+ bool overflowsRegion;
+ adjustLinePositionForPagination(curr, paginationDelta, overflowsRegion, layoutState.flowThread());
if (paginationDelta) {
if (containsFloats() || !layoutState.floats().isEmpty()) {
// FIXME: Do better eventually. For now if we ever shift because of pagination and floats are present just go to a full layout.
@@ -2273,7 +1789,7 @@ RootInlineBox* RenderBlock::determineStartPosition(LineLayoutState& layoutState,
curr->adjustBlockDirectionPosition(paginationDelta);
}
if (layoutState.flowThread())
- curr->setContainingRegion(regionAtBlockOffset(curr->lineTopWithLeading()));
+ updateRegionForLine(curr);
}
// If a new float has been inserted before this line or before its last known float, just do a full layout.
@@ -2291,27 +1807,22 @@ RootInlineBox* RenderBlock::determineStartPosition(LineLayoutState& layoutState,
}
if (layoutState.isFullLayout()) {
- m_lineBoxes.deleteLineBoxTree(renderArena());
+ m_lineBoxes.deleteLineBoxTree();
curr = 0;
- ASSERT(!firstLineBox() && !lastLineBox());
+ ASSERT(!firstRootBox() && !lastRootBox());
} else {
if (curr) {
// We have a dirty line.
if (RootInlineBox* prevRootBox = curr->prevRootBox()) {
// We have a previous line.
- if (!dirtiedByFloat && (!prevRootBox->endsWithBreak() || !prevRootBox->lineBreakObj() || (prevRootBox->lineBreakObj()->isText() && prevRootBox->lineBreakPos() >= toRenderText(prevRootBox->lineBreakObj())->textLength())))
+ if (!dirtiedByFloat && !curr->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;
+ }
}
- } 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();
}
@@ -2323,14 +1834,14 @@ RootInlineBox* RenderBlock::determineStartPosition(LineLayoutState& layoutState,
RootInlineBox* line = firstRootBox();
while (line != curr) {
if (Vector<RenderBox*>* cleanLineFloats = line->floatsPtr()) {
- Vector<RenderBox*>::iterator end = cleanLineFloats->end();
- for (Vector<RenderBox*>::iterator f = cleanLineFloats->begin(); f != end; ++f) {
- FloatingObject* floatingObject = insertFloatingObject(*f);
- ASSERT(!floatingObject->m_originatingLine);
- floatingObject->m_originatingLine = line;
- setLogicalHeight(logicalTopForChild(*f) - marginBeforeForChild(*f));
+ for (auto it = cleanLineFloats->begin(), end = cleanLineFloats->end(); it != end; ++it) {
+ RenderBox* floatingBox = *it;
+ FloatingObject* floatingObject = insertFloatingObject(*floatingBox);
+ ASSERT_WITH_SECURITY_IMPLICATION(!floatingObject->originatingLine());
+ floatingObject->setOriginatingLine(line);
+ setLogicalHeight(logicalTopForChild(*floatingBox) - marginBeforeForChild(*floatingBox));
positionNewFloats();
- ASSERT(layoutState.floats()[numCleanFloats].object == *f);
+ ASSERT(&layoutState.floats()[numCleanFloats].object == floatingBox);
numCleanFloats++;
}
}
@@ -2349,17 +1860,17 @@ RootInlineBox* RenderBlock::determineStartPosition(LineLayoutState& layoutState,
resolver.setPosition(iter, numberOfIsolateAncestors(iter));
resolver.setStatus(last->lineBreakBidiStatus());
} else {
- TextDirection direction = style()->direction();
- if (style()->unicodeBidi() == Plaintext)
- determineDirectionality(direction, InlineIterator(this, bidiFirstSkippingEmptyInlines(this), 0));
- resolver.setStatus(BidiStatus(direction, isOverride(style()->unicodeBidi())));
- InlineIterator iter = InlineIterator(this, bidiFirstSkippingEmptyInlines(this, &resolver), 0);
+ TextDirection direction = style().direction();
+ if (style().unicodeBidi() == Plaintext)
+ determineDirectionality(direction, InlineIterator(this, bidiFirstSkippingEmptyInlines(*this), 0));
+ resolver.setStatus(BidiStatus(direction, isOverride(style().unicodeBidi())));
+ InlineIterator iter = InlineIterator(this, bidiFirstSkippingEmptyInlines(*this, &resolver), 0);
resolver.setPosition(iter, numberOfIsolateAncestors(iter));
}
return curr;
}
-void RenderBlock::determineEndPosition(LineLayoutState& layoutState, RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus)
+void RenderBlockFlow::determineEndPosition(LineLayoutState& layoutState, RootInlineBox* startLine, InlineIterator& cleanLineStart, BidiStatus& cleanLineBidiStatus)
{
ASSERT(!layoutState.endLine());
size_t floatIndex = layoutState.floatIndex();
@@ -2396,11 +1907,11 @@ void RenderBlock::determineEndPosition(LineLayoutState& layoutState, RootInlineB
layoutState.setEndLine(last);
}
-bool RenderBlock::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState)
+bool RenderBlockFlow::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState)
{
LayoutUnit lineDelta = logicalHeight() - layoutState.endLineLogicalTop();
- bool paginated = view()->layoutState() && view()->layoutState()->isPaginated();
+ bool paginated = view().layoutState() && view().layoutState()->isPaginated();
if (paginated && layoutState.flowThread()) {
// Check all lines from here to the end, and see if the hypothetical new position for the lines will result
// in a different available line width.
@@ -2409,8 +1920,9 @@ bool RenderBlock::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState
// 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()))
@@ -2422,7 +1934,7 @@ bool RenderBlock::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState
return true;
// See if any floats end in the range along which we want to shift the lines vertically.
- LayoutUnit logicalTop = min(logicalHeight(), layoutState.endLineLogicalTop());
+ LayoutUnit logicalTop = std::min(logicalHeight(), layoutState.endLineLogicalTop());
RootInlineBox* lastLine = layoutState.endLine();
while (RootInlineBox* nextLine = lastLine->nextRootBox())
@@ -2431,17 +1943,29 @@ bool RenderBlock::checkPaginationAndFloatsAtEndLine(LineLayoutState& layoutState
LayoutUnit logicalBottom = lastLine->lineBottomWithLeading() + absoluteValue(lineDelta);
const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
- FloatingObjectSetIterator end = floatingObjectSet.end();
- for (FloatingObjectSetIterator it = floatingObjectSet.begin(); it != end; ++it) {
- FloatingObject* f = *it;
- if (logicalBottomForFloat(f) >= logicalTop && logicalBottomForFloat(f) < logicalBottom)
+ auto end = floatingObjectSet.end();
+ for (auto it = floatingObjectSet.begin(); it != end; ++it) {
+ const auto& floatingObject = *it->get();
+ if (logicalBottomForFloat(floatingObject) >= logicalTop && logicalBottomForFloat(floatingObject) < logicalBottom)
return false;
}
return true;
}
-bool RenderBlock::matchedEndLine(LineLayoutState& layoutState, const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus)
+bool RenderBlockFlow::lineWidthForPaginatedLineChanged(RootInlineBox* rootBox, LayoutUnit lineDelta, RenderFlowThread* flowThread) const
+{
+ if (!flowThread)
+ return false;
+
+ RenderRegion* currentRegion = regionAtBlockOffset(rootBox->lineTopWithLeading() + lineDelta);
+ // Just bail if the region didn't change.
+ if (rootBox->containingRegion() == currentRegion)
+ return false;
+ return rootBox->paginatedLineWidth() != availableLogicalWidthForContent(currentRegion);
+}
+
+bool RenderBlockFlow::matchedEndLine(LineLayoutState& layoutState, const InlineBidiResolver& resolver, const InlineIterator& endLineStart, const BidiStatus& endLineStatus)
{
if (resolver.position() == endLineStart) {
if (resolver.status() != endLineStatus)
@@ -2451,11 +1975,11 @@ bool RenderBlock::matchedEndLine(LineLayoutState& layoutState, const InlineBidiR
// 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().m_obj && line->lineBreakPos() == resolver.position().m_pos) {
+ 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.
@@ -2469,7 +1993,7 @@ bool RenderBlock::matchedEndLine(LineLayoutState& layoutState, const InlineBidiR
}
// Now delete the lines that we failed to sync.
- deleteLineRange(layoutState, renderArena(), originalEndLine, result);
+ deleteLineRange(layoutState, originalEndLine, result);
return matched;
}
}
@@ -2477,80 +2001,6 @@ bool RenderBlock::matchedEndLine(LineLayoutState& layoutState, const InlineBidiR
return false;
}
-static inline bool skipNonBreakingSpace(const InlineIterator& it, const LineInfo& lineInfo)
-{
- if (it.m_obj->style()->nbspMode() != SPACE || it.current() != noBreakSpace)
- return false;
-
- // FIXME: This is bad. It makes nbsp inconsistent with space and won't work correctly
- // with m_minWidth/m_maxWidth.
- // Do not skip a non-breaking space if it is the first character
- // on a line after a clean line break (or on the first line, since previousLineBrokeCleanly starts off
- // |true|).
- if (lineInfo.isEmpty() && lineInfo.previousLineBrokeCleanly())
- return false;
-
- return true;
-}
-
-enum WhitespacePosition { LeadingWhitespace, TrailingWhitespace };
-static inline bool shouldCollapseWhiteSpace(const RenderStyle* style, const LineInfo& lineInfo, WhitespacePosition whitespacePosition)
-{
- // CSS2 16.6.1
- // If a space (U+0020) at the beginning of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is removed.
- // If a space (U+0020) at the end of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is also removed.
- // If spaces (U+0020) or tabs (U+0009) at the end of a line have 'white-space' set to 'pre-wrap', UAs may visually collapse them.
- return style->collapseWhiteSpace()
- || (whitespacePosition == TrailingWhitespace && style->whiteSpace() == PRE_WRAP && (!lineInfo.isEmpty() || !lineInfo.previousLineBrokeCleanly()));
-}
-
-static bool requiresLineBoxForContent(RenderInline* flow, const LineInfo& lineInfo)
-{
- RenderObject* parent = flow->parent();
- if (flow->document()->inNoQuirksMode()
- && (flow->style(lineInfo.isFirstLine())->lineHeight() != parent->style(lineInfo.isFirstLine())->lineHeight()
- || flow->style()->verticalAlign() != parent->style()->verticalAlign()
- || !parent->style()->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(flow->style()->font().fontMetrics())))
- return true;
- return false;
-}
-
-static bool hasInlineDirectionBordersPaddingOrMargin(RenderInline* flow)
-{
- // Where an empty inline is split across anonymous blocks we should only give lineboxes to the 'sides' of the
- // inline that have borders, padding or margin.
- bool shouldApplyStartBorderPaddingOrMargin = !flow->parent()->isAnonymousBlock() || !flow->isInlineElementContinuation();
- if (shouldApplyStartBorderPaddingOrMargin && (flow->borderStart() || flow->marginStart() || flow->paddingStart()))
- return true;
-
- bool shouldApplyEndBorderPaddingOrMargin = !flow->parent()->isAnonymousBlock() || flow->isInlineElementContinuation() || !flow->inlineElementContinuation();
- return shouldApplyEndBorderPaddingOrMargin && (flow->borderEnd() || flow->marginEnd() || flow->paddingEnd());
-}
-
-static bool alwaysRequiresLineBox(RenderObject* flow)
-{
- // FIXME: Right now, we only allow line boxes for inlines that are truly empty.
- // We need to fix this, though, because at the very least, inlines containing only
- // ignorable whitespace should should also have line boxes.
- return isEmptyInline(flow) && hasInlineDirectionBordersPaddingOrMargin(toRenderInline(flow));
-}
-
-static bool requiresLineBox(const InlineIterator& it, const LineInfo& lineInfo = LineInfo(), WhitespacePosition whitespacePosition = LeadingWhitespace)
-{
- if (it.m_obj->isFloatingOrOutOfFlowPositioned())
- return false;
-
- if (it.m_obj->isRenderInline() && !alwaysRequiresLineBox(it.m_obj) && !requiresLineBoxForContent(toRenderInline(it.m_obj), lineInfo))
- return false;
-
- if (!shouldCollapseWhiteSpace(it.m_obj->style(), lineInfo, whitespacePosition) || it.m_obj->isBR())
- return true;
-
- UChar current = it.current();
- bool notJustWhitespace = current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || it.m_obj->preservesNewline()) && !skipNonBreakingSpace(it, lineInfo);
- return notJustWhitespace || isEmptyInline(it.m_obj);
-}
-
bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj)
{
ASSERT(inlineObj->parent() == this);
@@ -2563,1059 +2013,90 @@ bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj)
return !it.atEnd();
}
-// FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building
-// line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned
-// elements quite right. In other words, we need to build this function's work into the normal line
-// object iteration process.
-// NB. this function will insert any floating elements that would otherwise
-// be skipped but it will not position them.
-void RenderBlock::LineBreaker::skipTrailingWhitespace(InlineIterator& iterator, const LineInfo& lineInfo)
-{
- while (!iterator.atEnd() && !requiresLineBox(iterator, lineInfo, TrailingWhitespace)) {
- RenderObject* object = iterator.m_obj;
- if (object->isOutOfFlowPositioned())
- setStaticPositions(m_block, toRenderBox(object));
- else if (object->isFloating())
- m_block->insertFloatingObject(toRenderBox(object));
- iterator.increment();
- }
-}
-
-void RenderBlock::LineBreaker::skipLeadingWhitespace(InlineBidiResolver& resolver, LineInfo& lineInfo,
- FloatingObject* lastFloatFromPreviousLine, LineWidth& width)
+void RenderBlockFlow::addOverflowFromInlineChildren()
{
- while (!resolver.position().atEnd() && !requiresLineBox(resolver.position(), lineInfo, LeadingWhitespace)) {
- RenderObject* object = resolver.position().m_obj;
- if (object->isOutOfFlowPositioned()) {
- setStaticPositions(m_block, toRenderBox(object));
- if (object->style()->isOriginalDisplayInlineType()) {
- resolver.runs().addRun(createRun(0, 1, object, resolver));
- lineInfo.incrementRunsFromLeadingWhitespace();
- }
- } else if (object->isFloating()) {
- // The top margin edge of a self-collapsing block that clears a float intrudes up into it by the height of the margin,
- // so in order to place this first child float at the top content edge of the self-collapsing block add the margin back in before placement.
- LayoutUnit marginOffset = (!object->previousSibling() && m_block->isSelfCollapsingBlock() && m_block->style()->clear() && m_block->getClearDelta(m_block, LayoutUnit())) ? m_block->collapsedMarginBeforeForChild(m_block) : LayoutUnit();
- LayoutUnit oldLogicalHeight = m_block->logicalHeight();
- m_block->setLogicalHeight(oldLogicalHeight + marginOffset);
- m_block->positionNewFloatOnLine(m_block->insertFloatingObject(toRenderBox(object)), lastFloatFromPreviousLine, lineInfo, width);
- m_block->setLogicalHeight(oldLogicalHeight);
- } else if (object->isText() && object->style()->hasTextCombine() && object->isCombineText() && !toRenderCombineText(object)->isCombined()) {
- toRenderCombineText(object)->combineText();
- if (toRenderCombineText(object)->isCombined())
- continue;
- }
- resolver.increment();
- }
- resolver.commitExplicitEmbedding();
-}
-
-// This is currently just used for list markers and inline flows that have line boxes. Neither should
-// have an effect on whitespace at the start of the line.
-static bool shouldSkipWhitespaceAfterStartObject(RenderBlock* block, RenderObject* o, LineMidpointState& lineMidpointState)
-{
- RenderObject* next = bidiNextSkippingEmptyInlines(block, o);
- while (next && next->isFloatingOrOutOfFlowPositioned())
- next = bidiNextSkippingEmptyInlines(block, next);
-
- if (next && !next->isBR() && next->isText() && toRenderText(next)->textLength() > 0) {
- RenderText* nextText = toRenderText(next);
- UChar nextChar = nextText->characterAt(0);
- if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) {
- startIgnoringSpaces(lineMidpointState, InlineIterator(0, o, 0));
- return true;
- }
- }
-
- return false;
-}
-
-static ALWAYS_INLINE float textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, float xPos, bool isFixedPitch, bool collapseWhiteSpace, HashSet<const SimpleFontData*>& fallbackFonts, TextLayout* layout = 0)
-{
- GlyphOverflow glyphOverflow;
- if (isFixedPitch || (!from && len == text->textLength()) || text->style()->hasTextCombine())
- return text->width(from, len, font, xPos, &fallbackFonts, &glyphOverflow);
-
- if (layout)
- return Font::width(*layout, from, len, &fallbackFonts);
-
- TextRun run = RenderBlock::constructTextRun(text, font, text, from, len, text->style());
- run.setCharactersLength(text->textLength() - from);
- ASSERT(run.charactersLength() >= run.length());
-
- run.setCharacterScanForCodePath(!text->canUseSimpleFontCodePath());
- run.setTabSize(!collapseWhiteSpace, text->style()->tabSize());
- run.setXPos(xPos);
- return font.width(run, &fallbackFonts, &glyphOverflow);
-}
-
-static void tryHyphenating(RenderText* text, const Font& font, const AtomicString& localeIdentifier, unsigned consecutiveHyphenatedLines, int consecutiveHyphenatedLinesLimit, int minimumPrefixLimit, int minimumSuffixLimit, unsigned lastSpace, unsigned pos, float xPos, int availableWidth, bool isFixedPitch, bool collapseWhiteSpace, int lastSpaceWordSpacing, InlineIterator& lineBreak, int nextBreakable, bool& hyphenated)
-{
- // Map 'hyphenate-limit-{before,after}: auto;' to 2.
- unsigned minimumPrefixLength;
- unsigned minimumSuffixLength;
-
- if (minimumPrefixLimit < 0)
- minimumPrefixLength = 2;
- else
- minimumPrefixLength = static_cast<unsigned>(minimumPrefixLimit);
-
- if (minimumSuffixLimit < 0)
- minimumSuffixLength = 2;
- else
- minimumSuffixLength = static_cast<unsigned>(minimumSuffixLimit);
-
- if (pos - lastSpace <= minimumSuffixLength)
- return;
-
- if (consecutiveHyphenatedLinesLimit >= 0 && consecutiveHyphenatedLines >= static_cast<unsigned>(consecutiveHyphenatedLinesLimit))
- return;
-
- int hyphenWidth = measureHyphenWidth(text, font);
-
- float maxPrefixWidth = availableWidth - xPos - hyphenWidth - lastSpaceWordSpacing;
- // If the maximum width available for the prefix before the hyphen is small, then it is very unlikely
- // that an hyphenation opportunity exists, so do not bother to look for it.
- if (maxPrefixWidth <= font.pixelSize() * 5 / 4)
- return;
-
- TextRun run = RenderBlock::constructTextRun(text, font, text, lastSpace, pos - lastSpace, text->style());
- run.setCharactersLength(text->textLength() - lastSpace);
- ASSERT(run.charactersLength() >= run.length());
-
- run.setTabSize(!collapseWhiteSpace, text->style()->tabSize());
- run.setXPos(xPos + lastSpaceWordSpacing);
-
- unsigned prefixLength = font.offsetForPosition(run, maxPrefixWidth, false);
- if (prefixLength < minimumPrefixLength)
- return;
-
- prefixLength = lastHyphenLocation(text->characters() + lastSpace, pos - lastSpace, min(prefixLength, pos - lastSpace - minimumSuffixLength) + 1, localeIdentifier);
- if (!prefixLength || prefixLength < minimumPrefixLength)
- return;
-
- // When lastSapce is a space, which it always is except sometimes at the beginning of a line or after collapsed
- // space, it should not count towards hyphenate-limit-before.
- if (prefixLength == minimumPrefixLength) {
- UChar characterAtLastSpace = text->characterAt(lastSpace);
- if (characterAtLastSpace == ' ' || characterAtLastSpace == '\n' || characterAtLastSpace == '\t' || characterAtLastSpace == noBreakSpace)
- return;
- }
-
- ASSERT(pos - lastSpace - prefixLength >= minimumSuffixLength);
-
-#if !ASSERT_DISABLED
- HashSet<const SimpleFontData*> fallbackFonts;
- float prefixWidth = hyphenWidth + textWidth(text, lastSpace, prefixLength, font, xPos, isFixedPitch, collapseWhiteSpace, fallbackFonts) + lastSpaceWordSpacing;
- ASSERT(xPos + prefixWidth <= availableWidth);
-#else
- UNUSED_PARAM(isFixedPitch);
-#endif
-
- lineBreak.moveTo(text, lastSpace + prefixLength, nextBreakable);
- hyphenated = true;
-}
-
-class TrailingObjects {
-public:
- TrailingObjects();
- void setTrailingWhitespace(RenderText*);
- void clear();
- void appendBoxIfNeeded(RenderBoxModelObject*);
-
- enum CollapseFirstSpaceOrNot { DoNotCollapseFirstSpace, CollapseFirstSpace };
-
- void updateMidpointsForTrailingBoxes(LineMidpointState&, const InlineIterator& lBreak, CollapseFirstSpaceOrNot);
-
-private:
- RenderText* m_whitespace;
- Vector<RenderBoxModelObject*, 4> m_boxes;
-};
-
-TrailingObjects::TrailingObjects()
- : m_whitespace(0)
-{
-}
-
-inline void TrailingObjects::setTrailingWhitespace(RenderText* whitespace)
-{
- ASSERT(whitespace);
- m_whitespace = whitespace;
-}
-
-inline void TrailingObjects::clear()
-{
- m_whitespace = 0;
- m_boxes.shrink(0); // Use shrink(0) instead of clear() to retain our capacity.
-}
-
-inline void TrailingObjects::appendBoxIfNeeded(RenderBoxModelObject* box)
-{
- if (m_whitespace)
- m_boxes.append(box);
-}
-
-void TrailingObjects::updateMidpointsForTrailingBoxes(LineMidpointState& lineMidpointState, const InlineIterator& lBreak, CollapseFirstSpaceOrNot collapseFirstSpace)
-{
- if (!m_whitespace)
+ if (auto layout = simpleLineLayout()) {
+ ASSERT(!hasOverflowClip());
+ SimpleLineLayout::collectFlowOverflow(*this, *layout);
return;
-
- // This object is either going to be part of the last midpoint, or it is going to be the actual endpoint.
- // In both cases we just decrease our pos by 1 level to exclude the space, allowing it to - in effect - collapse into the newline.
- if (lineMidpointState.numMidpoints % 2) {
- // Find the trailing space object's midpoint.
- int trailingSpaceMidpoint = lineMidpointState.numMidpoints - 1;
- for ( ; trailingSpaceMidpoint > 0 && lineMidpointState.midpoints[trailingSpaceMidpoint].m_obj != m_whitespace; --trailingSpaceMidpoint) { }
- ASSERT(trailingSpaceMidpoint >= 0);
- if (collapseFirstSpace == CollapseFirstSpace)
- lineMidpointState.midpoints[trailingSpaceMidpoint].m_pos--;
-
- // Now make sure every single trailingPositionedBox following the trailingSpaceMidpoint properly stops and starts
- // ignoring spaces.
- size_t currentMidpoint = trailingSpaceMidpoint + 1;
- for (size_t i = 0; i < m_boxes.size(); ++i) {
- if (currentMidpoint >= lineMidpointState.numMidpoints) {
- // We don't have a midpoint for this box yet.
- ensureLineBoxInsideIgnoredSpaces(lineMidpointState, m_boxes[i]);
- } else {
- ASSERT(lineMidpointState.midpoints[currentMidpoint].m_obj == m_boxes[i]);
- ASSERT(lineMidpointState.midpoints[currentMidpoint + 1].m_obj == m_boxes[i]);
- }
- currentMidpoint += 2;
- }
- } else if (!lBreak.m_obj) {
- ASSERT(m_whitespace->isText());
- ASSERT(collapseFirstSpace == CollapseFirstSpace);
- // Add a new end midpoint that stops right at the very end.
- unsigned length = m_whitespace->textLength();
- unsigned pos = length >= 2 ? length - 2 : UINT_MAX;
- InlineIterator endMid(0, m_whitespace, pos);
- startIgnoringSpaces(lineMidpointState, endMid);
- for (size_t i = 0; i < m_boxes.size(); ++i) {
- ensureLineBoxInsideIgnoredSpaces(lineMidpointState, m_boxes[i]);
- }
- }
-}
-
-void RenderBlock::LineBreaker::reset()
-{
- m_positionedObjects.clear();
- m_hyphenated = false;
- m_clear = CNONE;
-}
-
-InlineIterator RenderBlock::LineBreaker::nextLineBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements& wordMeasurements)
-{
-#if !ENABLE(CSS_SHAPES)
- return nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
-#else
- ShapeInsideInfo* shapeInsideInfo = m_block->layoutShapeInsideInfo();
-
- if (!shapeInsideInfo || !shapeInsideInfo->lineOverlapsShapeBounds())
- return nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
-
- InlineIterator end = resolver.position();
- InlineIterator oldEnd = end;
-
- if (!shapeInsideInfo->hasSegments()) {
- end = nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
- resolver.setPositionIgnoringNestedIsolates(oldEnd);
- return oldEnd;
- }
-
- const SegmentList& segments = shapeInsideInfo->segments();
- SegmentRangeList& segmentRanges = shapeInsideInfo->segmentRanges();
-
- for (unsigned i = 0; i < segments.size() && !end.atEnd(); i++) {
- InlineIterator segmentStart = resolver.position();
- end = nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
-
- ASSERT(segmentRanges.size() == i);
- if (resolver.position().atEnd()) {
- segmentRanges.append(LineSegmentRange(segmentStart, end));
- break;
- }
- if (resolver.position() == end) {
- // Nothing fit this segment
- end = segmentStart;
- segmentRanges.append(LineSegmentRange(segmentStart, segmentStart));
- resolver.setPositionIgnoringNestedIsolates(segmentStart);
- } else {
- // Note that resolver.position is already skipping some of the white space at the beginning of the line,
- // so that's why segmentStart might be different than resolver.position().
- LineSegmentRange range(resolver.position(), end);
- segmentRanges.append(range);
- resolver.setPosition(end, numberOfIsolateAncestors(end));
-
- if (lineInfo.previousLineBrokeCleanly()) {
- // If we hit a new line break, just stop adding anything to this line.
- break;
- }
- }
- }
- resolver.setPositionIgnoringNestedIsolates(oldEnd);
- return end;
-#endif
-}
-
-static inline bool iteratorIsBeyondEndOfRenderCombineText(const InlineIterator& iter, RenderCombineText* renderer)
-{
- return iter.m_obj == renderer && iter.m_pos >= renderer->textLength();
-}
-
-static inline void commitLineBreakAtCurrentWidth(LineWidth& width, InlineIterator& lBreak, RenderObject* object, unsigned offset = 0, int nextBreak = -1)
-{
- width.commit();
- lBreak.moveTo(object, offset, nextBreak);
-}
-
-static bool textBeginsWithBreakablePosition(RenderObject* next)
-{
- ASSERT(next->isText());
- RenderText* nextText = toRenderText(next);
- if (nextText->isWordBreak())
- return true;
- if (!nextText->textLength())
- return false;
- UChar c = nextText->characterAt(0);
- return c == ' ' || c == '\t' || (c == '\n' && !nextText->preservesNewline());
-}
-
-static bool canBreakAtThisPosition(bool autoWrap, LineWidth& width, InlineIterator& lBreak, RenderObject* next, const InlineIterator& current, EWhiteSpace currWS, bool currentCharacterIsSpace, bool autoWrapWasEverTrueOnLine)
-{
- // If we are no-wrap and have found a line-breaking opportunity already then we should take it.
- if (width.committedWidth() && !width.fitsOnLine(currentCharacterIsSpace) && currWS == NOWRAP)
- return true;
-
- // Avoid breaking before empty inlines.
- if (next && isEmptyInline(next))
- return false;
-
- // Return early if we autowrap and the current character is a space as we will always want to break at such a position.
- if (autoWrap && currentCharacterIsSpace)
- return true;
-
- bool nextIsText = (next && (current.m_obj->isText() || isEmptyInline(current.m_obj)) && next->isText() && !next->isBR() && (autoWrap || next->style()->autoWrap()));
- if (!nextIsText)
- return autoWrap;
-
- bool canBreakHere = !currentCharacterIsSpace && textBeginsWithBreakablePosition(next);
-
- // See if attempting to fit below floats creates more available width on the line.
- if (!width.fitsOnLine() && !width.committedWidth())
- width.fitBelowFloats();
-
- bool canPlaceOnLine = width.fitsOnLine() || !autoWrapWasEverTrueOnLine;
-
- if (canPlaceOnLine && canBreakHere)
- commitLineBreakAtCurrentWidth(width, lBreak, next);
-
- return canBreakHere;
-}
-
-InlineIterator RenderBlock::LineBreaker::nextSegmentBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements& wordMeasurements)
-{
- reset();
-
- ASSERT(resolver.position().root() == m_block);
-
- bool appliedStartWidth = resolver.position().m_pos > 0;
- bool includeEndWidth = true;
- LineMidpointState& lineMidpointState = resolver.midpointState();
-
- LineWidth width(m_block, lineInfo.isFirstLine(), requiresIndent(lineInfo.isFirstLine(), lineInfo.previousLineBrokeCleanly(), m_block->style()));
-
- skipLeadingWhitespace(resolver, lineInfo, lastFloatFromPreviousLine, width);
-
- if (resolver.position().atEnd())
- return resolver.position();
-
- // This variable is used only if whitespace isn't set to PRE, and it tells us whether
- // or not we are currently ignoring whitespace.
- bool ignoringSpaces = false;
- InlineIterator ignoreStart;
-
- // This variable tracks whether the very last character we saw was a space. We use
- // this to detect when we encounter a second space so we know we have to terminate
- // a run.
- bool currentCharacterIsSpace = false;
- bool currentCharacterIsWS = false;
- TrailingObjects trailingObjects;
-
- InlineIterator lBreak = resolver.position();
-
- // FIXME: It is error-prone to split the position object out like this.
- // Teach this code to work with objects instead of this split tuple.
- InlineIterator current = resolver.position();
- RenderObject* last = current.m_obj;
- bool atStart = true;
-
- bool startingNewParagraph = lineInfo.previousLineBrokeCleanly();
- lineInfo.setPreviousLineBrokeCleanly(false);
-
- bool autoWrapWasEverTrueOnLine = false;
- bool floatsFitOnLine = true;
-
- // Firefox and Opera will allow a table cell to grow to fit an image inside it under
- // very specific circumstances (in order to match common WinIE renderings).
- // Not supporting the quirk has caused us to mis-render some real sites. (See Bugzilla 10517.)
- RenderStyle* blockStyle = m_block->style();
- bool allowImagesToBreak = !m_block->document()->inQuirksMode() || !m_block->isTableCell() || !blockStyle->logicalWidth().isIntrinsicOrAuto();
-
- EWhiteSpace currWS = blockStyle->whiteSpace();
- EWhiteSpace lastWS = currWS;
- while (current.m_obj) {
- RenderStyle* currentStyle = current.m_obj->style();
- RenderObject* next = bidiNextSkippingEmptyInlines(m_block, current.m_obj);
- if (next && next->parent() && !next->parent()->isDescendantOf(current.m_obj->parent()))
- includeEndWidth = true;
-
- currWS = current.m_obj->isReplaced() ? current.m_obj->parent()->style()->whiteSpace() : currentStyle->whiteSpace();
- lastWS = last->isReplaced() ? last->parent()->style()->whiteSpace() : last->style()->whiteSpace();
-
- bool autoWrap = RenderStyle::autoWrap(currWS);
- autoWrapWasEverTrueOnLine = autoWrapWasEverTrueOnLine || autoWrap;
-
-#if ENABLE(SVG)
- bool preserveNewline = current.m_obj->isSVGInlineText() ? false : RenderStyle::preserveNewline(currWS);
-#else
- bool preserveNewline = RenderStyle::preserveNewline(currWS);
-#endif
-
- bool collapseWhiteSpace = RenderStyle::collapseWhiteSpace(currWS);
-
- if (current.m_obj->isBR()) {
- if (width.fitsOnLine()) {
- lBreak.moveToStartOf(current.m_obj);
- lBreak.increment();
-
- // A <br> always breaks a line, so don't let the line be collapsed
- // away. Also, the space at the end of a line with a <br> does not
- // get collapsed away. It only does this if the previous line broke
- // cleanly. Otherwise the <br> has no effect on whether the line is
- // empty or not.
- if (startingNewParagraph)
- lineInfo.setEmpty(false, m_block, &width);
- trailingObjects.clear();
- lineInfo.setPreviousLineBrokeCleanly(true);
-
- // A <br> with clearance always needs a linebox in case the lines below it get dirtied later and
- // need to check for floats to clear - so if we're ignoring spaces, stop ignoring them and add a
- // run for this object.
- if (ignoringSpaces && currentStyle->clear() != CNONE)
- ensureLineBoxInsideIgnoredSpaces(lineMidpointState, current.m_obj);
-
- if (!lineInfo.isEmpty())
- m_clear = currentStyle->clear();
- }
- goto end;
- }
-
- if (current.m_obj->isOutOfFlowPositioned()) {
- // If our original display wasn't an inline type, then we can
- // go ahead and determine our static inline position now.
- RenderBox* box = toRenderBox(current.m_obj);
- bool isInlineType = box->style()->isOriginalDisplayInlineType();
- if (!isInlineType)
- m_block->setStaticInlinePositionForChild(box, m_block->logicalHeight(), m_block->startOffsetForContent(m_block->logicalHeight()));
- else {
- // If our original display was an INLINE type, then we can go ahead
- // and determine our static y position now.
- box->layer()->setStaticBlockPosition(m_block->logicalHeight());
- }
-
- // If we're ignoring spaces, we have to stop and include this object and
- // then start ignoring spaces again.
- if (isInlineType || current.m_obj->container()->isRenderInline()) {
- if (ignoringSpaces)
- ensureLineBoxInsideIgnoredSpaces(lineMidpointState, current.m_obj);
- trailingObjects.appendBoxIfNeeded(box);
- } else
- m_positionedObjects.append(box);
- width.addUncommittedWidth(inlineLogicalWidth(current.m_obj));
- // Reset prior line break context characters.
- renderTextInfo.m_lineBreakIterator.resetPriorContext();
- } else if (current.m_obj->isFloating()) {
- RenderBox* floatBox = toRenderBox(current.m_obj);
- FloatingObject* f = m_block->insertFloatingObject(floatBox);
- // check if it fits in the current line.
- // If it does, position it now, otherwise, position
- // it after moving to next line (in newLine() func)
- // FIXME: Bug 110372: Properly position multiple stacked floats with non-rectangular shape outside.
- if (floatsFitOnLine && width.fitsOnLineExcludingTrailingWhitespace(m_block->logicalWidthForFloat(f))) {
- m_block->positionNewFloatOnLine(f, lastFloatFromPreviousLine, lineInfo, width);
- if (lBreak.m_obj == current.m_obj) {
- ASSERT(!lBreak.m_pos);
- lBreak.increment();
- }
- } else
- floatsFitOnLine = false;
- // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for floating element.
- renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
- } else if (current.m_obj->isRenderInline()) {
- // Right now, we should only encounter empty inlines here.
- ASSERT(isEmptyInline(current.m_obj));
-
- RenderInline* flowBox = toRenderInline(current.m_obj);
-
- // Now that some inline flows have line boxes, if we are already ignoring spaces, we need
- // to make sure that we stop to include this object and then start ignoring spaces again.
- // If this object is at the start of the line, we need to behave like list markers and
- // start ignoring spaces.
- bool requiresLineBox = alwaysRequiresLineBox(current.m_obj);
- if (requiresLineBox || requiresLineBoxForContent(flowBox, lineInfo)) {
- // An empty inline that only has line-height, vertical-align or font-metrics will only get a
- // line box to affect the height of the line if the rest of the line is not empty.
- if (requiresLineBox)
- lineInfo.setEmpty(false, m_block, &width);
- if (ignoringSpaces) {
- trailingObjects.clear();
- ensureLineBoxInsideIgnoredSpaces(lineMidpointState, current.m_obj);
- } else if (blockStyle->collapseWhiteSpace() && resolver.position().m_obj == current.m_obj
- && shouldSkipWhitespaceAfterStartObject(m_block, current.m_obj, lineMidpointState)) {
- // Like with list markers, we start ignoring spaces to make sure that any
- // additional spaces we see will be discarded.
- currentCharacterIsSpace = true;
- currentCharacterIsWS = true;
- ignoringSpaces = true;
- } else {
- trailingObjects.appendBoxIfNeeded(flowBox);
- }
- }
-
- width.addUncommittedWidth(inlineLogicalWidth(current.m_obj) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox));
- } else if (current.m_obj->isReplaced()) {
- RenderBox* replacedBox = toRenderBox(current.m_obj);
-
- if (atStart)
- width.updateAvailableWidth(replacedBox->logicalHeight());
-
- // Break on replaced elements if either has normal white-space.
- if ((autoWrap || RenderStyle::autoWrap(lastWS)) && (!current.m_obj->isImage() || allowImagesToBreak))
- commitLineBreakAtCurrentWidth(width, lBreak, current.m_obj);
-
- if (ignoringSpaces)
- stopIgnoringSpaces(lineMidpointState, InlineIterator(0, current.m_obj, 0));
-
- lineInfo.setEmpty(false, m_block, &width);
- ignoringSpaces = false;
- currentCharacterIsSpace = false;
- currentCharacterIsWS = false;
- trailingObjects.clear();
-
- // Optimize for a common case. If we can't find whitespace after the list
- // item, then this is all moot.
- LayoutUnit replacedLogicalWidth = m_block->logicalWidthForChild(replacedBox) + m_block->marginStartForChild(replacedBox) + m_block->marginEndForChild(replacedBox) + inlineLogicalWidth(current.m_obj);
- if (current.m_obj->isListMarker()) {
- if (blockStyle->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(m_block, current.m_obj, lineMidpointState)) {
- // Like with inline flows, we start ignoring spaces to make sure that any
- // additional spaces we see will be discarded.
- currentCharacterIsSpace = true;
- currentCharacterIsWS = true;
- ignoringSpaces = true;
- }
- if (toRenderListMarker(current.m_obj)->isInside())
- width.addUncommittedWidth(replacedLogicalWidth);
- } else
- width.addUncommittedWidth(replacedLogicalWidth);
- if (current.m_obj->isRubyRun())
- width.applyOverhang(toRenderRubyRun(current.m_obj), last, next);
- // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for replaced element.
- renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
- } else if (current.m_obj->isText()) {
- if (!current.m_pos)
- appliedStartWidth = false;
-
- RenderText* t = toRenderText(current.m_obj);
-
-#if ENABLE(SVG)
- bool isSVGText = t->isSVGInlineText();
-#endif
-
- // If we have left a no-wrap inline and entered an autowrap inline while ignoring spaces
- // then we need to mark the start of the autowrap inline as a potential linebreak now.
- if (autoWrap && !RenderStyle::autoWrap(lastWS) && ignoringSpaces)
- commitLineBreakAtCurrentWidth(width, lBreak, current.m_obj);
-
- if (t->style()->hasTextCombine() && current.m_obj->isCombineText() && !toRenderCombineText(current.m_obj)->isCombined()) {
- RenderCombineText* combineRenderer = toRenderCombineText(current.m_obj);
- combineRenderer->combineText();
- // The length of the renderer's text may have changed. Increment stale iterator positions
- if (iteratorIsBeyondEndOfRenderCombineText(lBreak, combineRenderer)) {
- ASSERT(iteratorIsBeyondEndOfRenderCombineText(resolver.position(), combineRenderer));
- lBreak.increment();
- resolver.increment();
- }
- }
-
- RenderStyle* style = t->style(lineInfo.isFirstLine());
- const Font& f = style->font();
- bool isFixedPitch = f.isFixedPitch();
- bool canHyphenate = style->hyphens() == HyphensAuto && WebCore::canHyphenate(style->locale());
-
- unsigned lastSpace = current.m_pos;
- float wordSpacing = currentStyle->wordSpacing();
- float lastSpaceWordSpacing = 0;
- float wordSpacingForWordMeasurement = 0;
-
- float wrapW = width.uncommittedWidth() + inlineLogicalWidth(current.m_obj, !appliedStartWidth, true);
- float charWidth = 0;
- bool breakNBSP = autoWrap && currentStyle->nbspMode() == SPACE;
- // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word,
- // which is only possible if the word is the first thing on the line, that is, if |w| is zero.
- bool breakWords = currentStyle->breakWords() && ((autoWrap && !width.committedWidth()) || currWS == PRE);
- bool midWordBreak = false;
- bool breakAll = currentStyle->wordBreak() == BreakAllWordBreak && autoWrap;
- float hyphenWidth = 0;
-#if ENABLE(SVG)
- if (isSVGText) {
- breakWords = false;
- breakAll = false;
- }
-#endif
-
- if (t->isWordBreak()) {
- commitLineBreakAtCurrentWidth(width, lBreak, current.m_obj);
- ASSERT(current.m_pos == t->textLength());
- }
-
- if (renderTextInfo.m_text != t) {
- updateCounterIfNeeded(t);
- renderTextInfo.m_text = t;
- renderTextInfo.m_font = &f;
- renderTextInfo.m_layout = f.createLayout(t, width.currentWidth(), collapseWhiteSpace);
- renderTextInfo.m_lineBreakIterator.resetStringAndReleaseIterator(t->text(), style->locale());
- } else if (renderTextInfo.m_layout && renderTextInfo.m_font != &f) {
- renderTextInfo.m_font = &f;
- renderTextInfo.m_layout = f.createLayout(t, width.currentWidth(), collapseWhiteSpace);
- }
-
- TextLayout* textLayout = renderTextInfo.m_layout.get();
-
- // Non-zero only when kerning is enabled and TextLayout isn't used, in which case we measure
- // words with their trailing space, then subtract its width.
- HashSet<const SimpleFontData*> fallbackFonts;
- float wordTrailingSpaceWidth = (f.typesettingFeatures() & Kerning) && !textLayout ? f.width(constructTextRun(t, f, &space, 1, style), &fallbackFonts) + wordSpacing : 0;
-
- UChar lastCharacter = renderTextInfo.m_lineBreakIterator.lastCharacter();
- UChar secondToLastCharacter = renderTextInfo.m_lineBreakIterator.secondToLastCharacter();
- for (; current.m_pos < t->textLength(); current.fastIncrementInTextNode()) {
- bool previousCharacterIsSpace = currentCharacterIsSpace;
- bool previousCharacterIsWS = currentCharacterIsWS;
- UChar c = current.current();
- currentCharacterIsSpace = c == ' ' || c == '\t' || (!preserveNewline && (c == '\n'));
-
- if (!collapseWhiteSpace || !currentCharacterIsSpace)
- lineInfo.setEmpty(false, m_block, &width);
-
- if (c == softHyphen && autoWrap && !hyphenWidth && style->hyphens() != HyphensNone) {
- hyphenWidth = measureHyphenWidth(t, f, &fallbackFonts);
- width.addUncommittedWidth(hyphenWidth);
- }
-
- bool applyWordSpacing = false;
-
- currentCharacterIsWS = currentCharacterIsSpace || (breakNBSP && c == noBreakSpace);
-
- if ((breakAll || breakWords) && !midWordBreak) {
- wrapW += charWidth;
- bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && current.m_pos + 1 < t->textLength() && U16_IS_TRAIL(t->characters()[current.m_pos + 1]);
- charWidth = textWidth(t, current.m_pos, midWordBreakIsBeforeSurrogatePair ? 2 : 1, f, width.committedWidth() + wrapW, isFixedPitch, collapseWhiteSpace, fallbackFonts, textLayout);
- midWordBreak = width.committedWidth() + wrapW + charWidth > width.availableWidth();
- }
-
- bool betweenWords = c == '\n' || (currWS != PRE && !atStart && isBreakable(renderTextInfo.m_lineBreakIterator, current.m_pos, current.m_nextBreakablePosition, breakNBSP)
- && (style->hyphens() != HyphensNone || (current.previousInSameNode() != softHyphen)));
-
- if (betweenWords || midWordBreak) {
- bool stoppedIgnoringSpaces = false;
- if (ignoringSpaces) {
- lastSpaceWordSpacing = 0;
- if (!currentCharacterIsSpace) {
- // Stop ignoring spaces and begin at this
- // new point.
- ignoringSpaces = false;
- wordSpacingForWordMeasurement = 0;
- lastSpace = current.m_pos; // e.g., "Foo goo", don't add in any of the ignored spaces.
- stopIgnoringSpaces(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos));
- stoppedIgnoringSpaces = true;
- } else {
- // Just keep ignoring these spaces.
- goto nextCharacter;
- }
- }
-
- wordMeasurements.grow(wordMeasurements.size() + 1);
- WordMeasurement& wordMeasurement = wordMeasurements.last();
-
- wordMeasurement.renderer = t;
- wordMeasurement.endOffset = current.m_pos;
- wordMeasurement.startOffset = lastSpace;
-
- float additionalTempWidth;
- if (wordTrailingSpaceWidth && c == ' ')
- additionalTempWidth = textWidth(t, lastSpace, current.m_pos + 1 - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout) - wordTrailingSpaceWidth;
- else
- additionalTempWidth = textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout);
-
- if (wordMeasurement.fallbackFonts.isEmpty() && !fallbackFonts.isEmpty())
- wordMeasurement.fallbackFonts.swap(fallbackFonts);
- fallbackFonts.clear();
-
- wordMeasurement.width = additionalTempWidth + wordSpacingForWordMeasurement;
- additionalTempWidth += lastSpaceWordSpacing;
- width.addUncommittedWidth(additionalTempWidth);
-
- if (collapseWhiteSpace && previousCharacterIsSpace && currentCharacterIsSpace && additionalTempWidth)
- width.setTrailingWhitespaceWidth(additionalTempWidth);
-
- if (!appliedStartWidth) {
- width.addUncommittedWidth(inlineLogicalWidth(current.m_obj, true, false));
- appliedStartWidth = true;
- }
-
- applyWordSpacing = wordSpacing && currentCharacterIsSpace;
-
- if (!width.committedWidth() && autoWrap && !width.fitsOnLine())
- width.fitBelowFloats();
-
- if (autoWrap || breakWords) {
- // If we break only after white-space, consider the current character
- // as candidate width for this line.
- bool lineWasTooWide = false;
- if (width.fitsOnLine() && currentCharacterIsWS && currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) {
- float charWidth = textWidth(t, current.m_pos, 1, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout) + (applyWordSpacing ? wordSpacing : 0);
- // Check if line is too big even without the extra space
- // at the end of the line. If it is not, do nothing.
- // If the line needs the extra whitespace to be too long,
- // then move the line break to the space and skip all
- // additional whitespace.
- if (!width.fitsOnLineIncludingExtraWidth(charWidth)) {
- lineWasTooWide = true;
- lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition);
- skipTrailingWhitespace(lBreak, lineInfo);
- }
- }
- if (lineWasTooWide || !width.fitsOnLine()) {
- if (canHyphenate && !width.fitsOnLine()) {
- tryHyphenating(t, f, style->locale(), consecutiveHyphenatedLines, blockStyle->hyphenationLimitLines(), style->hyphenationLimitBefore(), style->hyphenationLimitAfter(), lastSpace, current.m_pos, width.currentWidth() - additionalTempWidth, width.availableWidth(), isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, current.m_nextBreakablePosition, m_hyphenated);
- if (m_hyphenated)
- goto end;
- }
- if (lBreak.atTextParagraphSeparator()) {
- if (!stoppedIgnoringSpaces && current.m_pos > 0)
- ensureCharacterGetsLineBox(lineMidpointState, current);
- lBreak.increment();
- lineInfo.setPreviousLineBrokeCleanly(true);
- wordMeasurement.endOffset = lBreak.m_pos;
- }
- if (lBreak.m_obj && lBreak.m_pos && lBreak.m_obj->isText() && toRenderText(lBreak.m_obj)->textLength() && toRenderText(lBreak.m_obj)->characterAt(lBreak.m_pos - 1) == softHyphen && style->hyphens() != HyphensNone)
- m_hyphenated = true;
- if (lBreak.m_pos && lBreak.m_pos != (unsigned)wordMeasurement.endOffset && !wordMeasurement.width) {
- if (charWidth) {
- wordMeasurement.endOffset = lBreak.m_pos;
- wordMeasurement.width = charWidth;
- }
- }
- // Didn't fit. Jump to the end unless there's still an opportunity to collapse whitespace.
- if (ignoringSpaces || !collapseWhiteSpace || !currentCharacterIsSpace || !previousCharacterIsSpace)
- goto end;
- } else {
- if (!betweenWords || (midWordBreak && !autoWrap))
- width.addUncommittedWidth(-additionalTempWidth);
- if (hyphenWidth) {
- // Subtract the width of the soft hyphen out since we fit on a line.
- width.addUncommittedWidth(-hyphenWidth);
- hyphenWidth = 0;
- }
- }
- }
-
- if (c == '\n' && preserveNewline) {
- if (!stoppedIgnoringSpaces && current.m_pos > 0)
- ensureCharacterGetsLineBox(lineMidpointState, current);
- lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition);
- lBreak.increment();
- lineInfo.setPreviousLineBrokeCleanly(true);
- return lBreak;
- }
-
- if (autoWrap && betweenWords) {
- wrapW = 0;
- commitLineBreakAtCurrentWidth(width, lBreak, current.m_obj, current.m_pos, current.m_nextBreakablePosition);
- // Auto-wrapping text should not wrap in the middle of a word once it has had an
- // opportunity to break after a word.
- breakWords = false;
- }
-
- if (midWordBreak && !U16_IS_TRAIL(c) && !(category(c) & (Mark_NonSpacing | Mark_Enclosing | Mark_SpacingCombining))) {
- // Remember this as a breakable position in case
- // adding the end width forces a break.
- lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition);
- midWordBreak &= (breakWords || breakAll);
- }
-
- if (betweenWords) {
- lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
- wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0;
- lastSpace = current.m_pos;
- }
-
- if (!ignoringSpaces && currentStyle->collapseWhiteSpace()) {
- // If we encounter a newline, or if we encounter a
- // second space, we need to go ahead and break up this
- // run and enter a mode where we start collapsing spaces.
- if (currentCharacterIsSpace && previousCharacterIsSpace) {
- ignoringSpaces = true;
-
- // We just entered a mode where we are ignoring
- // spaces. Create a midpoint to terminate the run
- // before the second space.
- startIgnoringSpaces(lineMidpointState, ignoreStart);
- trailingObjects.updateMidpointsForTrailingBoxes(lineMidpointState, InlineIterator(), TrailingObjects::DoNotCollapseFirstSpace);
- }
- }
- } else if (ignoringSpaces) {
- // Stop ignoring spaces and begin at this
- // new point.
- ignoringSpaces = false;
- lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
- wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0;
- lastSpace = current.m_pos; // e.g., "Foo goo", don't add in any of the ignored spaces.
- stopIgnoringSpaces(lineMidpointState, InlineIterator(0, current.m_obj, current.m_pos));
- }
-
-#if ENABLE(SVG)
- if (isSVGText && current.m_pos > 0) {
- // Force creation of new InlineBoxes for each absolute positioned character (those that start new text chunks).
- if (toRenderSVGInlineText(t)->characterStartsNewTextChunk(current.m_pos))
- ensureCharacterGetsLineBox(lineMidpointState, current);
- }
-#endif
-
- if (currentCharacterIsSpace && !previousCharacterIsSpace) {
- ignoreStart.m_obj = current.m_obj;
- ignoreStart.m_pos = current.m_pos;
- }
-
- if (!currentCharacterIsWS && previousCharacterIsWS) {
- if (autoWrap && currentStyle->breakOnlyAfterWhiteSpace())
- lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition);
- }
-
- if (collapseWhiteSpace && currentCharacterIsSpace && !ignoringSpaces)
- trailingObjects.setTrailingWhitespace(toRenderText(current.m_obj));
- else if (!currentStyle->collapseWhiteSpace() || !currentCharacterIsSpace)
- trailingObjects.clear();
-
- atStart = false;
- nextCharacter:
- secondToLastCharacter = lastCharacter;
- lastCharacter = c;
- }
-
- renderTextInfo.m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
-
- wordMeasurements.grow(wordMeasurements.size() + 1);
- WordMeasurement& wordMeasurement = wordMeasurements.last();
- wordMeasurement.renderer = t;
-
- // IMPORTANT: current.m_pos is > length here!
- float additionalTempWidth = ignoringSpaces ? 0 : textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, wordMeasurement.fallbackFonts, textLayout);
- wordMeasurement.startOffset = lastSpace;
- wordMeasurement.endOffset = current.m_pos;
- wordMeasurement.width = ignoringSpaces ? 0 : additionalTempWidth + wordSpacingForWordMeasurement;
- additionalTempWidth += lastSpaceWordSpacing;
-
- float inlineLogicalTempWidth = inlineLogicalWidth(current.m_obj, !appliedStartWidth, includeEndWidth);
- width.addUncommittedWidth(additionalTempWidth + inlineLogicalTempWidth);
-
- if (wordMeasurement.fallbackFonts.isEmpty() && !fallbackFonts.isEmpty())
- wordMeasurement.fallbackFonts.swap(fallbackFonts);
- fallbackFonts.clear();
-
- if (collapseWhiteSpace && currentCharacterIsSpace && additionalTempWidth)
- width.setTrailingWhitespaceWidth(additionalTempWidth, inlineLogicalTempWidth);
-
- includeEndWidth = false;
-
- if (!width.fitsOnLine()) {
- if (canHyphenate)
- tryHyphenating(t, f, style->locale(), consecutiveHyphenatedLines, blockStyle->hyphenationLimitLines(), style->hyphenationLimitBefore(), style->hyphenationLimitAfter(), lastSpace, current.m_pos, width.currentWidth() - additionalTempWidth, width.availableWidth(), isFixedPitch, collapseWhiteSpace, lastSpaceWordSpacing, lBreak, current.m_nextBreakablePosition, m_hyphenated);
-
- if (!m_hyphenated && lBreak.previousInSameNode() == softHyphen && style->hyphens() != HyphensNone)
- m_hyphenated = true;
-
- if (m_hyphenated)
- goto end;
- }
- } else
- ASSERT_NOT_REACHED();
-
- bool canBreakHere = canBreakAtThisPosition(autoWrap, width, lBreak, next, current, currWS, currentCharacterIsSpace, autoWrapWasEverTrueOnLine);
- if (canBreakHere && !width.fitsOnLine(ignoringSpaces)) {
- // if we have floats, try to get below them.
- if (currentCharacterIsSpace && !ignoringSpaces && currentStyle->collapseWhiteSpace())
- trailingObjects.clear();
-
- if (width.committedWidth())
- goto end;
-
- width.fitBelowFloats();
-
- // |width| may have been adjusted because we got shoved down past a float (thus
- // giving us more room), so we need to retest, and only jump to
- // the end label if we still don't fit on the line. -dwh
- if (!width.fitsOnLine(ignoringSpaces))
- goto end;
- } else if (blockStyle->autoWrap() && !width.fitsOnLine() && !width.committedWidth()) {
- // If the container autowraps but the current child does not then we still need to ensure that it
- // wraps and moves below any floats.
- width.fitBelowFloats();
- }
-
- if (!current.m_obj->isFloatingOrOutOfFlowPositioned()) {
- last = current.m_obj;
- if (last->isReplaced() && autoWrap && (!last->isImage() || allowImagesToBreak) && (!last->isListMarker() || toRenderListMarker(last)->isInside()))
- commitLineBreakAtCurrentWidth(width, lBreak, next);
- }
-
- // Clear out our character space bool, since inline <pre>s don't collapse whitespace
- // with adjacent inline normal/nowrap spans.
- if (!collapseWhiteSpace)
- currentCharacterIsSpace = false;
-
- current.moveToStartOf(next);
- atStart = false;
- }
-
- if (width.fitsOnLine(true) || lastWS == NOWRAP)
- lBreak.clear();
-
- end:
-#if ENABLE(CSS_SHAPES)
- ShapeInsideInfo* shapeInfo = m_block->layoutShapeInsideInfo();
- bool segmentAllowsOverflow = !shapeInfo || !shapeInfo->hasSegments();
-#else
- bool segmentAllowsOverflow = true;
-#endif
-
- if (segmentAllowsOverflow) {
- if (lBreak == resolver.position()) {
- if (!lBreak.m_obj || !lBreak.m_obj->isBR()) {
- // we just add as much as possible
- if (blockStyle->whiteSpace() == PRE && !current.m_pos) {
- lBreak.moveTo(last, last->isText() ? last->length() : 0);
- } else if (lBreak.m_obj) {
- // Don't ever break in the middle of a word if we can help it.
- // There's no room at all. We just have to be on this line,
- // even though we'll spill out.
- lBreak.moveTo(current.m_obj, current.m_pos);
- }
- }
- // make sure we consume at least one char/object.
- if (lBreak == resolver.position())
- lBreak.increment();
- } else if (!width.committedWidth() && (!current.m_obj || !current.m_obj->isBR()) && !current.m_pos) {
- // Do not push the current object to the next line, when this line has some content, but it is still considered empty.
- // Empty inline elements like <span></span> can produce such lines and now we just ignore these break opportunities
- // at the start of a line, if no width has been committed yet.
- // Behave as if it was actually empty and consume at least one object.
- lBreak.increment();
- }
- }
-
- // Sanity check our midpoints.
- checkMidpoints(lineMidpointState, lBreak);
-
- trailingObjects.updateMidpointsForTrailingBoxes(lineMidpointState, lBreak, TrailingObjects::CollapseFirstSpace);
-
- // We might have made lBreak an iterator that points past the end
- // of the object. Do this adjustment to make it point to the start
- // of the next object instead to avoid confusing the rest of the
- // code.
- if (lBreak.m_pos > 0) {
- lBreak.m_pos--;
- lBreak.increment();
}
-
- return lBreak;
-}
-
-void RenderBlock::addOverflowFromInlineChildren()
-{
LayoutUnit endPadding = hasOverflowClip() ? paddingEnd() : LayoutUnit();
// FIXME: Need to find another way to do this, since scrollbars could show when we don't want them to.
- if (hasOverflowClip() && !endPadding && node() && node()->isRootEditableElement() && style()->isLeftToRightDirection())
+ if (hasOverflowClip() && !endPadding && element() && element()->isRootEditableElement() && style().isLeftToRightDirection())
endPadding = 1;
for (RootInlineBox* curr = firstRootBox(); curr; curr = curr->nextRootBox()) {
addLayoutOverflow(curr->paddedLayoutOverflowRect(endPadding));
- if (!hasOverflowClip())
- addVisualOverflow(curr->visualOverflowRect(curr->lineTop(), curr->lineBottom()));
+ RenderRegion* region = flowThreadContainingBlock() ? curr->containingRegion() : nullptr;
+ if (region)
+ region->addLayoutOverflowForBox(this, curr->paddedLayoutOverflowRect(endPadding));
+ if (!hasOverflowClip()) {
+ LayoutRect childVisualOverflowRect = curr->visualOverflowRect(curr->lineTop(), curr->lineBottom());
+ addVisualOverflow(childVisualOverflowRect);
+ if (region)
+ region->addVisualOverflowForBox(this, childVisualOverflowRect);
+ }
}
}
-void RenderBlock::deleteEllipsisLineBoxes()
+void RenderBlockFlow::deleteEllipsisLineBoxes()
{
- ETextAlign textAlign = style()->textAlign();
- bool ltr = style()->isLeftToRightDirection();
- bool firstLine = true;
+ ETextAlign textAlign = style().textAlign();
+ bool ltr = style().isLeftToRightDirection();
+ 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;
}
}
-void RenderBlock::checkLinesForTextOverflow()
+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(this, firstLineFont, &horizontalEllipsis, 1, firstLineStyle()));
+ float ellipsisWidth = (font == firstLineFont) ? firstLineEllipsisWidth : font.width(constructTextRun(this, font, &horizontalEllipsis, 1, style()));
// For LTR text truncation, we want to get the right edge of our padding box, and then we want to see
// if the right edge of a line box exceeds that. For RTL, we use the left edge of the padding box and
// check the left edge of the line box to see if it is less
// Include the scrollbar for overflow blocks, which means we want to use "contentWidth()"
- bool ltr = style()->isLeftToRightDirection();
- ETextAlign textAlign = style()->textAlign();
+ bool ltr = style().isLeftToRightDirection();
+ 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
@@ -3626,7 +2107,7 @@ void RenderBlock::checkLinesForTextOverflow()
}
}
-bool RenderBlock::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;
@@ -3636,39 +2117,43 @@ bool RenderBlock::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObjec
// 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->m_paginationStrut || !lineInfo.previousLineBrokeCleanly() || !lineInfo.isEmpty())
+ if (!newFloat.paginationStrut() || !lineInfo.previousLineBrokeCleanly() || !lineInfo.isEmpty())
return true;
const FloatingObjectSet& floatingObjectSet = m_floatingObjects->set();
- ASSERT(floatingObjectSet.last() == newFloat);
+ ASSERT(floatingObjectSet.last().get() == &newFloat);
LayoutUnit floatLogicalTop = logicalTopForFloat(newFloat);
- int paginationStrut = newFloat->m_paginationStrut;
+ LayoutUnit paginationStrut = newFloat.paginationStrut();
if (floatLogicalTop - paginationStrut != logicalHeight() + lineInfo.floatPaginationStrut())
return true;
- FloatingObjectSetIterator it = floatingObjectSet.end();
+ auto it = floatingObjectSet.end();
--it; // Last float is newFloat, skip that one.
- FloatingObjectSetIterator begin = floatingObjectSet.begin();
+ auto begin = floatingObjectSet.begin();
while (it != begin) {
--it;
- FloatingObject* f = *it;
- if (f == lastFloatFromPreviousLine)
+ auto& floatingObject = *it->get();
+ if (&floatingObject == lastFloatFromPreviousLine)
break;
- if (logicalTopForFloat(f) == logicalHeight() + lineInfo.floatPaginationStrut()) {
- f->m_paginationStrut += paginationStrut;
- RenderBox* o = f->m_renderer;
- setLogicalTopForChild(o, logicalTopForChild(o) + marginBeforeForChild(o) + paginationStrut);
- if (o->isRenderBlock())
- toRenderBlock(o)->setChildNeedsLayout(true, MarkOnlyThis);
- o->layoutIfNeeded();
+ if (logicalTopForFloat(floatingObject) == logicalHeight() + lineInfo.floatPaginationStrut()) {
+ floatingObject.setPaginationStrut(paginationStrut + floatingObject.paginationStrut());
+ RenderBox& floatBox = floatingObject.renderer();
+ setLogicalTopForChild(floatBox, logicalTopForChild(floatBox) + marginBeforeForChild(floatBox) + paginationStrut);
+
+ if (updateRegionRangeForBoxChild(floatBox))
+ floatBox.setNeedsLayout(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(f);
- m_floatingObjects->removePlacedObject(f);
- setLogicalTopForFloat(f, oldLogicalTop + paginationStrut);
- m_floatingObjects->addPlacedObject(f);
+ LayoutUnit oldLogicalTop = logicalTopForFloat(floatingObject);
+ m_floatingObjects->removePlacedObject(&floatingObject);
+ setLogicalTopForFloat(floatingObject, oldLogicalTop + paginationStrut);
+ m_floatingObjects->addPlacedObject(&floatingObject);
}
}
@@ -3678,46 +2163,105 @@ bool RenderBlock::positionNewFloatOnLine(FloatingObject* newFloat, FloatingObjec
return true;
}
-LayoutUnit RenderBlock::startAlignedOffsetForLine(LayoutUnit position, bool firstLine)
+LayoutUnit RenderBlockFlow::startAlignedOffsetForLine(LayoutUnit position, IndentTextOrNot shouldIndentText)
{
- ETextAlign textAlign = style()->textAlign();
-
- if (textAlign == TASTART) // FIXME: Handle TAEND here
- return startOffsetForLine(position, firstLine);
+ ETextAlign textAlign = style().textAlign();
+ bool shouldApplyIndentText = false;
+ switch (textAlign) {
+ case LEFT:
+ case WEBKIT_LEFT:
+ shouldApplyIndentText = style().isLeftToRightDirection();
+ break;
+ case RIGHT:
+ case WEBKIT_RIGHT:
+ shouldApplyIndentText = !style().isLeftToRightDirection();
+ break;
+ case TASTART:
+ shouldApplyIndentText = true;
+ break;
+ default:
+ shouldApplyIndentText = false;
+ }
+ // <rdar://problem/15427571>
+ // https://bugs.webkit.org/show_bug.cgi?id=124522
+ // This quirk is for legacy content that doesn't work properly with the center positioning scheme
+ // being honored (e.g., epubs).
+ if (shouldApplyIndentText || document().settings()->useLegacyTextAlignPositionedElementBehavior()) // FIXME: Handle TAEND here
+ return startOffsetForLine(position, shouldIndentText);
// 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())
+ if (!style().isLeftToRightDirection())
return logicalWidth() - logicalLeft;
return logicalLeft;
}
-
-void RenderBlock::layoutLineGridBox()
+void RenderBlockFlow::updateRegionForLine(RootInlineBox* lineBox) const
{
- if (style()->lineGrid() == RenderStyle::initialLineGrid()) {
- setLineGridBox(0);
+ ASSERT(lineBox);
+
+ if (!hasRegionRangeInFlowThread())
+ lineBox->clearContainingRegion();
+ else {
+ if (auto containingRegion = regionAtBlockOffset(lineBox->lineTopWithLeading()))
+ lineBox->setContainingRegion(*containingRegion);
+ else
+ lineBox->clearContainingRegion();
+ }
+
+ RootInlineBox* prevLineBox = lineBox->prevRootBox();
+ if (!prevLineBox)
return;
+
+ // This check is more accurate than the one in |adjustLinePositionForPagination| because it takes into
+ // account just the container changes between lines. The before mentioned function doesn't set the flag
+ // correctly if the line is positioned at the top of the last fragment container.
+ if (lineBox->containingRegion() != prevLineBox->containingRegion())
+ 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());
+ }
}
- setLineGridBox(0);
+ // 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;
- RootInlineBox* lineGridBox = new (renderArena()) RootInlineBox(this);
- lineGridBox->setHasTextChildren(); // Needed to make the line ascent/descent actually be honored in quirks mode.
- lineGridBox->setConstructed();
- GlyphOverflowAndFallbackFontsMap textBoxDataMap;
- VerticalPositionCache verticalPositionCache;
- lineGridBox->alignBoxesInBlockDirection(logicalHeight(), textBoxDataMap, verticalPositionCache);
-
- setLineGridBox(lineGridBox);
+ // 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());
- // FIXME: If any of the characteristics of the box change compared to the old one, then we need to do a deep dirtying
- // (similar to what happens when the page height changes). Ideally, though, we only do this if someone is actually snapping
- // to this grid.
+ // 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());
}
}