Add diagnostic logging for ResourceResponse's source
[WebKit-https.git] / Source / WebCore / rendering / SimpleLineLayoutTextFragmentIterator.cpp
1 /*
2  * Copyright (C) 2015 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 "SimpleLineLayoutTextFragmentIterator.h"
28
29 #include "RenderBlockFlow.h"
30 #include "RenderChildIterator.h"
31 #include "RenderText.h"
32 #include "SimpleLineLayoutFlowContents.h"
33
34 namespace WebCore {
35 namespace SimpleLineLayout {
36
37 TextFragmentIterator::Style::Style(const RenderStyle& style)
38     : font(style.fontCascade())
39     , textAlign(style.textAlign())
40     , collapseWhitespace(style.collapseWhiteSpace())
41     , preserveNewline(style.preserveNewline())
42     , wrapLines(style.autoWrap())
43     , breakWordOnOverflow(style.overflowWrap() == BreakOverflowWrap && (wrapLines || preserveNewline))
44     , spaceWidth(font.width(TextRun(&space, 1)))
45     , tabWidth(collapseWhitespace ? 0 : style.tabSize())
46     , locale(style.locale())
47 {
48 }
49
50 TextFragmentIterator::TextFragmentIterator(const RenderBlockFlow& flow)
51     : m_flowContents(flow)
52     , m_lineBreakIterator((*m_flowContents.begin()).text, flow.style().locale())
53     , m_style(flow.style())
54 {
55 }
56
57 TextFragmentIterator::TextFragment TextFragmentIterator::nextTextFragment(float xPosition)
58 {
59     // A fragment can either be
60     // 1. new line character when preserveNewline is on (not considered as whitespace) or
61     // 2. whitespace (collasped, non-collapsed multi or single) or
62     // 3. non-whitespace characters.
63     // 4. empty, indicating content end.
64     if (isEnd(m_position))
65         return TextFragment(m_position, m_position, 0, TextFragment::ContentEnd);
66     if (isLineBreak(m_position)) {
67         TextFragment fragment(m_position, m_position + 1, 0, TextFragment::LineBreak);
68         ++m_position;
69         return fragment;
70     }
71     unsigned spaceCount = 0;
72     unsigned startPosition = m_position;
73     unsigned endPosition = findNextNonWhitespacePosition(startPosition, spaceCount);
74     ASSERT(startPosition <= endPosition);
75     if (endPosition > startPosition) {
76         bool multipleWhitespace = startPosition + 1 < endPosition;
77         bool isCollapsed = multipleWhitespace && m_style.collapseWhitespace;
78         bool isBreakable = !isCollapsed && multipleWhitespace;
79         float width = 0;
80         if (isCollapsed)
81             width = m_style.spaceWidth;
82         else {
83             unsigned length = endPosition - startPosition;
84             width = length == spaceCount ? length * m_style.spaceWidth : textWidth(startPosition, endPosition, xPosition);
85         }
86         m_position = endPosition;
87         return TextFragment(startPosition, endPosition, width, TextFragment::Whitespace, isCollapsed, isBreakable);
88     }
89     endPosition = findNextBreakablePosition(startPosition + 1);
90     m_position = endPosition;
91     return TextFragment(startPosition, endPosition, textWidth(startPosition, endPosition, xPosition), TextFragment::NonWhitespace, false, m_style.breakWordOnOverflow);
92 }
93
94 float TextFragmentIterator::textWidth(unsigned from, unsigned to, float xPosition) const
95 {
96     const auto& fromSegment = m_flowContents.segmentForPosition(from);
97     if ((m_style.font.isFixedPitch() && fromSegment.end >= to) || (from == fromSegment.start && to == fromSegment.end))
98         return fromSegment.renderer.width(from - fromSegment.start, to - from, m_style.font, xPosition, nullptr, nullptr);
99
100     const auto* segment = &fromSegment;
101     float textWidth = 0;
102     unsigned fragmentEnd = 0;
103     while (true) {
104         fragmentEnd = std::min(to, segment->end);
105         textWidth += segment->text.is8Bit() ? runWidth<LChar>(segment->text, from - segment->start, fragmentEnd - segment->start, xPosition + textWidth) :
106             runWidth<UChar>(segment->text, from - segment->start, fragmentEnd - segment->start, xPosition + textWidth);
107         if (fragmentEnd == to)
108             break;
109         from = fragmentEnd;
110         segment = &m_flowContents.segmentForPosition(fragmentEnd);
111     };
112
113     return textWidth;
114 }
115
116 template <typename CharacterType>
117 static unsigned nextBreakablePosition(LazyLineBreakIterator& lineBreakIterator, const FlowContents::Segment& segment, unsigned position)
118 {
119     const auto* characters = segment.text.characters<CharacterType>();
120     unsigned segmentLength = segment.end - segment.start;
121     unsigned segmentPosition = position - segment.start;
122     return nextBreakablePositionNonLoosely<CharacterType, NBSPBehavior::IgnoreNBSP>(lineBreakIterator, characters, segmentLength, segmentPosition);
123 }
124
125 unsigned TextFragmentIterator::findNextBreakablePosition(unsigned position) const
126 {
127     while (!isEnd(position)) {
128         auto& segment = m_flowContents.segmentForPosition(position);
129         if (segment.text.impl() != m_lineBreakIterator.string().impl()) {
130             UChar lastCharacter = segment.start > 0 ? characterAt(segment.start - 1) : 0;
131             UChar secondToLastCharacter = segment.start > 1 ? characterAt(segment.start - 2) : 0;
132             m_lineBreakIterator.setPriorContext(lastCharacter, secondToLastCharacter);
133             m_lineBreakIterator.resetStringAndReleaseIterator(segment.text, m_style.locale, LineBreakIteratorModeUAX14);
134         }
135
136         unsigned breakable = segment.text.is8Bit() ? nextBreakablePosition<LChar>(m_lineBreakIterator, segment, position) : nextBreakablePosition<UChar>(m_lineBreakIterator, segment, position);
137         position = segment.start + breakable;
138         if (position < segment.end)
139             break;
140     }
141     return position;
142 }
143
144 template <typename CharacterType>
145 static bool findNextNonWhitespace(const FlowContents::Segment& segment, const TextFragmentIterator::Style& style, unsigned& position, unsigned& spaceCount)
146 {
147     const auto* text = segment.text.characters<CharacterType>();
148     for (; position < segment.end; ++position) {
149         auto character = text[position - segment.start];
150         bool isSpace = character == ' ';
151         bool isWhitespace = isSpace || character == '\t' || (!style.preserveNewline && character == '\n');
152         if (!isWhitespace)
153             return true;
154         if (isSpace)
155             ++spaceCount;
156     }
157     return false;
158 }
159
160 unsigned TextFragmentIterator::findNextNonWhitespacePosition(unsigned position, unsigned& spaceCount) const
161 {
162     FlowContents::Iterator it(m_flowContents, m_flowContents.segmentIndexForPosition(position));
163     for (auto end = m_flowContents.end(); it != end; ++it) {
164         bool foundNonWhitespace = (*it).text.is8Bit() ? findNextNonWhitespace<LChar>(*it, m_style, position, spaceCount) : findNextNonWhitespace<UChar>(*it, m_style, position, spaceCount);
165         if (foundNonWhitespace)
166             break;
167     }
168     return position;
169 }
170
171 template <typename CharacterType>
172 float TextFragmentIterator::runWidth(const String& text, unsigned from, unsigned to, float xPosition) const
173 {
174     ASSERT(from <= to);
175     if (from == to)
176         return 0;
177     bool measureWithEndSpace = m_style.collapseWhitespace && to < text.length() && text[to] == ' ';
178     if (measureWithEndSpace)
179         ++to;
180     TextRun run(text.characters<CharacterType>() + from, to - from);
181     run.setXPos(xPosition);
182     run.setTabSize(!!m_style.tabWidth, m_style.tabWidth);
183     float width = m_style.font.width(run);
184     if (measureWithEndSpace)
185         width -= m_style.spaceWidth;
186     return width;
187 }
188
189 }
190 }