1 | /* |
2 | * Copyright (C) 2003, 2004, 2005, 2006, 2007 Apple Inc. All rights reserved. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Library General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Library General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Library General Public License |
15 | * along with this library; see the file COPYING.LIB. If not, write to |
16 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
17 | * Boston, MA 02110-1301, USA. |
18 | * |
19 | */ |
20 | |
21 | #pragma once |
22 | |
23 | #include "InlineBox.h" |
24 | #include "RenderOverflow.h" |
25 | #include "ShadowData.h" |
26 | |
27 | namespace WebCore { |
28 | |
29 | class HitTestRequest; |
30 | class HitTestResult; |
31 | class InlineTextBox; |
32 | class RenderLineBoxList; |
33 | class Font; |
34 | class VerticalPositionCache; |
35 | |
36 | struct GlyphOverflow; |
37 | |
38 | typedef HashMap<const InlineTextBox*, std::pair<Vector<const Font*>, GlyphOverflow>> GlyphOverflowAndFallbackFontsMap; |
39 | |
40 | class InlineFlowBox : public InlineBox { |
41 | WTF_MAKE_ISO_ALLOCATED(InlineFlowBox); |
42 | public: |
43 | explicit InlineFlowBox(RenderBoxModelObject& renderer) |
44 | : InlineBox(renderer) |
45 | , m_includeLogicalLeftEdge(false) |
46 | , m_includeLogicalRightEdge(false) |
47 | , m_descendantsHaveSameLineHeightAndBaseline(true) |
48 | , m_baselineType(AlphabeticBaseline) |
49 | , m_hasAnnotationsBefore(false) |
50 | , m_hasAnnotationsAfter(false) |
51 | , m_isFirstAfterPageBreak(false) |
52 | #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED |
53 | , m_hasBadChildList(false) |
54 | #endif |
55 | , m_firstChild(nullptr) |
56 | , m_lastChild(nullptr) |
57 | , m_prevLineBox(nullptr) |
58 | , m_nextLineBox(nullptr) |
59 | { |
60 | // Internet Explorer and Firefox always create a marker for list items, even when the list-style-type is none. We do not make a marker |
61 | // in the list-style-type: none case, since it is wasteful to do so. However, in order to match other browsers we have to pretend like |
62 | // an invisible marker exists. The side effect of having an invisible marker is that the quirks mode behavior of shrinking lines with no |
63 | // text children must not apply. This change also means that gaps will exist between image bullet list items. Even when the list bullet |
64 | // is an image, the line is still considered to be immune from the quirk. |
65 | m_hasTextChildren = renderer.style().display() == DisplayType::ListItem; |
66 | m_hasTextDescendants = m_hasTextChildren; |
67 | } |
68 | |
69 | #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED |
70 | virtual ~InlineFlowBox(); |
71 | #endif |
72 | |
73 | #if ENABLE(TREE_DEBUGGING) |
74 | void outputLineTreeAndMark(WTF::TextStream&, const InlineBox* markedBox, int depth) const override; |
75 | const char* boxName() const override; |
76 | #endif |
77 | |
78 | RenderBoxModelObject& renderer() const { return downcast<RenderBoxModelObject>(InlineBox::renderer()); } |
79 | const RenderStyle& lineStyle() const { return isFirstLine() ? renderer().firstLineStyle() : renderer().style(); } |
80 | |
81 | InlineFlowBox* prevLineBox() const { return m_prevLineBox; } |
82 | InlineFlowBox* nextLineBox() const { return m_nextLineBox; } |
83 | void setNextLineBox(InlineFlowBox* n) { m_nextLineBox = n; } |
84 | void setPreviousLineBox(InlineFlowBox* p) { m_prevLineBox = p; } |
85 | |
86 | InlineBox* firstChild() const { checkConsistency(); return m_firstChild; } |
87 | InlineBox* lastChild() const { checkConsistency(); return m_lastChild; } |
88 | |
89 | bool isLeaf() const final { return false; } |
90 | |
91 | InlineBox* firstLeafChild() const; |
92 | InlineBox* lastLeafChild() const; |
93 | |
94 | typedef void (*CustomInlineBoxRangeReverse)(void* userData, Vector<InlineBox*>::iterator first, Vector<InlineBox*>::iterator last); |
95 | void collectLeafBoxesInLogicalOrder(Vector<InlineBox*>&, CustomInlineBoxRangeReverse customReverseImplementation = nullptr, void* userData = nullptr) const; |
96 | |
97 | void setConstructed() final |
98 | { |
99 | InlineBox::setConstructed(); |
100 | for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) |
101 | child->setConstructed(); |
102 | } |
103 | |
104 | void addToLine(InlineBox* child); |
105 | void deleteLine() final; |
106 | void () final; |
107 | void attachLine() final; |
108 | void adjustPosition(float dx, float dy) override; |
109 | |
110 | virtual void (); |
111 | virtual void attachLineBoxToRenderObject(); |
112 | virtual void removeLineBoxFromRenderObject(); |
113 | |
114 | void clearTruncation() override; |
115 | |
116 | void paintBoxDecorations(PaintInfo&, const LayoutPoint&); |
117 | void paintMask(PaintInfo&, const LayoutPoint&); |
118 | void paintFillLayers(const PaintInfo&, const Color&, const FillLayer&, const LayoutRect&, CompositeOperator = CompositeSourceOver); |
119 | void paintFillLayer(const PaintInfo&, const Color&, const FillLayer&, const LayoutRect&, CompositeOperator = CompositeSourceOver); |
120 | void paintBoxShadow(const PaintInfo&, const RenderStyle&, ShadowStyle, const LayoutRect&); |
121 | void paint(PaintInfo&, const LayoutPoint&, LayoutUnit lineTop, LayoutUnit lineBottom) override; |
122 | bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom, HitTestAction) override; |
123 | |
124 | bool boxShadowCanBeAppliedToBackground(const FillLayer&) const; |
125 | |
126 | // logicalLeft = left in a horizontal line and top in a vertical line. |
127 | LayoutUnit marginBorderPaddingLogicalLeft() const { return marginLogicalLeft() + borderLogicalLeft() + paddingLogicalLeft(); } |
128 | LayoutUnit marginBorderPaddingLogicalRight() const { return marginLogicalRight() + borderLogicalRight() + paddingLogicalRight(); } |
129 | LayoutUnit marginLogicalLeft() const |
130 | { |
131 | if (!includeLogicalLeftEdge()) |
132 | return 0; |
133 | return isHorizontal() ? renderer().marginLeft() : renderer().marginTop(); |
134 | } |
135 | LayoutUnit marginLogicalRight() const |
136 | { |
137 | if (!includeLogicalRightEdge()) |
138 | return 0; |
139 | return isHorizontal() ? renderer().marginRight() : renderer().marginBottom(); |
140 | } |
141 | float borderLogicalLeft() const |
142 | { |
143 | if (!includeLogicalLeftEdge()) |
144 | return 0; |
145 | return isHorizontal() ? lineStyle().borderLeftWidth() : lineStyle().borderTopWidth(); |
146 | } |
147 | float borderLogicalRight() const |
148 | { |
149 | if (!includeLogicalRightEdge()) |
150 | return 0; |
151 | return isHorizontal() ? lineStyle().borderRightWidth() : lineStyle().borderBottomWidth(); |
152 | } |
153 | float paddingLogicalLeft() const |
154 | { |
155 | if (!includeLogicalLeftEdge()) |
156 | return 0; |
157 | return isHorizontal() ? renderer().paddingLeft() : renderer().paddingTop(); |
158 | } |
159 | float paddingLogicalRight() const |
160 | { |
161 | if (!includeLogicalRightEdge()) |
162 | return 0; |
163 | return isHorizontal() ? renderer().paddingRight() : renderer().paddingBottom(); |
164 | } |
165 | |
166 | bool includeLogicalLeftEdge() const { return m_includeLogicalLeftEdge; } |
167 | bool includeLogicalRightEdge() const { return m_includeLogicalRightEdge; } |
168 | void setEdges(bool includeLeft, bool includeRight) |
169 | { |
170 | m_includeLogicalLeftEdge = includeLeft; |
171 | m_includeLogicalRightEdge = includeRight; |
172 | } |
173 | |
174 | // Helper functions used during line construction and placement. |
175 | void determineSpacingForFlowBoxes(bool lastLine, bool isLogicallyLastRunWrapped, RenderObject* logicallyLastRunRenderer); |
176 | LayoutUnit getFlowSpacingLogicalWidth(); |
177 | float placeBoxesInInlineDirection(float logicalLeft, bool& needsWordSpacing); |
178 | float placeBoxRangeInInlineDirection(InlineBox* firstChild, InlineBox* lastChild, float& logicalLeft, float& minLogicalLeft, float& maxLogicalRight, bool& needsWordSpacing); |
179 | void beginPlacingBoxRangesInInlineDirection(float logicalLeft) { setLogicalLeft(logicalLeft); } |
180 | void endPlacingBoxRangesInInlineDirection(float logicalLeft, float logicalRight, float minLogicalLeft, float maxLogicalRight) |
181 | { |
182 | setLogicalWidth(logicalRight - logicalLeft); |
183 | if (knownToHaveNoOverflow() && (minLogicalLeft < logicalLeft || maxLogicalRight > logicalRight)) |
184 | clearKnownToHaveNoOverflow(); |
185 | } |
186 | |
187 | void computeLogicalBoxHeights(RootInlineBox&, LayoutUnit& maxPositionTop, LayoutUnit& maxPositionBottom, |
188 | int& maxAscent, int& maxDescent, bool& setMaxAscent, bool& setMaxDescent, |
189 | bool strictMode, GlyphOverflowAndFallbackFontsMap&, FontBaseline, VerticalPositionCache&); |
190 | void adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, |
191 | int maxPositionTop, int maxPositionBottom); |
192 | void placeBoxesInBlockDirection(LayoutUnit logicalTop, LayoutUnit maxHeight, int maxAscent, bool strictMode, LayoutUnit& lineTop, LayoutUnit& lineBottom, bool& setLineTop, |
193 | LayoutUnit& lineTopIncludingMargins, LayoutUnit& lineBottomIncludingMargins, bool& hasAnnotationsBefore, bool& hasAnnotationsAfter, FontBaseline); |
194 | void flipLinesInBlockDirection(LayoutUnit lineTop, LayoutUnit lineBottom); |
195 | bool requiresIdeographicBaseline(const GlyphOverflowAndFallbackFontsMap&) const; |
196 | |
197 | LayoutUnit computeOverAnnotationAdjustment(LayoutUnit allowedPosition) const; |
198 | LayoutUnit computeUnderAnnotationAdjustment(LayoutUnit allowedPosition) const; |
199 | |
200 | void computeOverflow(LayoutUnit lineTop, LayoutUnit lineBottom, GlyphOverflowAndFallbackFontsMap&); |
201 | |
202 | void removeChild(InlineBox* child); |
203 | |
204 | RenderObject::SelectionState selectionState() override; |
205 | |
206 | bool canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) const final; |
207 | float placeEllipsisBox(bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, float &truncatedWidth, bool&) override; |
208 | |
209 | bool hasTextChildren() const { return m_hasTextChildren; } |
210 | bool hasTextDescendants() const { return m_hasTextDescendants; } |
211 | void setHasTextChildren() { m_hasTextChildren = true; setHasTextDescendants(); } |
212 | void setHasTextDescendants() { m_hasTextDescendants = true; } |
213 | |
214 | void checkConsistency() const; |
215 | void setHasBadChildList(); |
216 | |
217 | // Line visual and layout overflow are in the coordinate space of the block. This means that they aren't purely physical directions. |
218 | // For horizontal-tb and vertical-lr they will match physical directions, but for horizontal-bt and vertical-rl, the top/bottom and left/right |
219 | // respectively are flipped when compared to their physical counterparts. For example minX is on the left in vertical-lr, but it is on the right in vertical-rl. |
220 | LayoutRect layoutOverflowRect(LayoutUnit lineTop, LayoutUnit lineBottom) const |
221 | { |
222 | return m_overflow ? m_overflow->layoutOverflowRect() : enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom)); |
223 | } |
224 | LayoutUnit logicalTopLayoutOverflow(LayoutUnit lineTop) const |
225 | { |
226 | if (m_overflow) |
227 | return isHorizontal() ? m_overflow->layoutOverflowRect().y() : m_overflow->layoutOverflowRect().x(); |
228 | return lineTop; |
229 | } |
230 | LayoutUnit logicalBottomLayoutOverflow(LayoutUnit lineBottom) const |
231 | { |
232 | if (m_overflow) |
233 | return isHorizontal() ? m_overflow->layoutOverflowRect().maxY() : m_overflow->layoutOverflowRect().maxX(); |
234 | return lineBottom; |
235 | } |
236 | LayoutRect logicalLayoutOverflowRect(LayoutUnit lineTop, LayoutUnit lineBottom) const |
237 | { |
238 | LayoutRect result = layoutOverflowRect(lineTop, lineBottom); |
239 | if (!renderer().isHorizontalWritingMode()) |
240 | result = result.transposedRect(); |
241 | return result; |
242 | } |
243 | |
244 | LayoutRect visualOverflowRect(LayoutUnit lineTop, LayoutUnit lineBottom) const |
245 | { |
246 | return m_overflow ? m_overflow->visualOverflowRect() : enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom)); |
247 | } |
248 | LayoutUnit logicalLeftVisualOverflow() const { return m_overflow ? (isHorizontal() ? m_overflow->visualOverflowRect().x() : m_overflow->visualOverflowRect().y()) : LayoutUnit(logicalLeft()); } |
249 | LayoutUnit logicalRightVisualOverflow() const { return m_overflow ? (isHorizontal() ? m_overflow->visualOverflowRect().maxX() : m_overflow->visualOverflowRect().maxY()) : LayoutUnit(ceilf(logicalRight())); } |
250 | LayoutUnit logicalTopVisualOverflow(LayoutUnit lineTop) const |
251 | { |
252 | if (m_overflow) |
253 | return isHorizontal() ? m_overflow->visualOverflowRect().y() : m_overflow->visualOverflowRect().x(); |
254 | return lineTop; |
255 | } |
256 | LayoutUnit logicalBottomVisualOverflow(LayoutUnit lineBottom) const |
257 | { |
258 | if (m_overflow) |
259 | return isHorizontal() ? m_overflow->visualOverflowRect().maxY() : m_overflow->visualOverflowRect().maxX(); |
260 | return lineBottom; |
261 | } |
262 | LayoutRect logicalVisualOverflowRect(LayoutUnit lineTop, LayoutUnit lineBottom) const |
263 | { |
264 | LayoutRect result = visualOverflowRect(lineTop, lineBottom); |
265 | if (!renderer().isHorizontalWritingMode()) |
266 | result = result.transposedRect(); |
267 | return result; |
268 | } |
269 | |
270 | void setOverflowFromLogicalRects(const LayoutRect& logicalLayoutOverflow, const LayoutRect& logicalVisualOverflow, LayoutUnit lineTop, LayoutUnit lineBottom); |
271 | void setLayoutOverflow(const LayoutRect&, LayoutUnit lineTop, LayoutUnit lineBottom); |
272 | void setVisualOverflow(const LayoutRect&, LayoutUnit lineTop, LayoutUnit lineBottom); |
273 | |
274 | FloatRect frameRectIncludingLineHeight(LayoutUnit lineTop, LayoutUnit lineBottom) const |
275 | { |
276 | if (isHorizontal()) |
277 | return FloatRect(x(), lineTop, width(), lineBottom - lineTop); |
278 | return FloatRect(lineTop, y(), lineBottom - lineTop, height()); |
279 | } |
280 | |
281 | FloatRect logicalFrameRectIncludingLineHeight(LayoutUnit lineTop, LayoutUnit lineBottom) const |
282 | { |
283 | return FloatRect(logicalLeft(), lineTop, logicalWidth(), lineBottom - lineTop); |
284 | } |
285 | |
286 | bool descendantsHaveSameLineHeightAndBaseline() const { return m_descendantsHaveSameLineHeightAndBaseline; } |
287 | void clearDescendantsHaveSameLineHeightAndBaseline() |
288 | { |
289 | m_descendantsHaveSameLineHeightAndBaseline = false; |
290 | if (parent() && parent()->descendantsHaveSameLineHeightAndBaseline()) |
291 | parent()->clearDescendantsHaveSameLineHeightAndBaseline(); |
292 | } |
293 | |
294 | void computeReplacedAndTextLineTopAndBottom(LayoutUnit& lineTop, LayoutUnit& lineBottom) const; |
295 | |
296 | void maxLogicalBottomForTextDecorationLine(float& maxLogicalBottom, const RenderElement* decorationRenderer, OptionSet<TextDecoration>) const; |
297 | void minLogicalTopForTextDecorationLine(float& minLogicalTop, const RenderElement* decorationRenderer, OptionSet<TextDecoration>) const; |
298 | |
299 | private: |
300 | bool isInlineFlowBox() const final { return true; } |
301 | void boxModelObject() const = delete; |
302 | |
303 | void addBoxShadowVisualOverflow(LayoutRect& logicalVisualOverflow); |
304 | void addBorderOutsetVisualOverflow(LayoutRect& logicalVisualOverflow); |
305 | void addTextBoxVisualOverflow(InlineTextBox&, GlyphOverflowAndFallbackFontsMap&, LayoutRect& logicalVisualOverflow); |
306 | void addOutlineVisualOverflow(LayoutRect& logicalVisualOverflow); |
307 | void addReplacedChildOverflow(const InlineBox*, LayoutRect& logicalLayoutOverflow, LayoutRect& logicalVisualOverflow); |
308 | void constrainToLineTopAndBottomIfNeeded(LayoutRect&) const; |
309 | |
310 | private: |
311 | unsigned m_includeLogicalLeftEdge : 1; |
312 | unsigned m_includeLogicalRightEdge : 1; |
313 | unsigned m_hasTextChildren : 1; |
314 | unsigned m_hasTextDescendants : 1; |
315 | unsigned m_descendantsHaveSameLineHeightAndBaseline : 1; |
316 | |
317 | protected: |
318 | // The following members are only used by RootInlineBox but moved here to keep the bits packed. |
319 | |
320 | // Whether or not this line uses alphabetic or ideographic baselines by default. |
321 | unsigned m_baselineType : 1; // FontBaseline |
322 | |
323 | // If the line contains any ruby runs, then this will be true. |
324 | unsigned m_hasAnnotationsBefore : 1; |
325 | unsigned m_hasAnnotationsAfter : 1; |
326 | |
327 | unsigned m_lineBreakBidiStatusEor : 5; // UCharDirection |
328 | unsigned m_lineBreakBidiStatusLastStrong : 5; // UCharDirection |
329 | unsigned m_lineBreakBidiStatusLast : 5; // UCharDirection |
330 | |
331 | unsigned m_isFirstAfterPageBreak : 1; |
332 | |
333 | // End of RootInlineBox-specific members. |
334 | |
335 | #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED |
336 | private: |
337 | unsigned m_hasBadChildList : 1; |
338 | #endif |
339 | |
340 | protected: |
341 | RefPtr<RenderOverflow> m_overflow; |
342 | |
343 | InlineBox* m_firstChild; |
344 | InlineBox* m_lastChild; |
345 | |
346 | InlineFlowBox* m_prevLineBox; // The previous box that also uses our RenderObject |
347 | InlineFlowBox* m_nextLineBox; // The next box that also uses our RenderObject |
348 | }; |
349 | |
350 | #ifdef NDEBUG |
351 | |
352 | inline void InlineFlowBox::checkConsistency() const |
353 | { |
354 | } |
355 | |
356 | #endif |
357 | |
358 | #if ASSERT_WITH_SECURITY_IMPLICATION_DISABLED |
359 | |
360 | inline void InlineFlowBox::setHasBadChildList() |
361 | { |
362 | } |
363 | |
364 | #endif |
365 | |
366 | } // namespace WebCore |
367 | |
368 | SPECIALIZE_TYPE_TRAITS_INLINE_BOX(InlineFlowBox, isInlineFlowBox()) |
369 | |
370 | #if ENABLE(TREE_DEBUGGING) |
371 | // Outside the WebCore namespace for ease of invocation from the debugger. |
372 | void showTree(const WebCore::InlineFlowBox*); |
373 | #endif |
374 | |