Home | History | Annotate | Download | only in line
      1 /*
      2  * Copyright (C) 2000 Lars Knoll (knoll (at) kde.org)
      3  * Copyright (C) 2003, 2004, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All right reserved.
      4  * Copyright (C) 2010 Google Inc. All rights reserved.
      5  * Copyright (C) 2013 Adobe Systems Incorporated.
      6  *
      7  * This library is free software; you can redistribute it and/or
      8  * modify it under the terms of the GNU Library General Public
      9  * License as published by the Free Software Foundation; either
     10  * version 2 of the License, or (at your option) any later version.
     11  *
     12  * This library is distributed in the hope that it will be useful,
     13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15  * Library General Public License for more details.
     16  *
     17  * You should have received a copy of the GNU Library General Public License
     18  * along with this library; see the file COPYING.LIB.  If not, write to
     19  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     20  * Boston, MA 02110-1301, USA.
     21  *
     22  */
     23 
     24 #ifndef BreakingContextInlineHeaders_h
     25 #define BreakingContextInlineHeaders_h
     26 
     27 #include "core/rendering/InlineIterator.h"
     28 #include "core/rendering/InlineTextBox.h"
     29 #include "core/rendering/RenderCombineText.h"
     30 #include "core/rendering/RenderInline.h"
     31 #include "core/rendering/RenderLayer.h"
     32 #include "core/rendering/RenderListMarker.h"
     33 #include "core/rendering/RenderObjectInlines.h"
     34 #include "core/rendering/RenderRubyRun.h"
     35 #include "core/rendering/TextRunConstructor.h"
     36 #include "core/rendering/break_lines.h"
     37 #include "core/rendering/line/LineBreaker.h"
     38 #include "core/rendering/line/LineInfo.h"
     39 #include "core/rendering/line/LineWidth.h"
     40 #include "core/rendering/line/RenderTextInfo.h"
     41 #include "core/rendering/line/TrailingObjects.h"
     42 #include "core/rendering/line/WordMeasurement.h"
     43 #include "core/rendering/svg/RenderSVGInlineText.h"
     44 
     45 namespace blink {
     46 
     47 // We don't let our line box tree for a single line get any deeper than this.
     48 const unsigned cMaxLineDepth = 200;
     49 
     50 class BreakingContext {
     51 public:
     52     BreakingContext(InlineBidiResolver& resolver, LineInfo& inLineInfo, LineWidth& lineWidth, RenderTextInfo& inRenderTextInfo, FloatingObject* inLastFloatFromPreviousLine, bool appliedStartWidth, RenderBlockFlow* block)
     53         : m_resolver(resolver)
     54         , m_current(resolver.position())
     55         , m_lineBreak(resolver.position())
     56         , m_block(block)
     57         , m_lastObject(m_current.object())
     58         , m_nextObject(0)
     59         , m_currentStyle(0)
     60         , m_blockStyle(block->style())
     61         , m_lineInfo(inLineInfo)
     62         , m_renderTextInfo(inRenderTextInfo)
     63         , m_lastFloatFromPreviousLine(inLastFloatFromPreviousLine)
     64         , m_width(lineWidth)
     65         , m_currWS(NORMAL)
     66         , m_lastWS(NORMAL)
     67         , m_preservesNewline(false)
     68         , m_atStart(true)
     69         , m_ignoringSpaces(false)
     70         , m_currentCharacterIsSpace(false)
     71         , m_currentCharacterShouldCollapseIfPreWap(false)
     72         , m_appliedStartWidth(appliedStartWidth)
     73         , m_includeEndWidth(true)
     74         , m_autoWrap(false)
     75         , m_autoWrapWasEverTrueOnLine(false)
     76         , m_floatsFitOnLine(true)
     77         , m_collapseWhiteSpace(false)
     78         , m_startingNewParagraph(m_lineInfo.previousLineBrokeCleanly())
     79         , m_allowImagesToBreak(!block->document().inQuirksMode() || !block->isTableCell() || !m_blockStyle->logicalWidth().isIntrinsicOrAuto())
     80         , m_atEnd(false)
     81         , m_lineMidpointState(resolver.midpointState())
     82     {
     83         m_lineInfo.setPreviousLineBrokeCleanly(false);
     84     }
     85 
     86     RenderObject* currentObject() { return m_current.object(); }
     87     InlineIterator lineBreak() { return m_lineBreak; }
     88     bool atEnd() { return m_atEnd; }
     89 
     90     void initializeForCurrentObject();
     91 
     92     void increment();
     93 
     94     void handleBR(EClear&);
     95     void handleOutOfFlowPositioned(Vector<RenderBox*>& positionedObjects);
     96     void handleFloat();
     97     void handleEmptyInline();
     98     void handleReplaced();
     99     bool handleText(WordMeasurements&, bool& hyphenated);
    100     void commitAndUpdateLineBreakIfNeeded();
    101     InlineIterator handleEndOfLine();
    102 
    103     void clearLineBreakIfFitsOnLine()
    104     {
    105         if (m_width.fitsOnLine() || m_lastWS == NOWRAP)
    106             m_lineBreak.clear();
    107     }
    108 
    109 private:
    110     void skipTrailingWhitespace(InlineIterator&, const LineInfo&);
    111 
    112     InlineBidiResolver& m_resolver;
    113 
    114     InlineIterator m_current;
    115     InlineIterator m_lineBreak;
    116     InlineIterator m_startOfIgnoredSpaces;
    117 
    118     RenderBlockFlow* m_block;
    119     RenderObject* m_lastObject;
    120     RenderObject* m_nextObject;
    121 
    122     RenderStyle* m_currentStyle;
    123     RenderStyle* m_blockStyle;
    124 
    125     LineInfo& m_lineInfo;
    126 
    127     RenderTextInfo& m_renderTextInfo;
    128 
    129     FloatingObject* m_lastFloatFromPreviousLine;
    130 
    131     LineWidth m_width;
    132 
    133     EWhiteSpace m_currWS;
    134     EWhiteSpace m_lastWS;
    135 
    136     bool m_preservesNewline;
    137     bool m_atStart;
    138     bool m_ignoringSpaces;
    139     bool m_currentCharacterIsSpace;
    140     bool m_currentCharacterShouldCollapseIfPreWap;
    141     bool m_appliedStartWidth;
    142     bool m_includeEndWidth;
    143     bool m_autoWrap;
    144     bool m_autoWrapWasEverTrueOnLine;
    145     bool m_floatsFitOnLine;
    146     bool m_collapseWhiteSpace;
    147     bool m_startingNewParagraph;
    148     bool m_allowImagesToBreak;
    149     bool m_atEnd;
    150 
    151     LineMidpointState& m_lineMidpointState;
    152 
    153     TrailingObjects m_trailingObjects;
    154 };
    155 
    156 inline bool shouldCollapseWhiteSpace(const RenderStyle* style, const LineInfo& lineInfo, WhitespacePosition whitespacePosition)
    157 {
    158     // CSS2 16.6.1
    159     // If a space (U+0020) at the beginning of a line has 'white-space' set to 'normal', 'nowrap', or 'pre-line', it is removed.
    160     // 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.
    161     // 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.
    162     return style->collapseWhiteSpace()
    163         || (whitespacePosition == TrailingWhitespace && style->whiteSpace() == PRE_WRAP && (!lineInfo.isEmpty() || !lineInfo.previousLineBrokeCleanly()));
    164 }
    165 
    166 inline bool requiresLineBoxForContent(RenderInline* flow, const LineInfo& lineInfo)
    167 {
    168     RenderObject* parent = flow->parent();
    169     if (flow->document().inNoQuirksMode()
    170         && (flow->style(lineInfo.isFirstLine())->lineHeight() != parent->style(lineInfo.isFirstLine())->lineHeight()
    171         || flow->style()->verticalAlign() != parent->style()->verticalAlign()
    172         || !parent->style()->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(flow->style()->font().fontMetrics())))
    173         return true;
    174     return false;
    175 }
    176 
    177 inline bool alwaysRequiresLineBox(RenderObject* flow)
    178 {
    179     // FIXME: Right now, we only allow line boxes for inlines that are truly empty.
    180     // We need to fix this, though, because at the very least, inlines containing only
    181     // ignorable whitespace should should also have line boxes.
    182     return isEmptyInline(flow) && toRenderInline(flow)->hasInlineDirectionBordersPaddingOrMargin();
    183 }
    184 
    185 inline bool requiresLineBox(const InlineIterator& it, const LineInfo& lineInfo = LineInfo(), WhitespacePosition whitespacePosition = LeadingWhitespace)
    186 {
    187     if (it.object()->isFloatingOrOutOfFlowPositioned())
    188         return false;
    189 
    190     if (it.object()->isRenderInline() && !alwaysRequiresLineBox(it.object()) && !requiresLineBoxForContent(toRenderInline(it.object()), lineInfo))
    191         return false;
    192 
    193     if (!shouldCollapseWhiteSpace(it.object()->style(), lineInfo, whitespacePosition) || it.object()->isBR())
    194         return true;
    195 
    196     UChar current = it.current();
    197     bool notJustWhitespace = current != ' ' && current != '\t' && current != softHyphen && (current != '\n' || it.object()->preservesNewline());
    198     return notJustWhitespace || isEmptyInline(it.object());
    199 }
    200 
    201 inline void setStaticPositions(RenderBlockFlow* block, RenderBox* child)
    202 {
    203     ASSERT(child->isOutOfFlowPositioned());
    204     // FIXME: The math here is actually not really right. It's a best-guess approximation that
    205     // will work for the common cases
    206     RenderObject* containerBlock = child->container();
    207     LayoutUnit blockHeight = block->logicalHeight();
    208     if (containerBlock->isRenderInline()) {
    209         // A relative positioned inline encloses us. In this case, we also have to determine our
    210         // position as though we were an inline. Set |staticInlinePosition| and |staticBlockPosition| on the relative positioned
    211         // inline so that we can obtain the value later.
    212         toRenderInline(containerBlock)->layer()->setStaticInlinePosition(block->startAlignedOffsetForLine(blockHeight, false));
    213         toRenderInline(containerBlock)->layer()->setStaticBlockPosition(blockHeight);
    214 
    215         // If |child| is a leading or trailing positioned object this is its only opportunity to ensure it moves with an inline
    216         // container changing width.
    217         child->moveWithEdgeOfInlineContainerIfNecessary(child->isHorizontalWritingMode());
    218     }
    219     block->updateStaticInlinePositionForChild(child, blockHeight);
    220     child->layer()->setStaticBlockPosition(blockHeight);
    221 }
    222 
    223 // FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building
    224 // line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned
    225 // elements quite right. In other words, we need to build this function's work into the normal line
    226 // object iteration process.
    227 // NB. this function will insert any floating elements that would otherwise
    228 // be skipped but it will not position them.
    229 inline void BreakingContext::skipTrailingWhitespace(InlineIterator& iterator, const LineInfo& lineInfo)
    230 {
    231     while (!iterator.atEnd() && !requiresLineBox(iterator, lineInfo, TrailingWhitespace)) {
    232         RenderObject* object = iterator.object();
    233         if (object->isOutOfFlowPositioned())
    234             setStaticPositions(m_block, toRenderBox(object));
    235         else if (object->isFloating())
    236             m_block->insertFloatingObject(toRenderBox(object));
    237         iterator.increment();
    238     }
    239 }
    240 
    241 inline void BreakingContext::initializeForCurrentObject()
    242 {
    243     m_currentStyle = m_current.object()->style();
    244     m_nextObject = bidiNextSkippingEmptyInlines(m_block, m_current.object());
    245     if (m_nextObject && m_nextObject->parent() && !m_nextObject->parent()->isDescendantOf(m_current.object()->parent()))
    246         m_includeEndWidth = true;
    247 
    248     m_currWS = m_current.object()->isReplaced() ? m_current.object()->parent()->style()->whiteSpace() : m_currentStyle->whiteSpace();
    249     m_lastWS = m_lastObject->isReplaced() ? m_lastObject->parent()->style()->whiteSpace() : m_lastObject->style()->whiteSpace();
    250 
    251     m_autoWrap = RenderStyle::autoWrap(m_currWS);
    252     m_autoWrapWasEverTrueOnLine = m_autoWrapWasEverTrueOnLine || m_autoWrap;
    253 
    254     m_preservesNewline = m_current.object()->isSVGInlineText() ? false : RenderStyle::preserveNewline(m_currWS);
    255 
    256     m_collapseWhiteSpace = RenderStyle::collapseWhiteSpace(m_currWS);
    257 }
    258 
    259 inline void BreakingContext::increment()
    260 {
    261     // Clear out our character space bool, since inline <pre>s don't collapse whitespace
    262     // with adjacent inline normal/nowrap spans.
    263     if (!m_collapseWhiteSpace)
    264         m_currentCharacterIsSpace = false;
    265 
    266     m_current.moveToStartOf(m_nextObject);
    267     m_atStart = false;
    268 }
    269 
    270 inline void BreakingContext::handleBR(EClear& clear)
    271 {
    272     if (m_width.fitsOnLine()) {
    273         RenderObject* br = m_current.object();
    274         m_lineBreak.moveToStartOf(br);
    275         m_lineBreak.increment();
    276 
    277         // A <br> always breaks a line, so don't let the line be collapsed
    278         // away. Also, the space at the end of a line with a <br> does not
    279         // get collapsed away. It only does this if the previous line broke
    280         // cleanly. Otherwise the <br> has no effect on whether the line is
    281         // empty or not.
    282         if (m_startingNewParagraph)
    283             m_lineInfo.setEmpty(false, m_block, &m_width);
    284         m_trailingObjects.clear();
    285         m_lineInfo.setPreviousLineBrokeCleanly(true);
    286 
    287         // A <br> with clearance always needs a linebox in case the lines below it get dirtied later and
    288         // need to check for floats to clear - so if we're ignoring spaces, stop ignoring them and add a
    289         // run for this object.
    290         if (m_ignoringSpaces && m_currentStyle->clear() != CNONE)
    291             m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(br);
    292 
    293         if (!m_lineInfo.isEmpty())
    294             clear = m_currentStyle->clear();
    295     }
    296     m_atEnd = true;
    297 }
    298 
    299 inline LayoutUnit borderPaddingMarginStart(RenderInline* child)
    300 {
    301     return child->marginStart() + child->paddingStart() + child->borderStart();
    302 }
    303 
    304 inline LayoutUnit borderPaddingMarginEnd(RenderInline* child)
    305 {
    306     return child->marginEnd() + child->paddingEnd() + child->borderEnd();
    307 }
    308 
    309 inline bool shouldAddBorderPaddingMargin(RenderObject* child, bool &checkSide)
    310 {
    311     if (!child || (child->isText() && !toRenderText(child)->textLength()))
    312         return true;
    313     checkSide = false;
    314     return checkSide;
    315 }
    316 
    317 inline LayoutUnit inlineLogicalWidth(RenderObject* child, bool start = true, bool end = true)
    318 {
    319     unsigned lineDepth = 1;
    320     LayoutUnit extraWidth = 0;
    321     RenderObject* parent = child->parent();
    322     while (parent->isRenderInline() && lineDepth++ < cMaxLineDepth) {
    323         RenderInline* parentAsRenderInline = toRenderInline(parent);
    324         if (!isEmptyInline(parentAsRenderInline)) {
    325             if (start && shouldAddBorderPaddingMargin(child->previousSibling(), start))
    326                 extraWidth += borderPaddingMarginStart(parentAsRenderInline);
    327             if (end && shouldAddBorderPaddingMargin(child->nextSibling(), end))
    328                 extraWidth += borderPaddingMarginEnd(parentAsRenderInline);
    329             if (!start && !end)
    330                 return extraWidth;
    331         }
    332         child = parent;
    333         parent = child->parent();
    334     }
    335     return extraWidth;
    336 }
    337 
    338 inline void BreakingContext::handleOutOfFlowPositioned(Vector<RenderBox*>& positionedObjects)
    339 {
    340     // If our original display wasn't an inline type, then we can
    341     // go ahead and determine our static inline position now.
    342     RenderBox* box = toRenderBox(m_current.object());
    343     bool isInlineType = box->style()->isOriginalDisplayInlineType();
    344     if (!isInlineType) {
    345         m_block->setStaticInlinePositionForChild(box, m_block->startOffsetForContent());
    346     } else {
    347         // If our original display was an INLINE type, then we can go ahead
    348         // and determine our static y position now.
    349         box->layer()->setStaticBlockPosition(m_block->logicalHeight());
    350     }
    351 
    352     // If we're ignoring spaces, we have to stop and include this object and
    353     // then start ignoring spaces again.
    354     if (isInlineType || box->container()->isRenderInline()) {
    355         if (m_ignoringSpaces)
    356             m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(box);
    357         m_trailingObjects.appendObjectIfNeeded(box);
    358     } else {
    359         positionedObjects.append(box);
    360     }
    361     m_width.addUncommittedWidth(inlineLogicalWidth(box).toFloat());
    362     // Reset prior line break context characters.
    363     m_renderTextInfo.m_lineBreakIterator.resetPriorContext();
    364 }
    365 
    366 inline void BreakingContext::handleFloat()
    367 {
    368     RenderBox* floatBox = toRenderBox(m_current.object());
    369     FloatingObject* floatingObject = m_block->insertFloatingObject(floatBox);
    370     // check if it fits in the current line.
    371     // If it does, position it now, otherwise, position
    372     // it after moving to next line (in newLine() func)
    373     // FIXME: Bug 110372: Properly position multiple stacked floats with non-rectangular shape outside.
    374     if (m_floatsFitOnLine && m_width.fitsOnLine(m_block->logicalWidthForFloat(floatingObject).toFloat(), ExcludeWhitespace)) {
    375         m_block->positionNewFloatOnLine(floatingObject, m_lastFloatFromPreviousLine, m_lineInfo, m_width);
    376         if (m_lineBreak.object() == m_current.object()) {
    377             ASSERT(!m_lineBreak.offset());
    378             m_lineBreak.increment();
    379         }
    380     } else {
    381         m_floatsFitOnLine = false;
    382     }
    383     // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for floating element.
    384     m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
    385 }
    386 
    387 // This is currently just used for list markers and inline flows that have line boxes. Neither should
    388 // have an effect on whitespace at the start of the line.
    389 inline bool shouldSkipWhitespaceAfterStartObject(RenderBlockFlow* block, RenderObject* o, LineMidpointState& lineMidpointState)
    390 {
    391     RenderObject* next = bidiNextSkippingEmptyInlines(block, o);
    392     while (next && next->isFloatingOrOutOfFlowPositioned())
    393         next = bidiNextSkippingEmptyInlines(block, next);
    394 
    395     if (next && !next->isBR() && next->isText() && toRenderText(next)->textLength() > 0) {
    396         RenderText* nextText = toRenderText(next);
    397         UChar nextChar = nextText->characterAt(0);
    398         if (nextText->style()->isCollapsibleWhiteSpace(nextChar)) {
    399             lineMidpointState.startIgnoringSpaces(InlineIterator(0, o, 0));
    400             return true;
    401         }
    402     }
    403 
    404     return false;
    405 }
    406 
    407 inline void BreakingContext::handleEmptyInline()
    408 {
    409     // This should only end up being called on empty inlines
    410     ASSERT(isEmptyInline(m_current.object()));
    411 
    412     RenderInline* flowBox = toRenderInline(m_current.object());
    413 
    414     bool requiresLineBox = alwaysRequiresLineBox(m_current.object());
    415     if (requiresLineBox || requiresLineBoxForContent(flowBox, m_lineInfo)) {
    416         // An empty inline that only has line-height, vertical-align or font-metrics will
    417         // not force linebox creation (and thus affect the height of the line) if the rest of the line is empty.
    418         if (requiresLineBox)
    419             m_lineInfo.setEmpty(false, m_block, &m_width);
    420         if (m_ignoringSpaces) {
    421             // If we are in a run of ignored spaces then ensure we get a linebox if lineboxes are eventually
    422             // created for the line...
    423             m_trailingObjects.clear();
    424             m_lineMidpointState.ensureLineBoxInsideIgnoredSpaces(m_current.object());
    425         } else if (m_blockStyle->collapseWhiteSpace() && m_resolver.position().object() == m_current.object()
    426             && shouldSkipWhitespaceAfterStartObject(m_block, m_current.object(), m_lineMidpointState)) {
    427             // If this object is at the start of the line, we need to behave like list markers and
    428             // start ignoring spaces.
    429             m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = true;
    430             m_ignoringSpaces = true;
    431         } else {
    432             // If we are after a trailing space but aren't ignoring spaces yet then ensure we get a linebox
    433             // if we encounter collapsible whitepace.
    434             m_trailingObjects.appendObjectIfNeeded(m_current.object());
    435         }
    436     }
    437 
    438     m_width.addUncommittedWidth((inlineLogicalWidth(m_current.object()) + borderPaddingMarginStart(flowBox) + borderPaddingMarginEnd(flowBox)).toFloat());
    439 }
    440 
    441 inline void BreakingContext::handleReplaced()
    442 {
    443     RenderBox* replacedBox = toRenderBox(m_current.object());
    444 
    445     if (m_atStart)
    446         m_width.updateAvailableWidth(replacedBox->logicalHeight());
    447 
    448     // Break on replaced elements if either has normal white-space.
    449     if ((m_autoWrap || RenderStyle::autoWrap(m_lastWS)) && (!m_current.object()->isImage() || m_allowImagesToBreak)) {
    450         m_width.commit();
    451         m_lineBreak.moveToStartOf(m_current.object());
    452     }
    453 
    454     if (m_ignoringSpaces)
    455         m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.object(), 0));
    456 
    457     m_lineInfo.setEmpty(false, m_block, &m_width);
    458     m_ignoringSpaces = false;
    459     m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = false;
    460     m_trailingObjects.clear();
    461 
    462     // Optimize for a common case. If we can't find whitespace after the list
    463     // item, then this is all moot.
    464     LayoutUnit replacedLogicalWidth = m_block->logicalWidthForChild(replacedBox) + m_block->marginStartForChild(replacedBox) + m_block->marginEndForChild(replacedBox) + inlineLogicalWidth(m_current.object());
    465     if (m_current.object()->isListMarker()) {
    466         if (m_blockStyle->collapseWhiteSpace() && shouldSkipWhitespaceAfterStartObject(m_block, m_current.object(), m_lineMidpointState)) {
    467             // Like with inline flows, we start ignoring spaces to make sure that any
    468             // additional spaces we see will be discarded.
    469             m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = true;
    470             m_ignoringSpaces = true;
    471         }
    472         if (toRenderListMarker(m_current.object())->isInside())
    473             m_width.addUncommittedWidth(replacedLogicalWidth.toFloat());
    474     } else {
    475         m_width.addUncommittedWidth(replacedLogicalWidth.toFloat());
    476     }
    477     if (m_current.object()->isRubyRun())
    478         m_width.applyOverhang(toRenderRubyRun(m_current.object()), m_lastObject, m_nextObject);
    479     // Update prior line break context characters, using U+FFFD (OBJECT REPLACEMENT CHARACTER) for replaced element.
    480     m_renderTextInfo.m_lineBreakIterator.updatePriorContext(replacementCharacter);
    481 }
    482 
    483 inline bool iteratorIsBeyondEndOfRenderCombineText(const InlineIterator& iter, RenderCombineText* renderer)
    484 {
    485     return iter.object() == renderer && iter.offset() >= renderer->textLength();
    486 }
    487 
    488 inline void nextCharacter(UChar& currentCharacter, UChar& lastCharacter, UChar& secondToLastCharacter)
    489 {
    490     secondToLastCharacter = lastCharacter;
    491     lastCharacter = currentCharacter;
    492 }
    493 
    494 inline float firstPositiveWidth(const WordMeasurements& wordMeasurements)
    495 {
    496     for (size_t i = 0; i < wordMeasurements.size(); ++i) {
    497         if (wordMeasurements[i].width > 0)
    498             return wordMeasurements[i].width;
    499     }
    500     return 0;
    501 }
    502 
    503 inline float measureHyphenWidth(RenderText* renderer, const Font& font, TextDirection textDirection)
    504 {
    505     RenderStyle* style = renderer->style();
    506     return font.width(constructTextRun(renderer, font,
    507         style->hyphenString().string(), style, style->direction()));
    508 }
    509 
    510 ALWAYS_INLINE TextDirection textDirectionFromUnicode(WTF::Unicode::Direction direction)
    511 {
    512     return direction == WTF::Unicode::RightToLeft
    513         || direction == WTF::Unicode::RightToLeftArabic ? RTL : LTR;
    514 }
    515 
    516 ALWAYS_INLINE float textWidth(RenderText* text, unsigned from, unsigned len, const Font& font, float xPos, bool isFixedPitch, bool collapseWhiteSpace, HashSet<const SimpleFontData*>* fallbackFonts = 0)
    517 {
    518     GlyphOverflow glyphOverflow;
    519     if (isFixedPitch || (!from && len == text->textLength()) || text->style()->hasTextCombine())
    520         return text->width(from, len, font, xPos, text->style()->direction(), fallbackFonts, &glyphOverflow);
    521 
    522     TextRun run = constructTextRun(text, font, text, from, len, text->style());
    523     run.setCharacterScanForCodePath(!text->canUseSimpleFontCodePath());
    524     run.setUseComplexCodePath(!text->canUseSimpleFontCodePath());
    525     run.setTabSize(!collapseWhiteSpace, text->style()->tabSize());
    526     run.setXPos(xPos);
    527     return font.width(run, fallbackFonts, &glyphOverflow);
    528 }
    529 
    530 inline bool BreakingContext::handleText(WordMeasurements& wordMeasurements, bool& hyphenated)
    531 {
    532     if (!m_current.offset())
    533         m_appliedStartWidth = false;
    534 
    535     RenderText* renderText = toRenderText(m_current.object());
    536 
    537     bool isSVGText = renderText->isSVGInlineText();
    538 
    539     // If we have left a no-wrap inline and entered an autowrap inline while ignoring spaces
    540     // then we need to mark the start of the autowrap inline as a potential linebreak now.
    541     if (m_autoWrap && !RenderStyle::autoWrap(m_lastWS) && m_ignoringSpaces) {
    542         m_width.commit();
    543         m_lineBreak.moveToStartOf(m_current.object());
    544     }
    545 
    546     if (renderText->style()->hasTextCombine() && m_current.object()->isCombineText() && !toRenderCombineText(m_current.object())->isCombined()) {
    547         RenderCombineText* combineRenderer = toRenderCombineText(m_current.object());
    548         combineRenderer->combineText();
    549         // The length of the renderer's text may have changed. Increment stale iterator positions
    550         if (iteratorIsBeyondEndOfRenderCombineText(m_lineBreak, combineRenderer)) {
    551             ASSERT(iteratorIsBeyondEndOfRenderCombineText(m_resolver.position(), combineRenderer));
    552             m_lineBreak.increment();
    553             m_resolver.position().increment(&m_resolver);
    554         }
    555     }
    556 
    557     RenderStyle* style = renderText->style(m_lineInfo.isFirstLine());
    558     const Font& font = style->font();
    559     bool isFixedPitch = font.isFixedPitch();
    560 
    561     unsigned lastSpace = m_current.offset();
    562     float wordSpacing = m_currentStyle->wordSpacing();
    563     float lastSpaceWordSpacing = 0;
    564     float wordSpacingForWordMeasurement = 0;
    565 
    566     float wrapW = m_width.uncommittedWidth() + inlineLogicalWidth(m_current.object(), !m_appliedStartWidth, true);
    567     float charWidth = 0;
    568     // Auto-wrapping text should wrap in the middle of a word only if it could not wrap before the word,
    569     // which is only possible if the word is the first thing on the line, that is, if |w| is zero.
    570     bool breakWords = m_currentStyle->breakWords() && ((m_autoWrap && !m_width.committedWidth()) || m_currWS == PRE);
    571     bool midWordBreak = false;
    572     bool breakAll = m_currentStyle->wordBreak() == BreakAllWordBreak && m_autoWrap;
    573     float hyphenWidth = 0;
    574 
    575     if (isSVGText) {
    576         breakWords = false;
    577         breakAll = false;
    578     }
    579 
    580     if (renderText->isWordBreak()) {
    581         m_width.commit();
    582         m_lineBreak.moveToStartOf(m_current.object());
    583         ASSERT(m_current.offset() == renderText->textLength());
    584     }
    585 
    586     if (m_renderTextInfo.m_text != renderText) {
    587         m_renderTextInfo.m_text = renderText;
    588         m_renderTextInfo.m_font = &font;
    589         m_renderTextInfo.m_lineBreakIterator.resetStringAndReleaseIterator(renderText->text(), style->locale());
    590     } else if (m_renderTextInfo.m_font != &font) {
    591         m_renderTextInfo.m_font = &font;
    592     }
    593 
    594     // Non-zero only when kerning is enabled, in which case we measure
    595     // words with their trailing space, then subtract its width.
    596     float wordTrailingSpaceWidth = (font.fontDescription().typesettingFeatures() & Kerning) ?
    597         font.width(constructTextRun(renderText, font, &space, 1, style, style->direction())) + wordSpacing
    598         : 0;
    599 
    600     UChar lastCharacter = m_renderTextInfo.m_lineBreakIterator.lastCharacter();
    601     UChar secondToLastCharacter = m_renderTextInfo.m_lineBreakIterator.secondToLastCharacter();
    602     for (; m_current.offset() < renderText->textLength(); m_current.fastIncrementInTextNode()) {
    603         bool previousCharacterIsSpace = m_currentCharacterIsSpace;
    604         bool previousCharacterShouldCollapseIfPreWap = m_currentCharacterShouldCollapseIfPreWap;
    605         UChar c = m_current.current();
    606         m_currentCharacterShouldCollapseIfPreWap = m_currentCharacterIsSpace = c == ' ' || c == '\t' || (!m_preservesNewline && (c == '\n'));
    607 
    608         if (!m_collapseWhiteSpace || !m_currentCharacterIsSpace)
    609             m_lineInfo.setEmpty(false, m_block, &m_width);
    610 
    611         if (c == softHyphen && m_autoWrap && !hyphenWidth) {
    612             hyphenWidth = measureHyphenWidth(renderText, font, textDirectionFromUnicode(m_resolver.position().direction()));
    613             m_width.addUncommittedWidth(hyphenWidth);
    614         }
    615 
    616         bool applyWordSpacing = false;
    617 
    618         if ((breakAll || breakWords) && !midWordBreak) {
    619             wrapW += charWidth;
    620             bool midWordBreakIsBeforeSurrogatePair = U16_IS_LEAD(c) && m_current.offset() + 1 < renderText->textLength() && U16_IS_TRAIL((*renderText)[m_current.offset() + 1]);
    621             charWidth = textWidth(renderText, m_current.offset(), midWordBreakIsBeforeSurrogatePair ? 2 : 1, font, m_width.committedWidth() + wrapW, isFixedPitch, m_collapseWhiteSpace);
    622             midWordBreak = m_width.committedWidth() + wrapW + charWidth > m_width.availableWidth();
    623         }
    624 
    625         int nextBreakablePosition = m_current.nextBreakablePosition();
    626         bool betweenWords = c == '\n' || (m_currWS != PRE && !m_atStart && isBreakable(m_renderTextInfo.m_lineBreakIterator, m_current.offset(), nextBreakablePosition));
    627         m_current.setNextBreakablePosition(nextBreakablePosition);
    628 
    629         if (betweenWords || midWordBreak) {
    630             bool stoppedIgnoringSpaces = false;
    631             if (m_ignoringSpaces) {
    632                 lastSpaceWordSpacing = 0;
    633                 if (!m_currentCharacterIsSpace) {
    634                     // Stop ignoring spaces and begin at this
    635                     // new point.
    636                     m_ignoringSpaces = false;
    637                     wordSpacingForWordMeasurement = 0;
    638                     lastSpace = m_current.offset(); // e.g., "Foo    goo", don't add in any of the ignored spaces.
    639                     m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.object(), m_current.offset()));
    640                     stoppedIgnoringSpaces = true;
    641                 } else {
    642                     // Just keep ignoring these spaces.
    643                     nextCharacter(c, lastCharacter, secondToLastCharacter);
    644                     continue;
    645                 }
    646             }
    647 
    648             wordMeasurements.grow(wordMeasurements.size() + 1);
    649             WordMeasurement& wordMeasurement = wordMeasurements.last();
    650 
    651             wordMeasurement.renderer = renderText;
    652             wordMeasurement.endOffset = m_current.offset();
    653             wordMeasurement.startOffset = lastSpace;
    654 
    655             float additionalTempWidth;
    656             if (wordTrailingSpaceWidth && c == ' ')
    657                 additionalTempWidth = textWidth(renderText, lastSpace, m_current.offset() + 1 - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts) - wordTrailingSpaceWidth;
    658             else
    659                 additionalTempWidth = textWidth(renderText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts);
    660 
    661             wordMeasurement.width = additionalTempWidth + wordSpacingForWordMeasurement;
    662             additionalTempWidth += lastSpaceWordSpacing;
    663             m_width.addUncommittedWidth(additionalTempWidth);
    664 
    665             if (m_collapseWhiteSpace && previousCharacterIsSpace && m_currentCharacterIsSpace && additionalTempWidth)
    666                 m_width.setTrailingWhitespaceWidth(additionalTempWidth);
    667 
    668             if (!m_appliedStartWidth) {
    669                 m_width.addUncommittedWidth(inlineLogicalWidth(m_current.object(), true, false).toFloat());
    670                 m_appliedStartWidth = true;
    671             }
    672 
    673             applyWordSpacing = wordSpacing && m_currentCharacterIsSpace;
    674 
    675             if (!m_width.committedWidth() && m_autoWrap && !m_width.fitsOnLine())
    676                 m_width.fitBelowFloats(m_lineInfo.isFirstLine());
    677 
    678             if (m_autoWrap || breakWords) {
    679                 // If we break only after white-space, consider the current character
    680                 // as candidate width for this line.
    681                 bool lineWasTooWide = false;
    682                 if (m_width.fitsOnLine() && m_currentCharacterIsSpace && m_currentStyle->breakOnlyAfterWhiteSpace() && !midWordBreak) {
    683                     float charWidth = textWidth(renderText, m_current.offset(), 1, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts) + (applyWordSpacing ? wordSpacing : 0);
    684                     // Check if line is too big even without the extra space
    685                     // at the end of the line. If it is not, do nothing.
    686                     // If the line needs the extra whitespace to be too long,
    687                     // then move the line break to the space and skip all
    688                     // additional whitespace.
    689                     if (!m_width.fitsOnLine(charWidth)) {
    690                         lineWasTooWide = true;
    691                         m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
    692                         skipTrailingWhitespace(m_lineBreak, m_lineInfo);
    693                     }
    694                 }
    695                 if (lineWasTooWide || !m_width.fitsOnLine()) {
    696                     if (m_lineBreak.atTextParagraphSeparator()) {
    697                         if (!stoppedIgnoringSpaces && m_current.offset() > 0)
    698                             m_lineMidpointState.ensureCharacterGetsLineBox(m_current);
    699                         m_lineBreak.increment();
    700                         m_lineInfo.setPreviousLineBrokeCleanly(true);
    701                         wordMeasurement.endOffset = m_lineBreak.offset();
    702                     }
    703                     if (m_lineBreak.object() && m_lineBreak.offset() && m_lineBreak.object()->isText() && toRenderText(m_lineBreak.object())->textLength() && toRenderText(m_lineBreak.object())->characterAt(m_lineBreak.offset() - 1) == softHyphen)
    704                         hyphenated = true;
    705                     if (m_lineBreak.offset() && m_lineBreak.offset() != (unsigned)wordMeasurement.endOffset && !wordMeasurement.width) {
    706                         if (charWidth) {
    707                             wordMeasurement.endOffset = m_lineBreak.offset();
    708                             wordMeasurement.width = charWidth;
    709                         }
    710                     }
    711                     // Didn't fit. Jump to the end unless there's still an opportunity to collapse whitespace.
    712                     if (m_ignoringSpaces || !m_collapseWhiteSpace || !m_currentCharacterIsSpace || !previousCharacterIsSpace) {
    713                         m_atEnd = true;
    714                         return false;
    715                     }
    716                 } else {
    717                     if (!betweenWords || (midWordBreak && !m_autoWrap))
    718                         m_width.addUncommittedWidth(-additionalTempWidth);
    719                     if (hyphenWidth) {
    720                         // Subtract the width of the soft hyphen out since we fit on a line.
    721                         m_width.addUncommittedWidth(-hyphenWidth);
    722                         hyphenWidth = 0;
    723                     }
    724                 }
    725             }
    726 
    727             if (c == '\n' && m_preservesNewline) {
    728                 if (!stoppedIgnoringSpaces && m_current.offset())
    729                     m_lineMidpointState.ensureCharacterGetsLineBox(m_current);
    730                 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
    731                 m_lineBreak.increment();
    732                 m_lineInfo.setPreviousLineBrokeCleanly(true);
    733                 return true;
    734             }
    735 
    736             if (m_autoWrap && betweenWords) {
    737                 m_width.commit();
    738                 wrapW = 0;
    739                 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
    740                 // Auto-wrapping text should not wrap in the middle of a word once it has had an
    741                 // opportunity to break after a word.
    742                 breakWords = false;
    743             }
    744 
    745             if (midWordBreak && !U16_IS_TRAIL(c) && !(WTF::Unicode::category(c) & (WTF::Unicode::Mark_NonSpacing | WTF::Unicode::Mark_Enclosing | WTF::Unicode::Mark_SpacingCombining))) {
    746                 // Remember this as a breakable position in case
    747                 // adding the end width forces a break.
    748                 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
    749                 midWordBreak &= (breakWords || breakAll);
    750             }
    751 
    752             if (betweenWords) {
    753                 lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
    754                 wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurement.width) ? wordSpacing : 0;
    755                 lastSpace = m_current.offset();
    756             }
    757 
    758             if (!m_ignoringSpaces && m_currentStyle->collapseWhiteSpace()) {
    759                 // If we encounter a newline, or if we encounter a
    760                 // second space, we need to go ahead and break up this
    761                 // run and enter a mode where we start collapsing spaces.
    762                 if (m_currentCharacterIsSpace && previousCharacterIsSpace) {
    763                     m_ignoringSpaces = true;
    764 
    765                     // We just entered a mode where we are ignoring
    766                     // spaces. Create a midpoint to terminate the run
    767                     // before the second space.
    768                     m_lineMidpointState.startIgnoringSpaces(m_startOfIgnoredSpaces);
    769                     m_trailingObjects.updateMidpointsForTrailingObjects(m_lineMidpointState, InlineIterator(), TrailingObjects::DoNotCollapseFirstSpace);
    770                 }
    771             }
    772         } else if (m_ignoringSpaces) {
    773             // Stop ignoring spaces and begin at this
    774             // new point.
    775             m_ignoringSpaces = false;
    776             lastSpaceWordSpacing = applyWordSpacing ? wordSpacing : 0;
    777             wordSpacingForWordMeasurement = (applyWordSpacing && wordMeasurements.last().width) ? wordSpacing : 0;
    778             lastSpace = m_current.offset(); // e.g., "Foo    goo", don't add in any of the ignored spaces.
    779             m_lineMidpointState.stopIgnoringSpaces(InlineIterator(0, m_current.object(), m_current.offset()));
    780         }
    781 
    782         if (isSVGText && m_current.offset()) {
    783             // Force creation of new InlineBoxes for each absolute positioned character (those that start new text chunks).
    784             if (toRenderSVGInlineText(renderText)->characterStartsNewTextChunk(m_current.offset()))
    785                 m_lineMidpointState.ensureCharacterGetsLineBox(m_current);
    786         }
    787 
    788         if (m_currentCharacterIsSpace && !previousCharacterIsSpace) {
    789             m_startOfIgnoredSpaces.setObject(m_current.object());
    790             m_startOfIgnoredSpaces.setOffset(m_current.offset());
    791         }
    792 
    793         if (!m_currentCharacterIsSpace && previousCharacterShouldCollapseIfPreWap) {
    794             if (m_autoWrap && m_currentStyle->breakOnlyAfterWhiteSpace())
    795                 m_lineBreak.moveTo(m_current.object(), m_current.offset(), m_current.nextBreakablePosition());
    796         }
    797 
    798         if (m_collapseWhiteSpace && m_currentCharacterIsSpace && !m_ignoringSpaces)
    799             m_trailingObjects.setTrailingWhitespace(toRenderText(m_current.object()));
    800         else if (!m_currentStyle->collapseWhiteSpace() || !m_currentCharacterIsSpace)
    801             m_trailingObjects.clear();
    802 
    803         m_atStart = false;
    804         nextCharacter(c, lastCharacter, secondToLastCharacter);
    805     }
    806 
    807     m_renderTextInfo.m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
    808 
    809     wordMeasurements.grow(wordMeasurements.size() + 1);
    810     WordMeasurement& wordMeasurement = wordMeasurements.last();
    811     wordMeasurement.renderer = renderText;
    812 
    813     // IMPORTANT: current.m_pos is > length here!
    814     float additionalTempWidth = m_ignoringSpaces ? 0 : textWidth(renderText, lastSpace, m_current.offset() - lastSpace, font, m_width.currentWidth(), isFixedPitch, m_collapseWhiteSpace, &wordMeasurement.fallbackFonts);
    815     wordMeasurement.startOffset = lastSpace;
    816     wordMeasurement.endOffset = m_current.offset();
    817     wordMeasurement.width = m_ignoringSpaces ? 0 : additionalTempWidth + wordSpacingForWordMeasurement;
    818     additionalTempWidth += lastSpaceWordSpacing;
    819 
    820     LayoutUnit inlineLogicalTempWidth = inlineLogicalWidth(m_current.object(), !m_appliedStartWidth, m_includeEndWidth);
    821     m_width.addUncommittedWidth(additionalTempWidth + inlineLogicalTempWidth);
    822 
    823     if (m_collapseWhiteSpace && m_currentCharacterIsSpace && additionalTempWidth)
    824         m_width.setTrailingWhitespaceWidth(additionalTempWidth + inlineLogicalTempWidth);
    825 
    826     m_includeEndWidth = false;
    827 
    828     if (!m_width.fitsOnLine()) {
    829         if (!hyphenated && m_lineBreak.previousInSameNode() == softHyphen) {
    830             hyphenated = true;
    831             m_atEnd = true;
    832         }
    833     }
    834     return false;
    835 }
    836 
    837 inline void BreakingContext::commitAndUpdateLineBreakIfNeeded()
    838 {
    839     bool checkForBreak = m_autoWrap;
    840     if (m_width.committedWidth() && !m_width.fitsOnLine() && m_lineBreak.object() && m_currWS == NOWRAP) {
    841         checkForBreak = true;
    842     } else if (m_nextObject && m_current.object()->isText() && m_nextObject->isText() && !m_nextObject->isBR() && (m_autoWrap || m_nextObject->style()->autoWrap())) {
    843         if (m_autoWrap && m_currentCharacterIsSpace) {
    844             checkForBreak = true;
    845         } else {
    846             RenderText* nextText = toRenderText(m_nextObject);
    847             if (nextText->textLength()) {
    848                 UChar c = nextText->characterAt(0);
    849                 // If the next item on the line is text, and if we did not end with
    850                 // a space, then the next text run continues our word (and so it needs to
    851                 // keep adding to the uncommitted width. Just update and continue.
    852                 checkForBreak = !m_currentCharacterIsSpace && (c == ' ' || c == '\t' || (c == '\n' && !m_nextObject->preservesNewline()));
    853             } else if (nextText->isWordBreak()) {
    854                 checkForBreak = true;
    855             }
    856 
    857             if (!m_width.fitsOnLine() && !m_width.committedWidth())
    858                 m_width.fitBelowFloats(m_lineInfo.isFirstLine());
    859 
    860             bool canPlaceOnLine = m_width.fitsOnLine() || !m_autoWrapWasEverTrueOnLine;
    861             if (canPlaceOnLine && checkForBreak) {
    862                 m_width.commit();
    863                 m_lineBreak.moveToStartOf(m_nextObject);
    864             }
    865         }
    866     }
    867 
    868     ASSERT_WITH_SECURITY_IMPLICATION(m_currentStyle->refCount() > 0);
    869     if (checkForBreak && !m_width.fitsOnLine()) {
    870         // if we have floats, try to get below them.
    871         if (m_currentCharacterIsSpace && !m_ignoringSpaces && m_currentStyle->collapseWhiteSpace())
    872             m_trailingObjects.clear();
    873 
    874         if (m_width.committedWidth()) {
    875             m_atEnd = true;
    876             return;
    877         }
    878 
    879         m_width.fitBelowFloats(m_lineInfo.isFirstLine());
    880 
    881         // |width| may have been adjusted because we got shoved down past a float (thus
    882         // giving us more room), so we need to retest, and only jump to
    883         // the end label if we still don't fit on the line. -dwh
    884         if (!m_width.fitsOnLine()) {
    885             m_atEnd = true;
    886             return;
    887         }
    888     } else if (m_blockStyle->autoWrap() && !m_width.fitsOnLine() && !m_width.committedWidth()) {
    889         // If the container autowraps but the current child does not then we still need to ensure that it
    890         // wraps and moves below any floats.
    891         m_width.fitBelowFloats(m_lineInfo.isFirstLine());
    892     }
    893 
    894     if (!m_current.object()->isFloatingOrOutOfFlowPositioned()) {
    895         m_lastObject = m_current.object();
    896         if (m_lastObject->isReplaced() && m_autoWrap && (!m_lastObject->isImage() || m_allowImagesToBreak) && (!m_lastObject->isListMarker() || toRenderListMarker(m_lastObject)->isInside())) {
    897             m_width.commit();
    898             m_lineBreak.moveToStartOf(m_nextObject);
    899         }
    900     }
    901 }
    902 
    903 inline IndentTextOrNot requiresIndent(bool isFirstLine, bool isAfterHardLineBreak, RenderStyle* style)
    904 {
    905     IndentTextOrNot shouldIndentText = DoNotIndentText;
    906     if (isFirstLine || (isAfterHardLineBreak && style->textIndentLine()) == TextIndentEachLine)
    907         shouldIndentText = IndentText;
    908 
    909     if (style->textIndentType() == TextIndentHanging)
    910         shouldIndentText = shouldIndentText == IndentText ? DoNotIndentText : IndentText;
    911 
    912     return shouldIndentText;
    913 }
    914 
    915 }
    916 
    917 #endif // BreakingContextInlineHeaders_h
    918