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