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