[LFC][IFC] Add generic inline line breaker
[WebKit-https.git] / Source / WebCore / layout / inlineformatting / InlineLineBreaker.cpp
1 /*
2  * Copyright (C) 2018 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23  * THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "InlineLineBreaker.h"
28
29 #if ENABLE(LAYOUT_FORMATTING_CONTEXT)
30
31 #include "FontCascade.h"
32 #include "Hyphenation.h"
33 #include "InlineRunProvider.h"
34 #include <wtf/IsoMallocInlines.h>
35
36 namespace WebCore {
37 namespace Layout {
38
39 WTF_MAKE_ISO_ALLOCATED_IMPL(InlineLineBreaker);
40
41 InlineLineBreaker::InlineLineBreaker(const LayoutContext& layoutContext, const InlineContent& inlineContent, const Vector<InlineRunProvider::Run>& inlineRuns)
42     : m_layoutContext(layoutContext)
43     , m_textUtil(inlineContent)
44     , m_inlineRuns(inlineRuns)
45 {
46 }
47
48 std::optional<InlineLineBreaker::Run> InlineLineBreaker::nextLayoutRun(LayoutUnit contentLogicalLeft, LayoutUnit availableWidth, bool lineIsEmpty)
49 {
50     if (isAtContentEnd())
51         return std::nullopt;
52
53     InlineRunProvider::Run currentInlineRun = m_inlineRuns[m_currentRunIndex];
54     // Adjust the current run if it is split midword.
55     if (m_splitPosition) {
56         ASSERT(currentInlineRun.isText());
57         currentInlineRun.textContext()->setStart(*m_splitPosition);
58         m_splitPosition = std::nullopt;
59     }
60
61     if (currentInlineRun.isLineBreak()) {
62         ++m_currentRunIndex;
63         return Run { Run::Position::LineEnd, currentInlineRun, 0 };
64     }
65
66     auto contentWidth = runWidth(currentInlineRun, contentLogicalLeft);
67     // 1. Plenty of space left.
68     if (contentWidth <= availableWidth) {
69         ++m_currentRunIndex;
70         return Run { lineIsEmpty ? Run::Position::LineBegin : Run::Position::Undetermined, currentInlineRun, contentWidth };
71     }
72
73     // 2. No space left whatsoever.
74     if (availableWidth <= 0) {
75         ++m_currentRunIndex;
76         return Run { Run::Position::LineBegin, currentInlineRun, contentWidth };
77     }
78
79     // 3. Some space left. Let's find out what we need to do with this run.
80     auto breakingBehavior = lineBreakingBehavior(currentInlineRun, lineIsEmpty);
81     if (breakingBehavior == LineBreakingBehavior::Keep) {
82         ++m_currentRunIndex;
83         return Run { lineIsEmpty ? Run::Position::LineBegin : Run::Position::Undetermined, currentInlineRun, contentWidth };
84     }
85
86     if (breakingBehavior == LineBreakingBehavior::WrapToNextLine) {
87         ++m_currentRunIndex;
88         return Run { Run::Position::LineBegin, currentInlineRun, contentWidth };
89     }
90
91     ASSERT(breakingBehavior == LineBreakingBehavior::Break);
92     // Split content.
93     return splitRun(currentInlineRun, contentLogicalLeft, availableWidth, lineIsEmpty);
94 }
95
96 bool InlineLineBreaker::isAtContentEnd() const
97 {
98     return m_currentRunIndex == m_inlineRuns.size();
99 }
100
101 InlineLineBreaker::LineBreakingBehavior InlineLineBreaker::lineBreakingBehavior(const InlineRunProvider::Run& inlineRun, bool lineIsEmpty)
102 {
103     // Line breaking behaviour:
104     // 1. Whitesapce collapse on -> push whitespace to next line.
105     // 2. Whitespace collapse off -> whitespace is split where possible.
106     // 3. Non-whitespace -> first run on the line -> either split or kept on the line. (depends on overflow-wrap)
107     // 4. Non-whitespace -> already content on the line -> either gets split (word-break: break-all) or gets pushed to the next line.
108     // (Hyphenate when possible)
109     // 5. Non-text type -> next line
110     auto& style = inlineRun.style();
111
112     if (inlineRun.isWhitespace())
113         return style.collapseWhiteSpace() ? LineBreakingBehavior::WrapToNextLine : LineBreakingBehavior::Break;
114
115     if (inlineRun.isNonWhitespace()) {
116         auto shouldHypenate = !m_hyphenationIsDisabled && style.hyphens() == Hyphens::Auto && canHyphenate(style.locale());
117         if (shouldHypenate)
118             return LineBreakingBehavior::Break;
119
120         if (style.autoWrap()) {
121             // Break any word
122             if (style.wordBreak() == WordBreak::BreakAll)
123                 return LineBreakingBehavior::Break;
124
125             // Break first run on line.
126             if (lineIsEmpty && style.breakWords() && style.preserveNewline())
127                 return LineBreakingBehavior::Break;
128         }
129
130         // Non-breakable non-whitespace run.
131         return lineIsEmpty ? LineBreakingBehavior::Keep : LineBreakingBehavior::WrapToNextLine;
132     }
133
134     ASSERT(inlineRun.isBox() || inlineRun.isFloat());
135     // Non-text inline runs.
136     return LineBreakingBehavior::WrapToNextLine;
137 }
138
139 LayoutUnit InlineLineBreaker::runWidth(const InlineRunProvider::Run& inlineRun, LayoutUnit contentLogicalLeft) const
140 {
141     ASSERT(!inlineRun.isLineBreak());
142
143     if (inlineRun.isText()) {
144         auto textContext = inlineRun.textContext();
145         return m_textUtil.width(inlineRun.inlineItem(), textContext->start(), textContext->isCollapsed() ? 1 : textContext->length(), contentLogicalLeft);
146     }
147
148     ASSERT(inlineRun.isBox() || inlineRun.isFloat());
149     auto& layoutBox = inlineRun.inlineItem().layoutBox();
150     ASSERT(m_layoutContext.hasDisplayBox(layoutBox));
151     auto& displayBox = m_layoutContext.displayBoxForLayoutBox(layoutBox);
152     return displayBox.width();
153 }
154
155 InlineLineBreaker::Run InlineLineBreaker::splitRun(const InlineRunProvider::Run& inlineRun, LayoutUnit, LayoutUnit, bool)
156 {
157     return { Run::Position::Undetermined, inlineRun, { } };
158 }
159
160 std::optional<ItemPosition> InlineLineBreaker::adjustSplitPositionWithHyphenation(const InlineRunProvider::Run&, ItemPosition, LayoutUnit, LayoutUnit, bool) const
161 {
162     return { };
163 }
164
165 }
166 }
167 #endif