Simple line layout: Add <br> support.
[WebKit-https.git] / Source / WebCore / rendering / RenderLineBreak.cpp
1 /**
2  * Copyright (C) 2000 Lars Knoll (knoll@kde.org)
3  * Copyright (C) 2006, 2013 Apple Inc. All rights reserved.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Library General Public
7  * License as published by the Free Software Foundation; either
8  * version 2 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB.  If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  *
20  */
21
22 #include "config.h"
23 #include "RenderLineBreak.h"
24
25 #include "Document.h"
26 #include "HTMLElement.h"
27 #include "HTMLWBRElement.h"
28 #include "InlineElementBox.h"
29 #include "LogicalSelectionOffsetCaches.h"
30 #include "RenderBlock.h"
31 #include "RenderView.h"
32 #include "RootInlineBox.h"
33 #include "SimpleLineLayoutFunctions.h"
34 #include "VisiblePosition.h"
35
36 #if PLATFORM(IOS)
37 #include "SelectionRect.h"
38 #endif
39
40 namespace WebCore {
41
42 static const int invalidLineHeight = -1;
43
44 static const SimpleLineLayout::Layout* simpleLineLayout(const RenderLineBreak& renderer)
45 {
46     if (!is<RenderBlockFlow>(*renderer.parent()))
47         return nullptr;
48     return downcast<RenderBlockFlow>(*renderer.parent()).simpleLineLayout();
49 }
50
51 static void ensureLineBoxes(const RenderLineBreak& renderer)
52 {
53     if (!is<RenderBlockFlow>(*renderer.parent()))
54         return;
55     downcast<RenderBlockFlow>(*renderer.parent()).ensureLineBoxes();
56 }
57
58 RenderLineBreak::RenderLineBreak(HTMLElement& element, Ref<RenderStyle>&& style)
59     : RenderBoxModelObject(element, WTF::move(style), 0)
60     , m_inlineBoxWrapper(nullptr)
61     , m_cachedLineHeight(invalidLineHeight)
62     , m_isWBR(is<HTMLWBRElement>(element))
63 {
64     setIsLineBreak();
65 }
66
67 RenderLineBreak::~RenderLineBreak()
68 {
69     delete m_inlineBoxWrapper;
70 }
71
72 LayoutUnit RenderLineBreak::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const
73 {
74     if (firstLine && document().styleSheetCollection().usesFirstLineRules()) {
75         const RenderStyle& firstLineStyle = this->firstLineStyle();
76         if (&firstLineStyle != &style())
77             return firstLineStyle.computedLineHeight();
78     }
79
80     if (m_cachedLineHeight == invalidLineHeight)
81         m_cachedLineHeight = style().computedLineHeight();
82     
83     return m_cachedLineHeight;
84 }
85
86 int RenderLineBreak::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
87 {
88     const RenderStyle& style = firstLine ? firstLineStyle() : this->style();
89     const FontMetrics& fontMetrics = style.fontMetrics();
90     return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
91 }
92
93 std::unique_ptr<InlineElementBox> RenderLineBreak::createInlineBox()
94 {
95     return std::make_unique<InlineElementBox>(*this);
96 }
97
98 void RenderLineBreak::setInlineBoxWrapper(InlineElementBox* inlineBox)
99 {
100     ASSERT(!inlineBox || !m_inlineBoxWrapper);
101     m_inlineBoxWrapper = inlineBox;
102 }
103
104 void RenderLineBreak::replaceInlineBoxWrapper(InlineElementBox& inlineBox)
105 {
106     deleteInlineBoxWrapper();
107     setInlineBoxWrapper(&inlineBox);
108 }
109
110 void RenderLineBreak::deleteInlineBoxWrapper()
111 {
112     if (!m_inlineBoxWrapper)
113         return;
114     if (!documentBeingDestroyed())
115         m_inlineBoxWrapper->removeFromParent();
116     delete m_inlineBoxWrapper;
117     m_inlineBoxWrapper = nullptr;
118 }
119
120 void RenderLineBreak::dirtyLineBoxes(bool fullLayout)
121 {
122     if (!m_inlineBoxWrapper)
123         return;
124     if (fullLayout) {
125         delete m_inlineBoxWrapper;
126         m_inlineBoxWrapper = nullptr;
127         return;
128     }
129     m_inlineBoxWrapper->dirtyLineBoxes();
130 }
131
132 void RenderLineBreak::deleteLineBoxesBeforeSimpleLineLayout()
133 {
134     delete m_inlineBoxWrapper;
135     m_inlineBoxWrapper = nullptr;
136 }
137
138 int RenderLineBreak::caretMinOffset() const
139 {
140     return 0;
141 }
142
143 int RenderLineBreak::caretMaxOffset() const
144
145     return 1;
146 }
147
148 bool RenderLineBreak::canBeSelectionLeaf() const
149 {
150     return true;
151 }
152
153 VisiblePosition RenderLineBreak::positionForPoint(const LayoutPoint&, const RenderRegion*)
154 {
155     ensureLineBoxes(*this);
156     return createVisiblePosition(0, DOWNSTREAM);
157 }
158
159 void RenderLineBreak::setSelectionState(SelectionState state)
160 {
161     if (state != SelectionNone)
162         ensureLineBoxes(*this);
163     RenderBoxModelObject::setSelectionState(state);
164     if (!m_inlineBoxWrapper)
165         return;
166     m_inlineBoxWrapper->root().setHasSelectedChildren(state != SelectionNone);
167 }
168
169 LayoutRect RenderLineBreak::localCaretRect(InlineBox* inlineBox, int caretOffset, LayoutUnit* extraWidthToEndOfLine)
170 {
171     ASSERT_UNUSED(caretOffset, !caretOffset);
172     ASSERT_UNUSED(inlineBox, inlineBox == m_inlineBoxWrapper);
173     if (!inlineBox)
174         return LayoutRect();
175
176     const RootInlineBox& rootBox = inlineBox->root();
177     return rootBox.computeCaretRect(inlineBox->logicalLeft(), caretWidth, extraWidthToEndOfLine);
178 }
179
180 IntRect RenderLineBreak::linesBoundingBox() const
181 {
182     if (auto* layout = simpleLineLayout(*this))
183         return SimpleLineLayout::computeBoundingBox(*this, *layout);
184
185     if (!m_inlineBoxWrapper)
186         return IntRect();
187
188     float logicalLeftSide = m_inlineBoxWrapper->logicalLeft();
189     float logicalRightSide = m_inlineBoxWrapper->logicalRight();
190
191     bool isHorizontal = style().isHorizontalWritingMode();
192
193     float x = isHorizontal ? logicalLeftSide : m_inlineBoxWrapper->x();
194     float y = isHorizontal ? m_inlineBoxWrapper->y() : logicalLeftSide;
195     float width = isHorizontal ? logicalRightSide - logicalLeftSide : m_inlineBoxWrapper->logicalBottom() - x;
196     float height = isHorizontal ? m_inlineBoxWrapper->logicalBottom() - y : logicalRightSide - logicalLeftSide;
197     return enclosingIntRect(FloatRect(x, y, width, height));
198 }
199
200 void RenderLineBreak::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
201 {
202     if (auto* layout = simpleLineLayout(*this)) {
203         rects.appendVector(SimpleLineLayout::collectAbsoluteRects(*this, *layout, accumulatedOffset));
204         return;
205     }
206
207     if (!m_inlineBoxWrapper)
208         return;
209     rects.append(enclosingIntRect(FloatRect(accumulatedOffset + m_inlineBoxWrapper->topLeft(), m_inlineBoxWrapper->size())));
210 }
211
212 void RenderLineBreak::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
213 {
214     if (auto* layout = simpleLineLayout(*this)) {
215         quads.appendVector(SimpleLineLayout::collectAbsoluteQuads(*this, *layout, wasFixed));
216         return;
217     }
218     if (!m_inlineBoxWrapper)
219         return;
220     quads.append(localToAbsoluteQuad(FloatRect(m_inlineBoxWrapper->topLeft(), m_inlineBoxWrapper->size()), UseTransforms, wasFixed));
221 }
222
223 void RenderLineBreak::updateFromStyle()
224 {
225     m_cachedLineHeight = invalidLineHeight;
226 }
227
228 IntRect RenderLineBreak::borderBoundingBox() const
229 {
230     return IntRect(IntPoint(), linesBoundingBox().size());
231 }
232
233 #if PLATFORM(IOS)
234 void RenderLineBreak::collectSelectionRects(Vector<SelectionRect>& rects, unsigned, unsigned)
235 {
236     ensureLineBoxes(*this);
237     InlineElementBox* box = m_inlineBoxWrapper;
238     if (!box)
239         return;
240     const RootInlineBox& rootBox = box->root();
241     LayoutRect rect = rootBox.computeCaretRect(box->logicalLeft(), 0, nullptr);
242     if (rootBox.isFirstAfterPageBreak()) {
243         if (box->isHorizontal())
244             rect.shiftYEdgeTo(rootBox.lineTopWithLeading());
245         else
246             rect.shiftXEdgeTo(rootBox.lineTopWithLeading());
247     }
248
249     RenderBlock* containingBlock = this->containingBlock();
250     // Map rect, extended left to leftOffset, and right to rightOffset, through transforms to get minX and maxX.
251     LogicalSelectionOffsetCaches cache(*containingBlock);
252     LayoutUnit leftOffset = containingBlock->logicalLeftSelectionOffset(*containingBlock, box->logicalTop(), cache);
253     LayoutUnit rightOffset = containingBlock->logicalRightSelectionOffset(*containingBlock, box->logicalTop(), cache);
254     LayoutRect extentsRect = rect;
255     if (box->isHorizontal()) {
256         extentsRect.setX(leftOffset);
257         extentsRect.setWidth(rightOffset - leftOffset);
258     } else {
259         extentsRect.setY(leftOffset);
260         extentsRect.setHeight(rightOffset - leftOffset);
261     }
262     extentsRect = localToAbsoluteQuad(FloatRect(extentsRect)).enclosingBoundingBox();
263     if (!box->isHorizontal())
264         extentsRect = extentsRect.transposedRect();
265     bool isFirstOnLine = !box->previousOnLineExists();
266     bool isLastOnLine = !box->nextOnLineExists();
267     if (containingBlock->isRubyBase() || containingBlock->isRubyText())
268         isLastOnLine = !containingBlock->containingBlock()->inlineBoxWrapper()->nextOnLineExists();
269
270     bool isFixed = false;
271     IntRect absRect = localToAbsoluteQuad(FloatRect(rect), UseTransforms, &isFixed).enclosingBoundingBox();
272     bool boxIsHorizontal = !box->isSVGInlineTextBox() ? box->isHorizontal() : !style().svgStyle().isVerticalWritingMode();
273     // If the containing block is an inline element, we want to check the inlineBoxWrapper orientation
274     // to determine the orientation of the block. In this case we also use the inlineBoxWrapper to
275     // determine if the element is the last on the line.
276     if (containingBlock->inlineBoxWrapper()) {
277         if (containingBlock->inlineBoxWrapper()->isHorizontal() != boxIsHorizontal) {
278             boxIsHorizontal = containingBlock->inlineBoxWrapper()->isHorizontal();
279             isLastOnLine = !containingBlock->inlineBoxWrapper()->nextOnLineExists();
280         }
281     }
282
283     rects.append(SelectionRect(absRect, box->direction(), extentsRect.x(), extentsRect.maxX(), extentsRect.maxY(), 0, box->isLineBreak(), isFirstOnLine, isLastOnLine, false, false, boxIsHorizontal, isFixed, containingBlock->isRubyText(), view().pageNumberForBlockProgressionOffset(absRect.x())));
284 }
285 #endif
286
287 } // namespace WebCore