Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1030)

Unified Diff: Source/core/rendering/RenderBlockLineLayout.cpp

Issue 25054004: Refactor LineBreaker::nextSegmentBreak and add a BreakingContext that holds all its state (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Fixing from eae's review Created 7 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: Source/core/rendering/RenderBlockLineLayout.cpp
diff --git a/Source/core/rendering/RenderBlockLineLayout.cpp b/Source/core/rendering/RenderBlockLineLayout.cpp
index 70bdd9a1af09743930887808004715a1011476b5..833354e4124f8a4978283f5c63b3b42036f75d04 100644
--- a/Source/core/rendering/RenderBlockLineLayout.cpp
+++ b/Source/core/rendering/RenderBlockLineLayout.cpp
@@ -66,48 +66,22 @@ struct RenderTextInfo {
const Font* m_font;
};
-class LineBreaker {
+class TrailingObjects {
public:
- LineBreaker(RenderBlock* block)
- : m_block(block)
- {
- reset();
- }
-
- InlineIterator nextLineBreak(InlineBidiResolver&, LineInfo&, RenderTextInfo&, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements&);
+ TrailingObjects();
+ void setTrailingWhitespace(RenderText*);
+ void clear();
+ void appendBoxIfNeeded(RenderBox*);
- bool lineWasHyphenated() { return m_hyphenated; }
- const Vector<RenderBox*>& positionedObjects() { return m_positionedObjects; }
- EClear clear() { return m_clear; }
-private:
- void reset();
+ enum CollapseFirstSpaceOrNot { DoNotCollapseFirstSpace, CollapseFirstSpace };
- InlineIterator nextSegmentBreak(InlineBidiResolver&, LineInfo&, RenderTextInfo&, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements&);
- void skipTrailingWhitespace(InlineIterator&, const LineInfo&);
- void skipLeadingWhitespace(InlineBidiResolver&, LineInfo&, FloatingObject* lastFloatFromPreviousLine, LineWidth&);
+ void updateMidpointsForTrailingBoxes(LineMidpointState&, const InlineIterator& lBreak, CollapseFirstSpaceOrNot);
- RenderBlock* m_block;
- bool m_hyphenated;
- EClear m_clear;
- Vector<RenderBox*> m_positionedObjects;
+private:
+ RenderText* m_whitespace;
+ Vector<RenderBox*, 4> m_boxes;
};
-ShapeInsideInfo* RenderBlock::layoutShapeInsideInfo() const
-{
- ShapeInsideInfo* shapeInsideInfo = view()->layoutState()->shapeInsideInfo();
-
- if (!shapeInsideInfo && flowThreadContainingBlock() && allowsShapeInsideInfoSharing()) {
- LayoutUnit lineHeight = this->lineHeight(false, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
- // regionAtBlockOffset returns regions like an array first={0,N-1}, second={N,M-1}, ...
- LayoutUnit offset = logicalHeight() + lineHeight - LayoutUnit(1);
- RenderRegion* region = regionAtBlockOffset(offset);
- if (region)
- shapeInsideInfo = region->shapeInsideInfo();
- }
-
- return shapeInsideInfo;
-}
-
class LineInfo {
public:
LineInfo()
@@ -154,6 +128,191 @@ private:
unsigned m_runsFromLeadingWhitespace;
};
+static IndentTextOrNot requiresIndent(bool isFirstLine, bool isAfterHardLineBreak, RenderStyle* style)
+{
+ if (isFirstLine)
+ return IndentText;
+ if (isAfterHardLineBreak && style->textIndentLine() == TextIndentEachLine)
+ return IndentText;
+
+ return DoNotIndentText;
+}
+
+class LineBreaker {
+public:
+ LineBreaker(RenderBlock* block)
+ : m_block(block)
+ {
+ reset();
+ }
+
+ InlineIterator nextLineBreak(InlineBidiResolver&, LineInfo&, RenderTextInfo&, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements&);
+
+ bool lineWasHyphenated() { return m_hyphenated; }
+ const Vector<RenderBox*>& positionedObjects() { return m_positionedObjects; }
+ EClear clear() { return m_clear; }
+private:
+ void reset();
+
+ InlineIterator nextSegmentBreak(InlineBidiResolver&, LineInfo&, RenderTextInfo&, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements&);
+
+ class BreakingContext {
+ public:
+ BreakingContext(InlineBidiResolver& resolver, LineInfo& inLineInfo, LineWidth& lineWidth, RenderTextInfo& inRenderTextInfo, FloatingObject* inLastFloatFromPreviousLine, bool appliedStartWidth, RenderBlock* block)
+ : m_resolver(resolver)
+ , m_current(resolver.position())
+ , m_lineBreak(resolver.position())
+ , m_lastObject(m_current.m_obj)
+ , m_block(block)
+ , m_blockStyle(block->style())
+ , m_lineInfo(inLineInfo)
+ , m_renderTextInfo(inRenderTextInfo)
+ , m_lastFloatFromPreviousLine(inLastFloatFromPreviousLine)
+ , m_width(lineWidth)
+ , m_preservesNewline(m_current.m_obj->isSVGInlineText() ? false : RenderStyle::preserveNewline(m_currWS))
+ , m_atStart(true)
+ , m_ignoringSpaces(false)
+ , m_currentCharacterIsSpace(false)
+ , m_currentCharacterShouldCollapseIfPreWap(false)
+ , m_appliedStartWidth(appliedStartWidth)
+ , m_includeEndWidth(true)
+ , m_autoWrap(RenderStyle::autoWrap(m_currWS))
+ , m_autoWrapWasEverTrueOnLine(m_autoWrap)
+ , m_floatsFitOnLine(true)
+ , m_collapseWhiteSpace(RenderStyle::collapseWhiteSpace(m_currWS))
+ , m_startingNewParagraph(m_lineInfo.previousLineBrokeCleanly())
+ , m_allowImagesToBreak(!block->document().inQuirksMode() || !block->isTableCell() || !m_blockStyle->logicalWidth().isIntrinsicOrAuto())
+ , m_atEnd(false)
+ , m_lineMidpointState(resolver.midpointState())
+ {
+ m_lineInfo.setPreviousLineBrokeCleanly(false);
+ }
+
+ void setupContext();
+
+ RenderObject* currentObject() { return m_current.m_obj; }
+ InlineIterator lineBreak() { return m_lineBreak; }
+ bool atEnd() { return m_atEnd; }
+
+ void initializeForCurrentObject();
+
+ void increment();
+
+ void handleBR(EClear&);
+ void handleOutOfFlowPositioned(Vector<RenderBox*>& positionedObjects);
+ void handleFloat();
+ void handleEmptyInline();
+ void handleReplaced();
+ bool handleText(WordMeasurements&, bool& hyphenated);
+ void commitAndUpdateLineBreakIfNeeded();
+ InlineIterator handleEndOfLine();
+
+ void clearLineBreakIfFitsOnLine()
+ {
+ if (m_width.fitsOnLine() || m_lastWS == NOWRAP)
+ m_lineBreak.clear();
+ }
+
+ private:
+ void skipTrailingWhitespace(InlineIterator&, const LineInfo&);
+
+ InlineBidiResolver& m_resolver;
+
+ InlineIterator m_current;
+ InlineIterator m_lineBreak;
+ InlineIterator m_startOfIgnoredSpaces;
+
+ RenderBlock* m_block;
+ RenderObject* m_lastObject;
+ RenderObject* m_nextObject;
+
+ RenderStyle* m_currentStyle;
+ RenderStyle* m_blockStyle;
+
+ LineInfo& m_lineInfo;
+
+ RenderTextInfo& m_renderTextInfo;
+
+ FloatingObject* m_lastFloatFromPreviousLine;
+
+ LineWidth m_width;
+
+ EWhiteSpace m_currWS;
+ EWhiteSpace m_lastWS;
+
+ bool m_preservesNewline;
+ bool m_atStart;
+ bool m_ignoringSpaces;
+ bool m_currentCharacterIsSpace;
+ bool m_currentCharacterShouldCollapseIfPreWap;
+ bool m_appliedStartWidth;
+ bool m_includeEndWidth;
+ bool m_autoWrap;
+ bool m_autoWrapWasEverTrueOnLine;
+ bool m_floatsFitOnLine;
+ bool m_collapseWhiteSpace;
+ bool m_startingNewParagraph;
+ bool m_allowImagesToBreak;
+ bool m_atEnd;
+
+ LineMidpointState& m_lineMidpointState;
+
+ TrailingObjects m_trailingObjects;
+ };
+
+ void skipLeadingWhitespace(InlineBidiResolver&, LineInfo&, FloatingObject* lastFloatFromPreviousLine, LineWidth&);
+
+ RenderBlock* m_block;
+ bool m_hyphenated;
+ EClear m_clear;
+ Vector<RenderBox*> m_positionedObjects;
+};
+
+inline void LineBreaker::BreakingContext::initializeForCurrentObject()
+{
+ m_currentStyle = m_current.m_obj->style();
+ m_nextObject = bidiNextSkippingEmptyInlines(m_block, m_current.m_obj);
+ if (m_nextObject && m_nextObject->parent() && !m_nextObject->parent()->isDescendantOf(m_current.m_obj->parent()))
+ m_includeEndWidth = true;
+
+ m_currWS = m_current.m_obj->isReplaced() ? m_current.m_obj->parent()->style()->whiteSpace() : m_currentStyle->whiteSpace();
+ m_lastWS = m_lastObject->isReplaced() ? m_lastObject->parent()->style()->whiteSpace() : m_lastObject->style()->whiteSpace();
+
+ m_autoWrap = RenderStyle::autoWrap(m_currWS);
+ m_autoWrapWasEverTrueOnLine = m_autoWrapWasEverTrueOnLine || m_autoWrap;
+
+ m_preservesNewline = m_current.m_obj->isSVGInlineText() ? false : RenderStyle::preserveNewline(m_currWS);
+
+ m_collapseWhiteSpace = RenderStyle::collapseWhiteSpace(m_currWS);
+}
+
+inline void LineBreaker::BreakingContext::increment()
+{
+ // Clear out our character space bool, since inline <pre>s don't collapse whitespace
+ // with adjacent inline normal/nowrap spans.
+ if (!m_collapseWhiteSpace)
+ m_currentCharacterIsSpace = false;
+
+ m_current.moveToStartOf(m_nextObject);
+ m_atStart = false;
+}
+
+ShapeInsideInfo* RenderBlock::layoutShapeInsideInfo() const
+{
+ ShapeInsideInfo* shapeInsideInfo = view()->layoutState()->shapeInsideInfo();
+
+ if (!shapeInsideInfo && flowThreadContainingBlock() && allowsShapeInsideInfoSharing()) {
+ LayoutUnit lineHeight = this->lineHeight(false, isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes);
+ // regionAtBlockOffset returns regions like an array first={0,N-1}, second={N,M-1}, ...
+ LayoutUnit offset = logicalHeight() + lineHeight - LayoutUnit(1);
+ RenderRegion* region = regionAtBlockOffset(offset);
+ if (region)
+ shapeInsideInfo = region->shapeInsideInfo();
+ }
+
+ return shapeInsideInfo;
+}
+
static inline LayoutUnit borderPaddingMarginStart(RenderInline* child)
{
return child->marginStart() + child->paddingStart() + child->borderStart();
@@ -832,16 +991,6 @@ void RenderBlock::updateLogicalWidthForAlignment(const ETextAlign& textAlign, co
}
}
-static IndentTextOrNot requiresIndent(bool isFirstLine, bool isAfterHardLineBreak, RenderStyle* style)
-{
- if (isFirstLine)
- return IndentText;
- if (isAfterHardLineBreak && style->textIndentLine() == TextIndentEachLine)
- return IndentText;
-
- return DoNotIndentText;
-}
-
static void updateLogicalInlinePositions(RenderBlock* block, float& lineLogicalLeft, float& lineLogicalRight, float& availableLogicalWidth, bool firstLine, IndentTextOrNot shouldIndentText, LayoutUnit boxLogicalHeight)
{
LayoutUnit lineLogicalHeight = block->minLineHeightForReplacedRenderer(firstLine, boxLogicalHeight);
@@ -1111,7 +1260,7 @@ static inline BidiStatus statusWithDirection(TextDirection textDirection, bool i
WTF::Unicode::Direction direction = textDirection == LTR ? LeftToRight : RightToLeft;
RefPtr<BidiContext> context = BidiContext::create(textDirection == LTR ? 0 : 1, direction, isOverride, FromStyleOrDOM);
- // This copies BidiStatus and may churn the ref on BidiContext. I doubt it matters.
+ // This copies BidiStatus and may churn the ref on BidiContext I doubt it matters.
return BidiStatus(direction, direction, direction, context.release());
}
@@ -2362,7 +2511,7 @@ bool RenderBlock::generatesLineBoxesForInlineChild(RenderObject* inlineObj)
// object iteration process.
// NB. this function will insert any floating elements that would otherwise
// be skipped but it will not position them.
-void LineBreaker::skipTrailingWhitespace(InlineIterator& iterator, const LineInfo& lineInfo)
+inline void LineBreaker::BreakingContext::skipTrailingWhitespace(InlineIterator& iterator, const LineInfo& lineInfo)
{
while (!iterator.atEnd() && !requiresLineBox(iterator, lineInfo, TrailingWhitespace)) {
RenderObject* object = iterator.m_obj;
@@ -2442,22 +2591,6 @@ static ALWAYS_INLINE float textWidth(RenderText* text, unsigned from, unsigned l
return font.width(run, fallbackFonts, &glyphOverflow);
}
-class TrailingObjects {
-public:
- TrailingObjects();
- void setTrailingWhitespace(RenderText*);
- void clear();
- void appendBoxIfNeeded(RenderBox*);
-
- enum CollapseFirstSpaceOrNot { DoNotCollapseFirstSpace, CollapseFirstSpace };
-
- void updateMidpointsForTrailingBoxes(LineMidpointState&, const InlineIterator& lBreak, CollapseFirstSpaceOrNot);
-
-private:
- RenderText* m_whitespace;
- Vector<RenderBox*, 4> m_boxes;
-};
-
TrailingObjects::TrailingObjects()
: m_whitespace(0)
{
@@ -2588,603 +2721,611 @@ static inline bool iteratorIsBeyondEndOfRenderCombineText(const InlineIterator&
return iter.m_obj == renderer && iter.m_pos >= renderer->textLength();
}
-InlineIterator LineBreaker::nextSegmentBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements& wordMeasurements)
+inline void LineBreaker::BreakingContext::handleBR(EClear& clear)
{
- reset();
+ if (m_width.fitsOnLine()) {
+ RenderObject* br = m_current.m_obj;
+ m_lineBreak.moveToStartOf(br);
+ m_lineBreak.increment();
- ASSERT(resolver.position().root() == m_block);
+ // 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 (m_startingNewParagraph)
+ m_lineInfo.setEmpty(false, m_block, &m_width);
+ m_trailingObjects.clear();
+ m_lineInfo.setPreviousLineBrokeCleanly(true);
- bool appliedStartWidth = resolver.position().m_pos > 0;
- bool includeEndWidth = true;
- LineMidpointState& lineMidpointState = resolver.midpointState();
+ // 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 (m_ignoringSpaces && m_currentStyle->clear() != CNONE)
+ ensureLineBoxInsideIgnoredSpaces(m_lineMidpointState, br);
- LineWidth width(*m_block, lineInfo.isFirstLine(), requiresIndent(lineInfo.isFirstLine(), lineInfo.previousLineBrokeCleanly(), m_block->style()));
+ if (!m_lineInfo.isEmpty())
+ clear = m_currentStyle->clear();
+ }
+ m_atEnd = true;
+}
- skipLeadingWhitespace(resolver, lineInfo, lastFloatFromPreviousLine, width);
+inline void LineBreaker::BreakingContext::handleOutOfFlowPositioned(Vector<RenderBox*>& positionedObjects)
+{
+ // If our original display wasn't an inline type, then we can
+ // go ahead and determine our static inline position now.
+ RenderBox* box = toRenderBox(m_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 (resolver.position().atEnd())
- return resolver.position();
+ // If we're ignoring spaces, we have to stop and include this object and
+ // then start ignoring spaces again.
+ if (isInlineType || box->container()->isRenderInline()) {
+ if (m_ignoringSpaces)
+ ensureLineBoxInsideIgnoredSpaces(m_lineMidpointState, box);
+ m_trailingObjects.appendBoxIfNeeded(box);
+ } else {
+ positionedObjects.append(box);
+ }
+ m_width.addUncommittedWidth(inlineLogicalWidth(box));
+ // Reset prior line break context characters.
+ m_renderTextInfo.m_lineBreakIterator.resetPriorContext();
+}
+
+inline void LineBreaker::BreakingContext::handleFloat()
+{
+ RenderBox* floatBox = toRenderBox(m_current.m_obj);
+ FloatingObject* floatingObject = 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 (m_floatsFitOnLine && m_width.fitsOnLine(floatingObject->logicalWidth(m_block->isHorizontalWritingMode()))) {
+ m_block->positionNewFloatOnLine(floatingObject, m_lastFloatFromPreviousLine, m_lineInfo, m_width);
+ if (m_lineBreak.m_obj == m_current.m_obj) {
+ ASSERT(!m_lineBreak.m_pos);
+ m_lineBreak.increment();
+ }
+ } else {
+ m_floatsFitOnLine = false;
+ }
+ // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for floating element.
+ m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
+}
+
+inline void LineBreaker::BreakingContext::handleEmptyInline()
+{
+ // This should only end up being called on empty inlines
+ ASSERT(isEmptyInline(m_current.m_obj));
+
+ RenderInline* flowBox = toRenderInline(m_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(m_current.m_obj);
+ if (requiresLineBox || requiresLineBoxForContent(flowBox, m_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)
+ m_lineInfo.setEmpty(false, m_block, &m_width);
+ if (m_ignoringSpaces) {
+ m_trailingObjects.clear();
+ ensureLineBoxInsideIgnoredSpaces(m_lineMidpointState, m_current.m_obj);
+ } else if (m_blockStyle->collapseWhiteSpace() && m_resolver.position().m_obj == m_current.m_obj
+ && shouldSkipWhitespaceAfterStartObject(m_block, m_current.m_obj, m_lineMidpointState)) {
+ // Like with list markers, we start ignoring spaces to make sure that any
+ // additional spaces we see will be discarded.
+ m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = true;
+ m_ignoringSpaces = true;
+ }
+ }
- // 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 currentCharacterShouldCollapseIfPreWap = 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;
-
- bool preserveNewline = current.m_obj->isSVGInlineText() ? false : RenderStyle::preserveNewline(currWS);
-
- 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());
- }
+ m_width.addUncommittedWidth(inlineLogicalWidth(m_current.m_obj) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox));
+}
- // 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.fitsOnLine(f->logicalWidth(m_block->isHorizontalWritingMode()))) {
- 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.
- currentCharacterShouldCollapseIfPreWap = currentCharacterIsSpace = true;
- ignoringSpaces = true;
- }
- }
+inline void LineBreaker::BreakingContext::handleReplaced()
+{
+ RenderBox* replacedBox = toRenderBox(m_current.m_obj);
- width.addUncommittedWidth(inlineLogicalWidth(current.m_obj) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox));
- } else if (current.m_obj->isReplaced()) {
- RenderBox* replacedBox = toRenderBox(current.m_obj);
+ if (m_atStart)
+ m_width.updateAvailableWidth(replacedBox->logicalHeight());
- if (atStart)
- width.updateAvailableWidth(replacedBox->logicalHeight());
+ // Break on replaced elements if either has normal white-space.
+ if ((m_autoWrap || RenderStyle::autoWrap(m_lastWS)) && (!m_current.m_obj->isImage() || m_allowImagesToBreak)) {
+ m_width.commit();
+ m_lineBreak.moveToStartOf(m_current.m_obj);
+ }
- // Break on replaced elements if either has normal white-space.
- if ((autoWrap || RenderStyle::autoWrap(lastWS)) && (!current.m_obj->isImage() || allowImagesToBreak)) {
- width.commit();
- lBreak.moveToStartOf(current.m_obj);
- }
+ if (m_ignoringSpaces)
+ stopIgnoringSpaces(m_lineMidpointState, InlineIterator(0, m_current.m_obj, 0));
- if (ignoringSpaces)
- stopIgnoringSpaces(lineMidpointState, InlineIterator(0, current.m_obj, 0));
-
- lineInfo.setEmpty(false, m_block, &width);
- ignoringSpaces = false;
- currentCharacterShouldCollapseIfPreWap = currentCharacterIsSpace = 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.
- currentCharacterShouldCollapseIfPreWap = currentCharacterIsSpace = 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);
-
- bool isSVGText = t->isSVGInlineText();
-
- 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.position().increment(&resolver);
- }
- }
+ m_lineInfo.setEmpty(false, m_block, &m_width);
+ m_ignoringSpaces = false;
+ m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = false;
+ m_trailingObjects.clear();
- RenderStyle* style = t->style(lineInfo.isFirstLine());
- const Font& f = style->font();
- bool isFixedPitch = f.isFixedPitch();
-
- 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;
- // 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 (isSVGText) {
- breakWords = false;
- breakAll = false;
- }
+ // 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(m_current.m_obj);
+ if (m_current.m_obj->isListMarker()) {
+ if (m_blockStyle->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(m_block, m_current.m_obj, m_lineMidpointState)) {
+ // Like with inline flows, we start ignoring spaces to make sure that any
+ // additional spaces we see will be discarded.
+ m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = true;
+ m_ignoringSpaces = true;
+ }
+ if (toRenderListMarker(m_current.m_obj)->isInside())
+ m_width.addUncommittedWidth(replacedLogicalWidth);
+ } else {
+ m_width.addUncommittedWidth(replacedLogicalWidth);
+ }
+ if (m_current.m_obj->isRubyRun())
+ m_width.applyOverhang(toRenderRubyRun(m_current.m_obj), m_lastObject, m_nextObject);
+ // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for replaced element.
+ m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
+}
- if (t->isWordBreak()) {
- width.commit();
- lBreak.moveToStartOf(current.m_obj);
- ASSERT(current.m_pos == t->textLength());
- }
+static inline void nextCharacter(UChar& currentCharacter, UChar& lastCharacter, UChar& secondToLastCharacter)
+{
+ secondToLastCharacter = lastCharacter;
+ lastCharacter = currentCharacter;
+}
- if (renderTextInfo.m_text != 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);
- }
+inline bool LineBreaker::BreakingContext::handleText(WordMeasurements& wordMeasurements, bool& hyphenated)
+{
+ if (!m_current.m_pos)
+ m_appliedStartWidth = false;
- TextLayout* textLayout = renderTextInfo.m_layout.get();
+ RenderText* renderText = toRenderText(m_current.m_obj);
- // 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.
- float wordTrailingSpaceWidth = (f.typesettingFeatures() & Kerning) && !textLayout ? f.width(RenderBlock::constructTextRun(t, f, &space, 1, style)) + wordSpacing : 0;
+ bool isSVGText = renderText->isSVGInlineText();
- UChar lastCharacter = renderTextInfo.m_lineBreakIterator.lastCharacter();
- UChar secondToLastCharacter = renderTextInfo.m_lineBreakIterator.secondToLastCharacter();
- for (; current.m_pos < t->textLength(); current.fastIncrementInTextNode()) {
- bool previousCharacterIsSpace = currentCharacterIsSpace;
- bool previousCharacterShouldCollapseIfPreWap = currentCharacterShouldCollapseIfPreWap;
- UChar c = current.current();
- currentCharacterShouldCollapseIfPreWap = currentCharacterIsSpace = c == ' ' || c == '\t' || (!preserveNewline && (c == '\n'));
+ if (renderText->style()->hasTextCombine() && m_current.m_obj->isCombineText() && !toRenderCombineText(m_current.m_obj)->isCombined()) {
+ RenderCombineText* combineRenderer = toRenderCombineText(m_current.m_obj);
+ combineRenderer->combineText();
+ // The length of the renderer's text may have changed. Increment stale iterator positions
+ if (iteratorIsBeyondEndOfRenderCombineText(m_lineBreak, combineRenderer)) {
+ ASSERT(iteratorIsBeyondEndOfRenderCombineText(m_resolver.position(), combineRenderer));
+ m_lineBreak.increment();
+ m_resolver.position().increment(&m_resolver);
+ }
+ }
- if (!collapseWhiteSpace || !currentCharacterIsSpace)
- lineInfo.setEmpty(false, m_block, &width);
+ RenderStyle* style = renderText->style(m_lineInfo.isFirstLine());
+ const Font& font = style->font();
+ bool isFixedPitch = font.isFixedPitch();
- if (c == softHyphen && autoWrap && !hyphenWidth) {
- hyphenWidth = measureHyphenWidth(t, f);
- width.addUncommittedWidth(hyphenWidth);
- }
+ unsigned lastSpace = m_current.m_pos;
+ float wordSpacing = m_currentStyle->wordSpacing();
+ float lastSpaceWordSpacing = 0;
+ float wordSpacingForWordMeasurement = 0;
- bool applyWordSpacing = false;
+ float wrapW = m_width.uncommittedWidth() + inlineLogicalWidth(m_current.m_obj, !m_appliedStartWidth, true);
+ float charWidth = 0;
+ // 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 = m_currentStyle->breakWords() && ((m_autoWrap && !m_width.committedWidth()) || m_currWS == PRE);
+ bool midWordBreak = false;
+ bool breakAll = m_currentStyle->wordBreak() == BreakAllWordBreak && m_autoWrap;
+ float hyphenWidth = 0;
- if ((breakAll || breakWords) && !midWordBreak) {
- wrapW += charWidth;
- bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && current.m_pos + 1 < t->textLength() && U16_IS_TRAIL((*t)[current.m_pos + 1]);
- charWidth = textWidth(t, current.m_pos, midWordBreakIsBeforeSurrogatePair ? 2 : 1, f, width.committedWidth() + wrapW, isFixedPitch, collapseWhiteSpace, 0, textLayout);
- midWordBreak = width.committedWidth() + wrapW + charWidth > width.availableWidth();
- }
+ if (isSVGText) {
+ breakWords = false;
+ breakAll = false;
+ }
- bool betweenWords = c == '\n' || (currWS != PRE && !atStart && isBreakable(renderTextInfo.m_lineBreakIterator, current.m_pos, current.m_nextBreakablePosition));
-
- 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;
- }
- }
+ if (renderText->isWordBreak()) {
+ m_width.commit();
+ m_lineBreak.moveToStartOf(m_current.m_obj);
+ ASSERT(m_current.m_pos == renderText->textLength());
+ }
- wordMeasurements.grow(wordMeasurements.size() + 1);
- WordMeasurement& wordMeasurement = wordMeasurements.last();
+ if (m_renderTextInfo.m_text != renderText) {
+ m_renderTextInfo.m_text = renderText;
+ m_renderTextInfo.m_font = &font;
+ m_renderTextInfo.m_layout = font.createLayout(renderText, m_width.currentWidth(), m_collapseWhiteSpace);
+ m_renderTextInfo.m_lineBreakIterator.resetStringAndReleaseIterator(renderText->text(), style->locale());
+ } else if (m_renderTextInfo.m_layout && m_renderTextInfo.m_font != &font) {
+ m_renderTextInfo.m_font = &font;
+ m_renderTextInfo.m_layout = font.createLayout(renderText, m_width.currentWidth(), m_collapseWhiteSpace);
+ }
- wordMeasurement.renderer = t;
- wordMeasurement.endOffset = current.m_pos;
- wordMeasurement.startOffset = lastSpace;
+ TextLayout* textLayout = m_renderTextInfo.m_layout.get();
- float additionalTmpW;
- if (wordTrailingSpaceWidth && c == ' ')
- additionalTmpW = textWidth(t, lastSpace, current.m_pos + 1 - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout) - wordTrailingSpaceWidth;
- else
- additionalTmpW = textWidth(t, lastSpace, current.m_pos - lastSpace, f, width.currentWidth(), isFixedPitch, collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout);
-
- wordMeasurement.width = additionalTmpW + wordSpacingForWordMeasurement;
- additionalTmpW += lastSpaceWordSpacing;
- width.addUncommittedWidth(additionalTmpW);
- if (!appliedStartWidth) {
- width.addUncommittedWidth(inlineLogicalWidth(current.m_obj, true, false));
- appliedStartWidth = true;
- }
+ // 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.
+ float wordTrailingSpaceWidth = (font.typesettingFeatures() & Kerning) && !textLayout ? font.width(RenderBlock::constructTextRun(renderText, font, &space, 1, style)) + wordSpacing : 0;
- 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() && currentCharacterIsSpace && 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.fitsOnLine(charWidth)) {
- lineWasTooWide = true;
- lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition);
- skipTrailingWhitespace(lBreak, lineInfo);
- }
- }
- if (lineWasTooWide || !width.fitsOnLine()) {
- 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)
- 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(-additionalTmpW);
- if (hyphenWidth) {
- // Subtract the width of the soft hyphen out since we fit on a line.
- width.addUncommittedWidth(-hyphenWidth);
- hyphenWidth = 0;
- }
- }
- }
+ UChar lastCharacter = m_renderTextInfo.m_lineBreakIterator.lastCharacter();
+ UChar secondToLastCharacter = m_renderTextInfo.m_lineBreakIterator.secondToLastCharacter();
+ for (; m_current.m_pos < renderText->textLength(); m_current.fastIncrementInTextNode()) {
+ bool previousCharacterIsSpace = m_currentCharacterIsSpace;
+ bool previousCharacterShouldCollapseIfPreWap = m_currentCharacterShouldCollapseIfPreWap;
+ UChar c = m_current.current();
+ m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = c == ' ' || c == '\t' || (!m_preservesNewline && (c == '\n'));
- 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 (!m_collapseWhiteSpace || !m_currentCharacterIsSpace)
+ m_lineInfo.setEmpty(false, m_block, &m_width);
- if (autoWrap && betweenWords) {
- width.commit();
- wrapW = 0;
- lBreak.moveTo(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 (c == softHyphen && m_autoWrap && !hyphenWidth) {
+ hyphenWidth = measureHyphenWidth(renderText, font);
+ m_width.addUncommittedWidth(hyphenWidth);
+ }
- 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);
- }
+ bool applyWordSpacing = false;
- if (betweenWords) {
- lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
- wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0;
- lastSpace = current.m_pos;
- }
+ if ((breakAll || breakWords) && !midWordBreak) {
+ wrapW += charWidth;
+ bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && m_current.m_pos + 1 < renderText->textLength() && U16_IS_TRAIL((*renderText)[m_current.m_pos + 1]);
+ charWidth = textWidth(renderText, m_current.m_pos, midWordBreakIsBeforeSurrogatePair ? 2 : 1, font, m_width.committedWidth() + wrapW, isFixedPitch, m_collapseWhiteSpace, 0, textLayout);
+ midWordBreak = m_width.committedWidth() + wrapW + charWidth > m_width.availableWidth();
+ }
- 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) {
+ bool betweenWords = c == '\n' || (m_currWS != PRE && !m_atStart && isBreakable(m_renderTextInfo.m_lineBreakIterator, m_current.m_pos, m_current.m_nextBreakablePosition));
+
+ if (betweenWords || midWordBreak) {
+ bool stoppedIgnoringSpaces = false;
+ if (m_ignoringSpaces) {
+ lastSpaceWordSpacing = 0;
+ if (!m_currentCharacterIsSpace) {
// 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 (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);
+ m_ignoringSpaces = false;
+ wordSpacingForWordMeasurement = 0;
+ lastSpace = m_current.m_pos; // e.g., "Foo goo", don't add in any of the ignored spaces.
+ stopIgnoringSpaces(m_lineMidpointState, InlineIterator(0, m_current.m_obj, m_current.m_pos));
+ stoppedIgnoringSpaces = true;
+ } else {
+ // Just keep ignoring these spaces.
+ nextCharacter(c, lastCharacter, secondToLastCharacter);
+ continue;
}
+ }
- if (currentCharacterIsSpace && !previousCharacterIsSpace) {
- ignoreStart.m_obj = current.m_obj;
- ignoreStart.m_pos = current.m_pos;
- }
+ wordMeasurements.grow(wordMeasurements.size() + 1);
+ WordMeasurement& wordMeasurement = wordMeasurements.last();
- if (!currentCharacterIsSpace && previousCharacterShouldCollapseIfPreWap) {
- if (autoWrap && currentStyle->breakOnlyAfterWhiteSpace())
- lBreak.moveTo(current.m_obj, current.m_pos, current.m_nextBreakablePosition);
- }
+ wordMeasurement.renderer = renderText;
+ wordMeasurement.endOffset = m_current.m_pos;
+ wordMeasurement.startOffset = lastSpace;
- if (collapseWhiteSpace && currentCharacterIsSpace && !ignoringSpaces)
- trailingObjects.setTrailingWhitespace(toRenderText(current.m_obj));
- else if (!currentStyle->collapseWhiteSpace() || !currentCharacterIsSpace)
- trailingObjects.clear();
+ float additionalTmpW;
+ if (wordTrailingSpaceWidth && c == ' ')
+ additionalTmpW = textWidth(renderText, lastSpace, m_current.m_pos + 1 - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout) - wordTrailingSpaceWidth;
+ else
+ additionalTmpW = textWidth(renderText, lastSpace, m_current.m_pos - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout);
- atStart = false;
- nextCharacter:
- secondToLastCharacter = lastCharacter;
- lastCharacter = c;
+ wordMeasurement.width = additionalTmpW + wordSpacingForWordMeasurement;
+ additionalTmpW += lastSpaceWordSpacing;
+ m_width.addUncommittedWidth(additionalTmpW);
+ if (!m_appliedStartWidth) {
+ m_width.addUncommittedWidth(inlineLogicalWidth(m_current.m_obj, true, false));
+ m_appliedStartWidth = true;
}
- renderTextInfo.m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
+ applyWordSpacing = wordSpacing && m_currentCharacterIsSpace;
+
+ if (!m_width.committedWidth() && m_autoWrap && !m_width.fitsOnLine())
+ m_width.fitBelowFloats();
+
+ if (m_autoWrap || breakWords) {
+ // If we break only after white-space, consider the current character
+ // as candidate width for this line.
+ bool lineWasTooWide = false;
+ if (m_width.fitsOnLine() && m_currentCharacterIsSpace && m_currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) {
+ float charWidth = textWidth(renderText, m_current.m_pos, 1, font, m_width.currentWidth(), isFixedPitch, m_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 (!m_width.fitsOnLine(charWidth)) {
+ lineWasTooWide = true;
+ m_lineBreak.moveTo(m_current.m_obj, m_current.m_pos, m_current.m_nextBreakablePosition);
+ skipTrailingWhitespace(m_lineBreak, m_lineInfo);
+ }
+ }
+ if (lineWasTooWide || !m_width.fitsOnLine()) {
+ if (m_lineBreak.atTextParagraphSeparator()) {
+ if (!stoppedIgnoringSpaces && m_current.m_pos > 0)
+ ensureCharacterGetsLineBox(m_lineMidpointState, m_current);
+ m_lineBreak.increment();
+ m_lineInfo.setPreviousLineBrokeCleanly(true);
+ wordMeasurement.endOffset = m_lineBreak.m_pos;
+ }
+ if (m_lineBreak.m_obj && m_lineBreak.m_pos && m_lineBreak.m_obj->isText() && toRenderText(m_lineBreak.m_obj)->textLength() && toRenderText(m_lineBreak.m_obj)->characterAt(m_lineBreak.m_pos - 1) == softHyphen)
+ hyphenated = true;
+ if (m_lineBreak.m_pos && m_lineBreak.m_pos != (unsigned)wordMeasurement.endOffset && !wordMeasurement.width) {
+ if (charWidth) {
+ wordMeasurement.endOffset = m_lineBreak.m_pos;
+ wordMeasurement.width = charWidth;
+ }
+ }
+ // Didn't fit. Jump to the end unless there's still an opportunity to collapse whitespace.
+ if (m_ignoringSpaces || !m_collapseWhiteSpace || !m_currentCharacterIsSpace || !previousCharacterIsSpace) {
+ m_atEnd = true;
+ return false;
+ }
+ } else {
+ if (!betweenWords || (midWordBreak && !m_autoWrap))
+ m_width.addUncommittedWidth(-additionalTmpW);
+ if (hyphenWidth) {
+ // Subtract the width of the soft hyphen out since we fit on a line.
+ m_width.addUncommittedWidth(-hyphenWidth);
+ hyphenWidth = 0;
+ }
+ }
+ }
- wordMeasurements.grow(wordMeasurements.size() + 1);
- WordMeasurement& wordMeasurement = wordMeasurements.last();
- wordMeasurement.renderer = t;
+ if (c == '\n' && m_preservesNewline) {
+ if (!stoppedIgnoringSpaces && m_current.m_pos > 0)
+ ensureCharacterGetsLineBox(m_lineMidpointState, m_current);
+ m_lineBreak.moveTo(m_current.m_obj, m_current.m_pos, m_current.m_nextBreakablePosition);
+ m_lineBreak.increment();
+ m_lineInfo.setPreviousLineBrokeCleanly(true);
+ return true;
+ }
- // IMPORTANT: current.m_pos is > length here!
- float additionalTmpW = 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 : additionalTmpW + wordSpacingForWordMeasurement;
- additionalTmpW += lastSpaceWordSpacing;
- width.addUncommittedWidth(additionalTmpW + inlineLogicalWidth(current.m_obj, !appliedStartWidth, includeEndWidth));
- includeEndWidth = false;
+ if (m_autoWrap && betweenWords) {
+ m_width.commit();
+ wrapW = 0;
+ m_lineBreak.moveTo(m_current.m_obj, m_current.m_pos, m_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 (!width.fitsOnLine()) {
- if (!m_hyphenated && lBreak.previousInSameNode() == softHyphen)
- m_hyphenated = true;
+ 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.
+ m_lineBreak.moveTo(m_current.m_obj, m_current.m_pos, m_current.m_nextBreakablePosition);
+ midWordBreak &= (breakWords || breakAll);
+ }
- if (m_hyphenated)
- goto end;
+ if (betweenWords) {
+ lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
+ wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0;
+ lastSpace = m_current.m_pos;
}
- } else
- ASSERT_NOT_REACHED();
- bool checkForBreak = autoWrap;
- if (width.committedWidth() && !width.fitsOnLine() && lBreak.m_obj && currWS == NOWRAP)
- checkForBreak = true;
- else if (next && current.m_obj->isText() && next->isText() && !next->isBR() && (autoWrap || next->style()->autoWrap())) {
- if (autoWrap && currentCharacterIsSpace)
- checkForBreak = true;
- else {
- RenderText* nextText = toRenderText(next);
- if (nextText->textLength()) {
- UChar c = nextText->characterAt(0);
- // If the next item on the line is text, and if we did not end with
- // a space, then the next text run continues our word (and so it needs to
- // keep adding to the uncommitted width. Just update and continue.
- checkForBreak = !currentCharacterIsSpace && (c == ' ' || c == '\t' || (c == '\n' && !next->preservesNewline()));
- } else if (nextText->isWordBreak())
- checkForBreak = true;
-
- if (!width.fitsOnLine() && !width.committedWidth())
- width.fitBelowFloats();
-
- bool canPlaceOnLine = width.fitsOnLine() || !autoWrapWasEverTrueOnLine;
- if (canPlaceOnLine && checkForBreak) {
- width.commit();
- lBreak.moveToStartOf(next);
+ if (!m_ignoringSpaces && m_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 (m_currentCharacterIsSpace && previousCharacterIsSpace) {
+ m_ignoringSpaces = true;
+
+ // We just entered a mode where we are ignoring
+ // spaces. Create a midpoint to terminate the run
+ // before the second space.
+ startIgnoringSpaces(m_lineMidpointState, m_startOfIgnoredSpaces);
+ m_trailingObjects.updateMidpointsForTrailingBoxes(m_lineMidpointState, InlineIterator(), TrailingObjects::DoNotCollapseFirstSpace);
}
}
+ } else if (m_ignoringSpaces) {
+ // Stop ignoring spaces and begin at this
+ // new point.
+ m_ignoringSpaces = false;
+ lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
+ wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0;
+ lastSpace = m_current.m_pos; // e.g., "Foo goo", don't add in any of the ignored spaces.
+ stopIgnoringSpaces(m_lineMidpointState, InlineIterator(0, m_current.m_obj, m_current.m_pos));
}
- if (checkForBreak && !width.fitsOnLine()) {
- // if we have floats, try to get below them.
- if (currentCharacterIsSpace && !ignoringSpaces && currentStyle->collapseWhiteSpace())
- trailingObjects.clear();
+ if (isSVGText && m_current.m_pos > 0) {
+ // Force creation of new InlineBoxes for each absolute positioned character (those that start new text chunks).
+ if (toRenderSVGInlineText(renderText)->characterStartsNewTextChunk(m_current.m_pos))
+ ensureCharacterGetsLineBox(m_lineMidpointState, m_current);
+ }
+
+ if (m_currentCharacterIsSpace && !previousCharacterIsSpace) {
+ m_startOfIgnoredSpaces.m_obj = m_current.m_obj;
+ m_startOfIgnoredSpaces.m_pos = m_current.m_pos;
+ }
+
+ if (!m_currentCharacterIsSpace && previousCharacterShouldCollapseIfPreWap) {
+ if (m_autoWrap && m_currentStyle->breakOnlyAfterWhiteSpace())
+ m_lineBreak.moveTo(m_current.m_obj, m_current.m_pos, m_current.m_nextBreakablePosition);
+ }
- if (width.committedWidth())
- goto end;
+ if (m_collapseWhiteSpace && m_currentCharacterIsSpace && !m_ignoringSpaces)
+ m_trailingObjects.setTrailingWhitespace(toRenderText(m_current.m_obj));
+ else if (!m_currentStyle->collapseWhiteSpace() || !m_currentCharacterIsSpace)
+ m_trailingObjects.clear();
- width.fitBelowFloats();
+ m_atStart = false;
+ nextCharacter(c, lastCharacter, secondToLastCharacter);
+ }
+
+ m_renderTextInfo.m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
+
+ wordMeasurements.grow(wordMeasurements.size() + 1);
+ WordMeasurement& wordMeasurement = wordMeasurements.last();
+ wordMeasurement.renderer = renderText;
- // |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())
- 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();
+ // IMPORTANT: current.m_pos is > length here!
+ float additionalTmpW = m_ignoringSpaces ? 0 : textWidth(renderText, lastSpace, m_current.m_pos - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts, textLayout);
+ wordMeasurement.startOffset = lastSpace;
+ wordMeasurement.endOffset = m_current.m_pos;
+ wordMeasurement.width = m_ignoringSpaces ? 0 : additionalTmpW + wordSpacingForWordMeasurement;
+ additionalTmpW += lastSpaceWordSpacing;
+ m_width.addUncommittedWidth(additionalTmpW + inlineLogicalWidth(m_current.m_obj, !m_appliedStartWidth, m_includeEndWidth));
+ m_includeEndWidth = false;
+
+ if (!m_width.fitsOnLine()) {
+ if (!hyphenated && m_lineBreak.previousInSameNode() == softHyphen) {
+ hyphenated = true;
+ m_atEnd = true;
}
+ }
+ return false;
+}
+
+inline void LineBreaker::BreakingContext::commitAndUpdateLineBreakIfNeeded()
+{
+ bool checkForBreak = m_autoWrap;
+ if (m_width.committedWidth() && !m_width.fitsOnLine() && m_lineBreak.m_obj && m_currWS == NOWRAP) {
+ checkForBreak = true;
+ } else if (m_nextObject && m_current.m_obj->isText() && m_nextObject->isText() && !m_nextObject->isBR() && (m_autoWrap || m_nextObject->style()->autoWrap())) {
+ if (m_autoWrap && m_currentCharacterIsSpace) {
+ checkForBreak = true;
+ } else {
+ RenderText* nextText = toRenderText(m_nextObject);
+ if (nextText->textLength()) {
+ UChar c = nextText->characterAt(0);
+ // If the next item on the line is text, and if we did not end with
+ // a space, then the next text run continues our word (and so it needs to
+ // keep adding to the uncommitted width. Just update and continue.
+ checkForBreak = !m_currentCharacterIsSpace && (c == ' ' || c == '\t' || (c == '\n' && !m_nextObject->preservesNewline()));
+ } else if (nextText->isWordBreak()) {
+ checkForBreak = true;
+ }
+
+ if (!m_width.fitsOnLine() && !m_width.committedWidth())
+ m_width.fitBelowFloats();
- if (!current.m_obj->isFloatingOrOutOfFlowPositioned()) {
- last = current.m_obj;
- if (last->isReplaced() && autoWrap && (!last->isImage() || allowImagesToBreak) && (!last->isListMarker() || toRenderListMarker(last)->isInside())) {
- width.commit();
- lBreak.moveToStartOf(next);
+ bool canPlaceOnLine = m_width.fitsOnLine() || !m_autoWrapWasEverTrueOnLine;
+ if (canPlaceOnLine && checkForBreak) {
+ m_width.commit();
+ m_lineBreak.moveToStartOf(m_nextObject);
}
}
+ }
+
+ if (checkForBreak && !m_width.fitsOnLine()) {
+ // if we have floats, try to get below them.
+ if (m_currentCharacterIsSpace && !m_ignoringSpaces && m_currentStyle->collapseWhiteSpace())
+ m_trailingObjects.clear();
+
+ if (m_width.committedWidth()) {
+ m_atEnd = true;
+ return;
+ }
- // Clear out our character space bool, since inline <pre>s don't collapse whitespace
- // with adjacent inline normal/nowrap spans.
- if (!collapseWhiteSpace)
- currentCharacterIsSpace = false;
+ m_width.fitBelowFloats();
- current.moveToStartOf(next);
- atStart = false;
+ // |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 (!m_width.fitsOnLine()) {
+ m_atEnd = true;
+ return;
+ }
+ } else if (m_blockStyle->autoWrap() && !m_width.fitsOnLine() && !m_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.
+ m_width.fitBelowFloats();
}
- if (width.fitsOnLine() || lastWS == NOWRAP)
- lBreak.clear();
+ if (!m_current.m_obj->isFloatingOrOutOfFlowPositioned()) {
+ m_lastObject = m_current.m_obj;
+ if (m_lastObject->isReplaced() && m_autoWrap && (!m_lastObject->isImage() || m_allowImagesToBreak) && (!m_lastObject->isListMarker() || toRenderListMarker(m_lastObject)->isInside())) {
+ m_width.commit();
+ m_lineBreak.moveToStartOf(m_nextObject);
+ }
+ }
+}
- end:
+InlineIterator LineBreaker::BreakingContext::handleEndOfLine()
+{
ShapeInsideInfo* shapeInfo = m_block->layoutShapeInsideInfo();
bool segmentAllowsOverflow = !shapeInfo || !shapeInfo->hasSegments();
- if (lBreak == resolver.position() && (!lBreak.m_obj || !lBreak.m_obj->isBR()) && segmentAllowsOverflow) {
+ if (m_lineBreak == m_resolver.position() && (!m_lineBreak.m_obj || !m_lineBreak.m_obj->isBR()) && segmentAllowsOverflow) {
// 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) {
+ if (m_blockStyle->whiteSpace() == PRE && !m_current.m_pos) {
+ m_lineBreak.moveTo(m_lastObject, m_lastObject->isText() ? m_lastObject->length() : 0);
+ } else if (m_lineBreak.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);
+ m_lineBreak.moveTo(m_current.m_obj, m_current.m_pos);
}
}
// FIXME Bug 100049: We do not need to consume input in a multi-segment line
// unless no segment will.
// make sure we consume at least one char/object.
- if (lBreak == resolver.position() && segmentAllowsOverflow)
- lBreak.increment();
+ if (m_lineBreak == m_resolver.position() && segmentAllowsOverflow)
+ m_lineBreak.increment();
// Sanity check our midpoints.
- checkMidpoints(lineMidpointState, lBreak);
+ checkMidpoints(m_lineMidpointState, m_lineBreak);
- trailingObjects.updateMidpointsForTrailingBoxes(lineMidpointState, lBreak, TrailingObjects::CollapseFirstSpace);
+ m_trailingObjects.updateMidpointsForTrailingBoxes(m_lineMidpointState, m_lineBreak, TrailingObjects::CollapseFirstSpace);
- // We might have made lBreak an iterator that points past the end
+ // We might have made lineBreak 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();
+ if (m_lineBreak.m_pos > 0) {
+ m_lineBreak.m_pos--;
+ m_lineBreak.increment();
}
- return lBreak;
+ return m_lineBreak;
+}
+
+InlineIterator 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;
+
+ 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();
+
+ BreakingContext context(resolver, lineInfo, width, renderTextInfo, lastFloatFromPreviousLine, appliedStartWidth, m_block);
+
+ while (context.currentObject()) {
+ context.initializeForCurrentObject();
+ if (context.currentObject()->isBR()) {
+ context.handleBR(m_clear);
+ } else if (context.currentObject()->isOutOfFlowPositioned()) {
+ context.handleOutOfFlowPositioned(m_positionedObjects);
+ } else if (context.currentObject()->isFloating()) {
+ context.handleFloat();
+ } else if (context.currentObject()->isRenderInline()) {
+ context.handleEmptyInline();
+ } else if (context.currentObject()->isReplaced()) {
+ context.handleReplaced();
+ } else if (context.currentObject()->isText()) {
+ if (context.handleText(wordMeasurements, m_hyphenated)) {
+ // We've hit a hard text line break. Our line break iterator is updated, so go ahead and early return.
+ return context.lineBreak();
+ }
+ } else {
+ ASSERT_NOT_REACHED();
+ }
+
+ if (context.atEnd())
+ return context.handleEndOfLine();
+
+ context.commitAndUpdateLineBreakIfNeeded();
+
+ if (context.atEnd())
+ return context.handleEndOfLine();
+
+ context.increment();
+ }
+
+ context.clearLineBreakIfFitsOnLine();
+
+ return context.handleEndOfLine();
}
void RenderBlock::addOverflowFromInlineChildren()
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698