Flaky Test: fast/events/resize-subframe-in-rendering-update.html
[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 "FontMetrics.h"
27 #include "HTMLElement.h"
28 #include "HTMLWBRElement.h"
29 #include "InlineElementBox.h"
30 #include "LineLayoutTraversal.h"
31 #include "LogicalSelectionOffsetCaches.h"
32 #include "RenderBlock.h"
33 #include "RenderView.h"
34 #include "RootInlineBox.h"
35 #include "VisiblePosition.h"
36 #include <wtf/IsoMallocInlines.h>
37
38 #if PLATFORM(IOS_FAMILY)
39 #include "SelectionRect.h"
40 #endif
41
42 namespace WebCore {
43
44 WTF_MAKE_ISO_ALLOCATED_IMPL(RenderLineBreak);
45
46 static const int invalidLineHeight = -1;
47
48 RenderLineBreak::RenderLineBreak(HTMLElement& element, RenderStyle&& style)
49     : RenderBoxModelObject(element, WTFMove(style), 0)
50     , m_inlineBoxWrapper(nullptr)
51     , m_cachedLineHeight(invalidLineHeight)
52     , m_isWBR(is<HTMLWBRElement>(element))
53 {
54     setIsLineBreak();
55 }
56
57 RenderLineBreak::~RenderLineBreak()
58 {
59     delete m_inlineBoxWrapper;
60 }
61
62 LayoutUnit RenderLineBreak::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const
63 {
64     if (firstLine && view().usesFirstLineRules()) {
65         const RenderStyle& firstLineStyle = this->firstLineStyle();
66         if (&firstLineStyle != &style())
67             return firstLineStyle.computedLineHeight();
68     }
69
70     if (m_cachedLineHeight == invalidLineHeight)
71         m_cachedLineHeight = style().computedLineHeight();
72     
73     return m_cachedLineHeight;
74 }
75
76 int RenderLineBreak::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
77 {
78     const RenderStyle& style = firstLine ? firstLineStyle() : this->style();
79     const FontMetrics& fontMetrics = style.fontMetrics();
80     return fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2;
81 }
82
83 std::unique_ptr<InlineElementBox> RenderLineBreak::createInlineBox()
84 {
85     return makeUnique<InlineElementBox>(*this);
86 }
87
88 void RenderLineBreak::setInlineBoxWrapper(InlineElementBox* inlineBox)
89 {
90     ASSERT(!inlineBox || !m_inlineBoxWrapper);
91     m_inlineBoxWrapper = inlineBox;
92 }
93
94 void RenderLineBreak::replaceInlineBoxWrapper(InlineElementBox& inlineBox)
95 {
96     deleteInlineBoxWrapper();
97     setInlineBoxWrapper(&inlineBox);
98 }
99
100 void RenderLineBreak::deleteInlineBoxWrapper()
101 {
102     if (!m_inlineBoxWrapper)
103         return;
104     if (!renderTreeBeingDestroyed())
105         m_inlineBoxWrapper->removeFromParent();
106     delete m_inlineBoxWrapper;
107     m_inlineBoxWrapper = nullptr;
108 }
109
110 void RenderLineBreak::dirtyLineBoxes(bool fullLayout)
111 {
112     if (!m_inlineBoxWrapper)
113         return;
114     if (fullLayout) {
115         delete m_inlineBoxWrapper;
116         m_inlineBoxWrapper = nullptr;
117         return;
118     }
119     m_inlineBoxWrapper->dirtyLineBoxes();
120 }
121
122 void RenderLineBreak::ensureLineBoxes()
123 {
124     if (!is<RenderBlockFlow>(*parent()))
125         return;
126     downcast<RenderBlockFlow>(*parent()).ensureLineBoxes();
127 }
128
129 void RenderLineBreak::deleteLineBoxesBeforeSimpleLineLayout()
130 {
131     delete m_inlineBoxWrapper;
132     m_inlineBoxWrapper = nullptr;
133 }
134
135 int RenderLineBreak::caretMinOffset() const
136 {
137     return 0;
138 }
139
140 int RenderLineBreak::caretMaxOffset() const
141
142     return 1;
143 }
144
145 bool RenderLineBreak::canBeSelectionLeaf() const
146 {
147     return true;
148 }
149
150 VisiblePosition RenderLineBreak::positionForPoint(const LayoutPoint&, const RenderFragmentContainer*)
151 {
152     ensureLineBoxes();
153     return createVisiblePosition(0, DOWNSTREAM);
154 }
155
156 void RenderLineBreak::setSelectionState(SelectionState state)
157 {
158     if (state != SelectionNone)
159         ensureLineBoxes();
160     RenderBoxModelObject::setSelectionState(state);
161     if (!m_inlineBoxWrapper)
162         return;
163     m_inlineBoxWrapper->root().setHasSelectedChildren(state != SelectionNone);
164 }
165
166 LayoutRect RenderLineBreak::localCaretRect(InlineBox* inlineBox, unsigned caretOffset, LayoutUnit* extraWidthToEndOfLine)
167 {
168     ASSERT_UNUSED(caretOffset, !caretOffset);
169     ASSERT_UNUSED(inlineBox, inlineBox == m_inlineBoxWrapper);
170     if (!inlineBox)
171         return LayoutRect();
172
173     const RootInlineBox& rootBox = inlineBox->root();
174     return rootBox.computeCaretRect(inlineBox->logicalLeft(), caretWidth, extraWidthToEndOfLine);
175 }
176
177 IntRect RenderLineBreak::linesBoundingBox() const
178 {
179     auto box = LineLayoutTraversal::elementBoxFor(*this);
180     if (!box)
181         return { };
182
183     return enclosingIntRect(box->rect());
184 }
185
186 void RenderLineBreak::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
187 {
188     auto box = LineLayoutTraversal::elementBoxFor(*this);
189     if (!box)
190         return;
191
192     auto rect = box->rect();
193     rects.append(enclosingIntRect(FloatRect(accumulatedOffset + rect.location(), rect.size())));
194 }
195
196 void RenderLineBreak::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
197 {
198     auto box = LineLayoutTraversal::elementBoxFor(*this);
199     if (!box)
200         return;
201
202     auto rect = box->rect();
203     quads.append(localToAbsoluteQuad(FloatRect(rect.location(), rect.size()), UseTransforms, wasFixed));
204 }
205
206 void RenderLineBreak::updateFromStyle()
207 {
208     m_cachedLineHeight = invalidLineHeight;
209 }
210
211 #if PLATFORM(IOS_FAMILY)
212 void RenderLineBreak::collectSelectionRects(Vector<SelectionRect>& rects, unsigned, unsigned)
213 {
214     ensureLineBoxes();
215     InlineElementBox* box = m_inlineBoxWrapper;
216     if (!box)
217         return;
218     const RootInlineBox& rootBox = box->root();
219     LayoutRect rect = rootBox.computeCaretRect(box->logicalLeft(), 0, nullptr);
220     if (rootBox.isFirstAfterPageBreak()) {
221         if (box->isHorizontal())
222             rect.shiftYEdgeTo(rootBox.lineTopWithLeading());
223         else
224             rect.shiftXEdgeTo(rootBox.lineTopWithLeading());
225     }
226
227     auto* containingBlock = containingBlockForObjectInFlow();
228     // Map rect, extended left to leftOffset, and right to rightOffset, through transforms to get minX and maxX.
229     LogicalSelectionOffsetCaches cache(*containingBlock);
230     LayoutUnit leftOffset = containingBlock->logicalLeftSelectionOffset(*containingBlock, LayoutUnit(box->logicalTop()), cache);
231     LayoutUnit rightOffset = containingBlock->logicalRightSelectionOffset(*containingBlock, LayoutUnit(box->logicalTop()), cache);
232     LayoutRect extentsRect = rect;
233     if (box->isHorizontal()) {
234         extentsRect.setX(leftOffset);
235         extentsRect.setWidth(rightOffset - leftOffset);
236     } else {
237         extentsRect.setY(leftOffset);
238         extentsRect.setHeight(rightOffset - leftOffset);
239     }
240     extentsRect = localToAbsoluteQuad(FloatRect(extentsRect)).enclosingBoundingBox();
241     if (!box->isHorizontal())
242         extentsRect = extentsRect.transposedRect();
243     bool isFirstOnLine = !box->previousOnLineExists();
244     bool isLastOnLine = !box->nextOnLineExists();
245     if (containingBlock->isRubyBase() || containingBlock->isRubyText())
246         isLastOnLine = !containingBlock->containingBlock()->inlineBoxWrapper()->nextOnLineExists();
247
248     bool isFixed = false;
249     IntRect absRect = localToAbsoluteQuad(FloatRect(rect), UseTransforms, &isFixed).enclosingBoundingBox();
250     bool boxIsHorizontal = !box->isSVGInlineTextBox() ? box->isHorizontal() : !style().isVerticalWritingMode();
251     // If the containing block is an inline element, we want to check the inlineBoxWrapper orientation
252     // to determine the orientation of the block. In this case we also use the inlineBoxWrapper to
253     // determine if the element is the last on the line.
254     if (containingBlock->inlineBoxWrapper()) {
255         if (containingBlock->inlineBoxWrapper()->isHorizontal() != boxIsHorizontal) {
256             boxIsHorizontal = containingBlock->inlineBoxWrapper()->isHorizontal();
257             isLastOnLine = !containingBlock->inlineBoxWrapper()->nextOnLineExists();
258         }
259     }
260
261     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())));
262 }
263 #endif
264
265 } // namespace WebCore