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 "BreakingContextInlineHeaders.h"
29#include "RenderCombineText.h"
30
31namespace WebCore {
32
33void LineBreaker::reset()
34{
35    m_positionedObjects.clear();
36    m_hyphenated = false;
37    m_clear = CNONE;
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, toRenderBox(object));
52        else if (object.isFloating())
53            m_block.insertFloatingObject(toRenderBox(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, toRenderBox(object));
64            if (object.style().isOriginalDisplayInlineType()) {
65                resolver.runs().addRun(new BidiRun(0, 1, object, resolver.context(), resolver.dir()));
66                lineInfo.incrementRunsFromLeadingWhitespace();
67            }
68        } else if (object.isFloating())
69            m_block.positionNewFloatOnLine(m_block.insertFloatingObject(toRenderBox(object)), lastFloatFromPreviousLine, lineInfo, width);
70        else if (object.isText() && object.style().hasTextCombine() && object.isCombineText() && !toRenderCombineText(object).isCombined()) {
71            toRenderCombineText(object).combineText();
72            if (toRenderCombineText(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    return nextSegmentBreak(resolver, lineInfo, renderTextInfo, lastFloatFromPreviousLine, consecutiveHyphenatedLines, wordMeasurements);
83}
84
85InlineIterator LineBreaker::nextSegmentBreak(InlineBidiResolver& resolver, LineInfo& lineInfo, RenderTextInfo& renderTextInfo, FloatingObject* lastFloatFromPreviousLine, unsigned consecutiveHyphenatedLines, WordMeasurements& wordMeasurements)
86{
87    reset();
88
89    ASSERT(resolver.position().root() == &m_block);
90
91    bool appliedStartWidth = resolver.position().offset();
92
93    LineWidth width(m_block, lineInfo.isFirstLine(), requiresIndent(lineInfo.isFirstLine(), lineInfo.previousLineBrokeCleanly(), m_block.style()));
94
95    skipLeadingWhitespace(resolver, lineInfo, lastFloatFromPreviousLine, width);
96
97    if (resolver.position().atEnd())
98        return resolver.position();
99
100    BreakingContext context(*this, resolver, lineInfo, width, renderTextInfo, lastFloatFromPreviousLine, appliedStartWidth, m_block);
101
102    while (context.currentObject()) {
103        context.initializeForCurrentObject();
104        if (context.currentObject()->isBR()) {
105            context.handleBR(m_clear);
106        } else if (context.currentObject()->isOutOfFlowPositioned()) {
107            context.handleOutOfFlowPositioned(m_positionedObjects);
108        } else if (context.currentObject()->isFloating()) {
109            context.handleFloat();
110        } else if (context.currentObject()->isRenderInline()) {
111            context.handleEmptyInline();
112        } else if (context.currentObject()->isReplaced()) {
113            context.handleReplaced();
114        } else if (context.currentObject()->isText()) {
115            if (context.handleText(wordMeasurements, m_hyphenated, consecutiveHyphenatedLines)) {
116                // We've hit a hard text line break. Our line break iterator is updated, so go ahead and early return.
117                return context.lineBreak();
118            }
119        } else if (context.currentObject()->isLineBreakOpportunity())
120            context.commitLineBreakAtCurrentWidth(context.currentObject());
121        else
122            ASSERT_NOT_REACHED();
123
124        if (context.atEnd())
125            return context.handleEndOfLine();
126
127        context.commitAndUpdateLineBreakIfNeeded();
128
129        if (context.atEnd())
130            return context.handleEndOfLine();
131
132        context.increment();
133    }
134
135    context.clearLineBreakIfFitsOnLine(true);
136
137    return context.handleEndOfLine();
138}
139
140}
141