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 | |
42 | namespace WebCore { |
43 | |
44 | WTF_MAKE_ISO_ALLOCATED_IMPL(RenderLineBreak); |
45 | |
46 | static const int invalidLineHeight = -1; |
47 | |
48 | static 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 | |
55 | RenderLineBreak::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 | |
64 | RenderLineBreak::~RenderLineBreak() |
65 | { |
66 | delete m_inlineBoxWrapper; |
67 | } |
68 | |
69 | LayoutUnit 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 | |
83 | int 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 | |
90 | std::unique_ptr<InlineElementBox> RenderLineBreak::createInlineBox() |
91 | { |
92 | return std::make_unique<InlineElementBox>(*this); |
93 | } |
94 | |
95 | void RenderLineBreak::setInlineBoxWrapper(InlineElementBox* inlineBox) |
96 | { |
97 | ASSERT(!inlineBox || !m_inlineBoxWrapper); |
98 | m_inlineBoxWrapper = inlineBox; |
99 | } |
100 | |
101 | void RenderLineBreak::replaceInlineBoxWrapper(InlineElementBox& inlineBox) |
102 | { |
103 | deleteInlineBoxWrapper(); |
104 | setInlineBoxWrapper(&inlineBox); |
105 | } |
106 | |
107 | void 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 | |
117 | void 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 | |
129 | void RenderLineBreak::ensureLineBoxes() |
130 | { |
131 | if (!is<RenderBlockFlow>(*parent())) |
132 | return; |
133 | downcast<RenderBlockFlow>(*parent()).ensureLineBoxes(); |
134 | } |
135 | |
136 | void RenderLineBreak::deleteLineBoxesBeforeSimpleLineLayout() |
137 | { |
138 | delete m_inlineBoxWrapper; |
139 | m_inlineBoxWrapper = nullptr; |
140 | } |
141 | |
142 | int RenderLineBreak::caretMinOffset() const |
143 | { |
144 | return 0; |
145 | } |
146 | |
147 | int RenderLineBreak::caretMaxOffset() const |
148 | { |
149 | return 1; |
150 | } |
151 | |
152 | bool RenderLineBreak::canBeSelectionLeaf() const |
153 | { |
154 | return true; |
155 | } |
156 | |
157 | VisiblePosition RenderLineBreak::positionForPoint(const LayoutPoint&, const RenderFragmentContainer*) |
158 | { |
159 | ensureLineBoxes(); |
160 | return createVisiblePosition(0, DOWNSTREAM); |
161 | } |
162 | |
163 | void 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 | |
173 | LayoutRect RenderLineBreak::localCaretRect(InlineBox* inlineBox, unsigned caretOffset, LayoutUnit* ) |
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 | |
184 | IntRect 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 | |
204 | void 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 | |
216 | void 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 | |
227 | void RenderLineBreak::updateFromStyle() |
228 | { |
229 | m_cachedLineHeight = invalidLineHeight; |
230 | } |
231 | |
232 | #if PLATFORM(IOS_FAMILY) |
233 | void 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 | |