1 | /**
|
---|
2 | * Copyright (C) 2000 Lars Knoll ([email protected])
|
---|
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 "InlineIteratorBox.h"
|
---|
30 | #include "InlineIteratorLineBox.h"
|
---|
31 | #include "InlineRunAndOffset.h"
|
---|
32 | #include "LegacyInlineElementBox.h"
|
---|
33 | #include "LegacyRootInlineBox.h"
|
---|
34 | #include "LineSelection.h"
|
---|
35 | #include "LogicalSelectionOffsetCaches.h"
|
---|
36 | #include "RenderBlock.h"
|
---|
37 | #include "RenderView.h"
|
---|
38 | #include "SVGElementTypeHelpers.h"
|
---|
39 | #include "SVGInlineTextBox.h"
|
---|
40 | #include "VisiblePosition.h"
|
---|
41 | #include <wtf/IsoMallocInlines.h>
|
---|
42 |
|
---|
43 | #if PLATFORM(IOS_FAMILY)
|
---|
44 | #include "SelectionGeometry.h"
|
---|
45 | #endif
|
---|
46 |
|
---|
47 | namespace WebCore {
|
---|
48 |
|
---|
49 | WTF_MAKE_ISO_ALLOCATED_IMPL(RenderLineBreak);
|
---|
50 |
|
---|
51 | static const int invalidLineHeight = -1;
|
---|
52 |
|
---|
53 | RenderLineBreak::RenderLineBreak(HTMLElement& element, RenderStyle&& style)
|
---|
54 | : RenderBoxModelObject(element, WTFMove(style), 0)
|
---|
55 | , m_inlineBoxWrapper(nullptr)
|
---|
56 | , m_cachedLineHeight(invalidLineHeight)
|
---|
57 | , m_isWBR(is<HTMLWBRElement>(element))
|
---|
58 | {
|
---|
59 | setIsLineBreak();
|
---|
60 | }
|
---|
61 |
|
---|
62 | RenderLineBreak::~RenderLineBreak()
|
---|
63 | {
|
---|
64 | delete m_inlineBoxWrapper;
|
---|
65 | }
|
---|
66 |
|
---|
67 | LayoutUnit RenderLineBreak::lineHeight(bool firstLine, LineDirectionMode /*direction*/, LinePositionMode /*linePositionMode*/) const
|
---|
68 | {
|
---|
69 | if (firstLine) {
|
---|
70 | const RenderStyle& firstLineStyle = this->firstLineStyle();
|
---|
71 | if (&firstLineStyle != &style())
|
---|
72 | return firstLineStyle.computedLineHeight();
|
---|
73 | }
|
---|
74 |
|
---|
75 | if (m_cachedLineHeight == invalidLineHeight)
|
---|
76 | m_cachedLineHeight = style().computedLineHeight();
|
---|
77 |
|
---|
78 | return m_cachedLineHeight;
|
---|
79 | }
|
---|
80 |
|
---|
81 | LayoutUnit RenderLineBreak::baselinePosition(FontBaseline baselineType, bool firstLine, LineDirectionMode direction, LinePositionMode linePositionMode) const
|
---|
82 | {
|
---|
83 | const RenderStyle& style = firstLine ? firstLineStyle() : this->style();
|
---|
84 | const FontMetrics& fontMetrics = style.metricsOfPrimaryFont();
|
---|
85 | return LayoutUnit { (fontMetrics.ascent(baselineType) + (lineHeight(firstLine, direction, linePositionMode) - fontMetrics.height()) / 2).toInt() };
|
---|
86 | }
|
---|
87 |
|
---|
88 | std::unique_ptr<LegacyInlineElementBox> RenderLineBreak::createInlineBox()
|
---|
89 | {
|
---|
90 | return makeUnique<LegacyInlineElementBox>(*this);
|
---|
91 | }
|
---|
92 |
|
---|
93 | void RenderLineBreak::setInlineBoxWrapper(LegacyInlineElementBox* inlineBox)
|
---|
94 | {
|
---|
95 | ASSERT(!inlineBox || !m_inlineBoxWrapper);
|
---|
96 | m_inlineBoxWrapper = inlineBox;
|
---|
97 | }
|
---|
98 |
|
---|
99 | void RenderLineBreak::replaceInlineBoxWrapper(LegacyInlineElementBox& inlineBox)
|
---|
100 | {
|
---|
101 | deleteInlineBoxWrapper();
|
---|
102 | setInlineBoxWrapper(&inlineBox);
|
---|
103 | }
|
---|
104 |
|
---|
105 | void RenderLineBreak::deleteInlineBoxWrapper()
|
---|
106 | {
|
---|
107 | if (!m_inlineBoxWrapper)
|
---|
108 | return;
|
---|
109 | if (!renderTreeBeingDestroyed())
|
---|
110 | m_inlineBoxWrapper->removeFromParent();
|
---|
111 | delete m_inlineBoxWrapper;
|
---|
112 | m_inlineBoxWrapper = nullptr;
|
---|
113 | }
|
---|
114 |
|
---|
115 | void RenderLineBreak::dirtyLineBoxes(bool fullLayout)
|
---|
116 | {
|
---|
117 | if (!m_inlineBoxWrapper)
|
---|
118 | return;
|
---|
119 | if (fullLayout) {
|
---|
120 | delete m_inlineBoxWrapper;
|
---|
121 | m_inlineBoxWrapper = nullptr;
|
---|
122 | return;
|
---|
123 | }
|
---|
124 | m_inlineBoxWrapper->dirtyLineBoxes();
|
---|
125 | }
|
---|
126 |
|
---|
127 | int RenderLineBreak::caretMinOffset() const
|
---|
128 | {
|
---|
129 | return 0;
|
---|
130 | }
|
---|
131 |
|
---|
132 | int RenderLineBreak::caretMaxOffset() const
|
---|
133 | {
|
---|
134 | return 1;
|
---|
135 | }
|
---|
136 |
|
---|
137 | bool RenderLineBreak::canBeSelectionLeaf() const
|
---|
138 | {
|
---|
139 | return true;
|
---|
140 | }
|
---|
141 |
|
---|
142 | VisiblePosition RenderLineBreak::positionForPoint(const LayoutPoint&, const RenderFragmentContainer*)
|
---|
143 | {
|
---|
144 | return createVisiblePosition(0, Affinity::Downstream);
|
---|
145 | }
|
---|
146 |
|
---|
147 | IntRect RenderLineBreak::linesBoundingBox() const
|
---|
148 | {
|
---|
149 | auto run = InlineIterator::boxFor(*this);
|
---|
150 | if (!run)
|
---|
151 | return { };
|
---|
152 |
|
---|
153 | return enclosingIntRect(run->visualRectIgnoringBlockDirection());
|
---|
154 | }
|
---|
155 |
|
---|
156 | void RenderLineBreak::absoluteRects(Vector<IntRect>& rects, const LayoutPoint& accumulatedOffset) const
|
---|
157 | {
|
---|
158 | auto box = InlineIterator::boxFor(*this);
|
---|
159 | if (!box)
|
---|
160 | return;
|
---|
161 |
|
---|
162 | auto rect = box->visualRectIgnoringBlockDirection();
|
---|
163 | rects.append(enclosingIntRect(FloatRect(accumulatedOffset + rect.location(), rect.size())));
|
---|
164 | }
|
---|
165 |
|
---|
166 | void RenderLineBreak::absoluteQuads(Vector<FloatQuad>& quads, bool* wasFixed) const
|
---|
167 | {
|
---|
168 | auto box = InlineIterator::boxFor(*this);
|
---|
169 | if (!box)
|
---|
170 | return;
|
---|
171 |
|
---|
172 | auto rect = box->visualRectIgnoringBlockDirection();
|
---|
173 | quads.append(localToAbsoluteQuad(FloatRect(rect.location(), rect.size()), UseTransforms, wasFixed));
|
---|
174 | }
|
---|
175 |
|
---|
176 | void RenderLineBreak::updateFromStyle()
|
---|
177 | {
|
---|
178 | m_cachedLineHeight = invalidLineHeight;
|
---|
179 | RELEASE_ASSERT_WITH_SECURITY_IMPLICATION(isInline());
|
---|
180 | }
|
---|
181 |
|
---|
182 | #if PLATFORM(IOS_FAMILY)
|
---|
183 | void RenderLineBreak::collectSelectionGeometries(Vector<SelectionGeometry>& rects, unsigned, unsigned)
|
---|
184 | {
|
---|
185 | auto run = InlineIterator::boxFor(*this);
|
---|
186 |
|
---|
187 | if (!run)
|
---|
188 | return;
|
---|
189 | auto lineBox = run->lineBox();
|
---|
190 |
|
---|
191 | auto lineSelectionRect = LineSelection::logicalRect(*lineBox);
|
---|
192 | LayoutRect rect = IntRect(run->logicalLeft(), lineSelectionRect.y(), 0, lineSelectionRect.height());
|
---|
193 | if (!lineBox->isHorizontal())
|
---|
194 | rect = rect.transposedRect();
|
---|
195 |
|
---|
196 | if (lineBox->isFirstAfterPageBreak()) {
|
---|
197 | if (run->isHorizontal())
|
---|
198 | rect.shiftYEdgeTo(lineBox->top());
|
---|
199 | else
|
---|
200 | rect.shiftXEdgeTo(lineBox->top());
|
---|
201 | }
|
---|
202 |
|
---|
203 | // FIXME: Out-of-flow positioned line breaks do not follow normal containing block chain.
|
---|
204 | auto* containingBlock = RenderObject::containingBlockForPositionType(PositionType::Static, *this);
|
---|
205 | // Map rect, extended left to leftOffset, and right to rightOffset, through transforms to get minX and maxX.
|
---|
206 | LogicalSelectionOffsetCaches cache(*containingBlock);
|
---|
207 | LayoutUnit leftOffset = containingBlock->logicalLeftSelectionOffset(*containingBlock, LayoutUnit(run->logicalTop()), cache);
|
---|
208 | LayoutUnit rightOffset = containingBlock->logicalRightSelectionOffset(*containingBlock, LayoutUnit(run->logicalTop()), cache);
|
---|
209 | LayoutRect extentsRect = rect;
|
---|
210 | if (run->isHorizontal()) {
|
---|
211 | extentsRect.setX(leftOffset);
|
---|
212 | extentsRect.setWidth(rightOffset - leftOffset);
|
---|
213 | } else {
|
---|
214 | extentsRect.setY(leftOffset);
|
---|
215 | extentsRect.setHeight(rightOffset - leftOffset);
|
---|
216 | }
|
---|
217 | extentsRect = localToAbsoluteQuad(FloatRect(extentsRect)).enclosingBoundingBox();
|
---|
218 | if (!run->isHorizontal())
|
---|
219 | extentsRect = extentsRect.transposedRect();
|
---|
220 | bool isFirstOnLine = !run->previousOnLine();
|
---|
221 | bool isLastOnLine = !run->nextOnLine();
|
---|
222 | if (containingBlock->isRubyBase() || containingBlock->isRubyText())
|
---|
223 | isLastOnLine = !containingBlock->containingBlock()->inlineBoxWrapper()->nextOnLineExists();
|
---|
224 |
|
---|
225 | bool isFixed = false;
|
---|
226 | auto absoluteQuad = localToAbsoluteQuad(FloatRect(rect), UseTransforms, &isFixed);
|
---|
227 | bool boxIsHorizontal = !is<SVGInlineTextBox>(run->legacyInlineBox()) ? run->isHorizontal() : !style().isVerticalWritingMode();
|
---|
228 | // If the containing block is an inline element, we want to check the inlineBoxWrapper orientation
|
---|
229 | // to determine the orientation of the block. In this case we also use the inlineBoxWrapper to
|
---|
230 | // determine if the element is the last on the line.
|
---|
231 | if (containingBlock->inlineBoxWrapper()) {
|
---|
232 | if (containingBlock->inlineBoxWrapper()->isHorizontal() != boxIsHorizontal) {
|
---|
233 | boxIsHorizontal = containingBlock->inlineBoxWrapper()->isHorizontal();
|
---|
234 | isLastOnLine = !containingBlock->inlineBoxWrapper()->nextOnLineExists();
|
---|
235 | }
|
---|
236 | }
|
---|
237 |
|
---|
238 | rects.append(SelectionGeometry(absoluteQuad, HTMLElement::selectionRenderingBehavior(element()), run->direction(), extentsRect.x(), extentsRect.maxX(), extentsRect.maxY(), 0, run->isLineBreak(), isFirstOnLine, isLastOnLine, false, false, boxIsHorizontal, isFixed, containingBlock->isRubyText(), view().pageNumberForBlockProgressionOffset(absoluteQuad.enclosingBoundingBox().x())));
|
---|
239 | }
|
---|
240 | #endif
|
---|
241 |
|
---|
242 | } // namespace WebCore
|
---|