Simple line layout: Small tweaks to improve performance.
[WebKit-https.git] / Source / WebCore / rendering / SimpleLineLayoutTextFragmentIterator.h
1 /*
2  * Copyright (C) 2015-2016 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 #pragma once
27
28 #include "BreakLines.h"
29 #include "RenderLineBreak.h"
30 #include "SimpleLineLayoutFlowContents.h"
31
32 namespace WebCore {
33
34 class RenderBlockFlow;
35 class RenderStyle;
36
37 namespace SimpleLineLayout {
38
39 class TextFragmentIterator  {
40 public:
41     TextFragmentIterator(const RenderBlockFlow&);
42     class TextFragment {
43     public:
44         enum Type { ContentEnd, SoftLineBreak, HardLineBreak, Whitespace, NonWhitespace };
45         TextFragment() = default;
46         TextFragment(unsigned start, unsigned end, float width, Type type, bool isLastInRenderer = false, bool overlapsToNextRenderer = false, bool isCollapsed = false, bool isCollapsible = false)
47             : m_start(start)
48             , m_end(end)
49             , m_width(width)
50             , m_type(type)
51             , m_isLastInRenderer(isLastInRenderer)
52             , m_overlapsToNextRenderer(overlapsToNextRenderer)
53             , m_isCollapsed(isCollapsed)
54             , m_isCollapsible(isCollapsible)
55         {
56         }
57
58         unsigned start() const { return m_start; }
59         unsigned end() const { return m_end; }
60         float width() const { return m_width; }
61         Type type() const { return m_type; }
62         bool overlapsToNextRenderer() const { return m_overlapsToNextRenderer; }
63         bool isLastInRenderer() const { return m_isLastInRenderer; }
64         bool isLineBreak() const { return m_type == SoftLineBreak || m_type == HardLineBreak; }
65         bool isCollapsed() const { return m_isCollapsed; }
66         bool isCollapsible() const { return m_isCollapsible; }
67         bool hasHyphen() const { return m_hasHyphen; }
68         unsigned wrappingWithHyphenCounter() const { return m_hyphenationCounter; }
69
70         bool isEmpty() const { return start() == end() && !isLineBreak(); }
71         TextFragment split(unsigned splitPosition, const TextFragmentIterator&);
72         TextFragment splitWithHyphen(unsigned hyphenPosition, const TextFragmentIterator&);
73         bool operator==(const TextFragment& other) const
74         {
75             return m_start == other.m_start
76                 && m_end == other.m_end
77                 && m_width == other.m_width
78                 && m_type == other.m_type
79                 && m_isLastInRenderer == other.m_isLastInRenderer
80                 && m_overlapsToNextRenderer == other.m_overlapsToNextRenderer
81                 && m_isCollapsed == other.m_isCollapsed
82                 && m_isCollapsible == other.m_isCollapsible
83                 && m_hasHyphen == other.m_hasHyphen;
84         }
85
86     private:
87         unsigned m_start { 0 };
88         unsigned m_end { 0 };
89         float m_width { 0 };
90         Type m_type { NonWhitespace };
91         bool m_isLastInRenderer { false };
92         bool m_overlapsToNextRenderer { false };
93         bool m_isCollapsed { false };
94         bool m_isCollapsible { false };
95         bool m_hasHyphen { false };
96         unsigned m_hyphenationCounter { 0 };
97     };
98     TextFragment nextTextFragment(float xPosition = 0);
99     void revertToEndOfFragment(const TextFragment&);
100
101     // FIXME: These functions below should be decoupled from the text iterator.
102     float textWidth(unsigned startPosition, unsigned endPosition, float xPosition) const;
103     std::optional<unsigned> lastHyphenPosition(const TextFragmentIterator::TextFragment& run, unsigned beforeIndex) const;
104
105     struct Style {
106         explicit Style(const RenderStyle&);
107
108         const FontCascade& font;
109         ETextAlign textAlign;
110         bool collapseWhitespace;
111         bool preserveNewline;
112         bool wrapLines;
113         bool breakAnyWordOnOverflow;
114         bool breakFirstWordOnOverflow;
115         bool breakNBSP;
116         bool keepAllWordsForCJK;
117         float spaceWidth;
118         float wordSpacing;
119         unsigned tabWidth;
120         bool shouldHyphenate;
121         float hyphenStringWidth;
122         unsigned hyphenLimitBefore;
123         unsigned hyphenLimitAfter;
124         AtomicString locale;
125         std::optional<unsigned> hyphenLimitLines;
126     };
127     const Style& style() const { return m_style; }
128
129 private:
130     TextFragment findNextTextFragment(float xPosition);
131     enum PositionType { Breakable, NonWhitespace };
132     unsigned skipToNextPosition(PositionType, unsigned startPosition, float& width, float xPosition, bool& overlappingFragment);
133     bool isSoftLineBreak(unsigned position) const;
134     bool isHardLineBreak(const FlowContents::Iterator& segment) const;
135     unsigned nextBreakablePosition(const FlowContents::Segment&, unsigned startPosition);
136     unsigned nextNonWhitespacePosition(const FlowContents::Segment&, unsigned startPosition);
137     float runWidth(const FlowContents::Segment&, unsigned startPosition, unsigned endPosition, float xPosition) const;
138
139     FlowContents m_flowContents;
140     FlowContents::Iterator m_currentSegment;
141     LazyLineBreakIterator m_lineBreakIterator;
142     const Style m_style;
143     unsigned m_position { 0 };
144     bool m_atEndOfSegment { false };
145 };
146
147 inline TextFragmentIterator::TextFragment TextFragmentIterator::TextFragment::split(unsigned splitPosition, const TextFragmentIterator& textFragmentIterator)
148 {
149     auto updateFragmentProperties = [&textFragmentIterator] (TextFragment& fragment)
150     {
151         fragment.m_width = 0;
152         if (fragment.start() != fragment.end())
153             fragment.m_width = textFragmentIterator.textWidth(fragment.start(), fragment.end(), 0);
154         if (fragment.start() + 1 > fragment.end())
155             return;
156         fragment.m_isCollapsed = false;
157     };
158
159     TextFragment rightSide(*this);
160     m_end = splitPosition;
161     updateFragmentProperties(*this);
162
163     rightSide.m_start = splitPosition;
164     updateFragmentProperties(rightSide);
165     return rightSide;
166 }
167
168 inline TextFragmentIterator::TextFragment TextFragmentIterator::TextFragment::splitWithHyphen(unsigned hyphenPosition,
169     const TextFragmentIterator& textFragmentIterator)
170 {
171     ASSERT(textFragmentIterator.style().shouldHyphenate);
172     auto rightSide = split(hyphenPosition, textFragmentIterator);
173     rightSide.m_hyphenationCounter = m_hyphenationCounter + 1;
174     m_hasHyphen = true;
175     m_width += textFragmentIterator.style().hyphenStringWidth;
176     return rightSide;
177 }
178
179 inline bool TextFragmentIterator::isSoftLineBreak(unsigned position) const
180 {
181     const auto& segment = *m_currentSegment;
182     ASSERT(segment.start <= position && position < segment.end);
183     return m_style.preserveNewline && segment.text[position - segment.start] == '\n';
184 }
185
186 inline bool TextFragmentIterator::isHardLineBreak(const FlowContents::Iterator& segment) const
187 {
188     ASSERT(segment->start != segment->end || (segment->start == segment->end && is<RenderLineBreak>(segment->renderer)));
189     return segment->start == segment->end;
190 }
191
192 }
193 }