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