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 ChangSeok Oh <shivamidow@gmail.com>
6 * Copyright (C) 2013 Adobe Systems Inc. All right reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 *
23 */
24
25#include "config.h"
26#include "LineBreaker.h"
27
28#include "BreakingContext.h"
29#include "RenderCombineText.h"
30
31namespace WebCore {
32
33void LineBreaker::reset()
34{
35 m_positionedObjects.clear();
36 m_hyphenated = false;
37 m_clear = Clear::None;
38}
39
40// FIXME: The entire concept of the skipTrailingWhitespace function is flawed, since we really need to be building
41// line boxes even for containers that may ultimately collapse away. Otherwise we'll never get positioned
42// elements quite right. In other words, we need to build this function's work into the normal line
43// object iteration process.
44// NB. this function will insert any floating elements that would otherwise
45// be skipped but it will not position them.
46void LineBreaker::skipTrailingWhitespace(InlineIterator& iterator, const LineInfo& lineInfo)
47{
48 while (!iterator.atEnd() && !requiresLineBox(iterator, lineInfo, TrailingWhitespace)) {
49 RenderObject& object = *iterator.renderer();
50 if (object.isOutOfFlowPositioned())
51 setStaticPositions(m_block, downcast<RenderBox>(object), DoNotIndentText);
52 else if (object.isFloating())
53 m_block.insertFloatingObject(downcast<RenderBox>(object));
54 iterator.increment();
55 }
56}
57
58void LineBreaker::skipLeadingWhitespace(InlineBidiResolver& resolver, LineInfo& lineInfo, FloatingObject* lastFloatFromPreviousLine, LineWidth& width)
59{
60 while (!resolver.position().atEnd() && !requiresLineBox(resolver.position(), lineInfo, LeadingWhitespace)) {
61 RenderObject& object = *resolver.position().renderer();
62 if (object.isOutOfFlowPositioned()) {
63 setStaticPositions(m_block, downcast<RenderBox>(object), width.shouldIndentText());
64 if (object.style().isOriginalDisplayInlineType()) {
65 resolver.runs().appendRun(std::make_unique<BidiRun>(0, 1, object, resolver.context(), resolver.dir()));
66 lineInfo.incrementRunsFromLeadingWhitespace();
67 }
68 } else if (object.isFloating())
69 m_block.positionNewFloatOnLine(*m_block.insertFloatingObject(downcast<RenderBox>(object)), lastFloatFromPreviousLine, lineInfo, width);
70 else if (object.style().hasTextCombine() && is<RenderCombineText>(object)) {
71 downcast<RenderCombineText>(object).combineTextIfNeeded();
72 if (downcast<RenderCombineText>(object).isCombined())
73 continue;
74 }
75 resolver.increment();
76 }
77 resolver.commitExplicitEmbedding();
78}
79
80InlineIterator LineBreaker::nextLineBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements& wordMeasurements)
81{
82 reset();
83
84 ASSERT(resolver.position().root() == &m_block);
85
86 bool appliedStartWidth = resolver.position().offset();
87
88 LineWidth width(m_block, lineInfo.isFirstLine(), requiresIndent(lineInfo.isFirstLine(), lineInfo.previousLineBrokeCleanly(), m_block.style()));
89
90 skipLeadingWhitespace(resolver, lineInfo, lastFloatFromPreviousLine, width);
91
92 if (resolver.position().atEnd())
93 return resolver.position();
94
95 BreakingContext context(*this, resolver, lineInfo, width, renderTextInfo, lastFloatFromPreviousLine, appliedStartWidth, m_block);
96
97 while (context.currentObject()) {
98 context.initializeForCurrentObject();
99 if (context.currentObject()->isBR()) {
100 context.handleBR(m_clear);
101 } else if (context.currentObject()->isOutOfFlowPositioned()) {
102 context.handleOutOfFlowPositioned(m_positionedObjects);
103 } else if (context.currentObject()->isFloating()) {
104 context.handleFloat();
105 } else if (context.currentObject()->isRenderInline()) {
106 context.handleEmptyInline();
107 } else if (context.currentObject()->isReplaced()) {
108 context.handleReplaced();
109 } else if (context.currentObject()->isText()) {
110 if (context.handleText(wordMeasurements, m_hyphenated, consecutiveHyphenatedLines)) {
111 // We've hit a hard text line break. Our line break iterator is updated, so early return.
112 return context.lineBreak();
113 }
114 } else if (context.currentObject()->isLineBreakOpportunity())
115 context.commitLineBreakAtCurrentWidth(*context.currentObject());
116 else
117 ASSERT_NOT_REACHED();
118
119 if (context.atEnd())
120 return context.handleEndOfLine();
121
122 context.commitAndUpdateLineBreakIfNeeded();
123
124 if (context.atEnd())
125 return context.handleEndOfLine();
126
127 context.increment();
128 }
129
130 context.clearLineBreakIfFitsOnLine(true);
131
132 return context.handleEndOfLine();
133}
134
135}
136