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
27namespace WebCore {
28
29class HitTestRequest;
30class HitTestResult;
31class InlineTextBox;
32class RenderLineBoxList;
33class Font;
34class VerticalPositionCache;
35
36struct GlyphOverflow;
37
38typedef HashMap<const InlineTextBox*, std::pair<Vector<const Font*>, GlyphOverflow>> GlyphOverflowAndFallbackFontsMap;
39
40class InlineFlowBox : public InlineBox {
41 WTF_MAKE_ISO_ALLOCATED(InlineFlowBox);
42public:
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 extractLine() final;
107 void attachLine() final;
108 void adjustPosition(float dx, float dy) override;
109
110 virtual void extractLineBoxFromRenderObject();
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
299private:
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
310private:
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
317protected:
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
336private:
337 unsigned m_hasBadChildList : 1;
338#endif
339
340protected:
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
352inline void InlineFlowBox::checkConsistency() const
353{
354}
355
356#endif
357
358#if ASSERT_WITH_SECURITY_IMPLICATION_DISABLED
359
360inline void InlineFlowBox::setHasBadChildList()
361{
362}
363
364#endif
365
366} // namespace WebCore
367
368SPECIALIZE_TYPE_TRAITS_INLINE_BOX(InlineFlowBox, isInlineFlowBox())
369
370#if ENABLE(TREE_DEBUGGING)
371// Outside the WebCore namespace for ease of invocation from the debugger.
372void showTree(const WebCore::InlineFlowBox*);
373#endif
374