1 | /* |
2 | * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2009, 2010, 2011 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 "RenderBoxModelObject.h" |
24 | #include "RenderText.h" |
25 | #include "TextFlags.h" |
26 | #include <wtf/IsoMalloc.h> |
27 | #include <wtf/TypeCasts.h> |
28 | |
29 | namespace WebCore { |
30 | |
31 | class HitTestRequest; |
32 | class HitTestResult; |
33 | class RootInlineBox; |
34 | |
35 | // InlineBox represents a rectangle that occurs on a line. It corresponds to |
36 | // some RenderObject (i.e., it represents a portion of that RenderObject). |
37 | class InlineBox { |
38 | WTF_MAKE_ISO_ALLOCATED(InlineBox); |
39 | public: |
40 | virtual ~InlineBox(); |
41 | |
42 | void assertNotDeleted() const; |
43 | |
44 | virtual void deleteLine() = 0; |
45 | virtual void () = 0; |
46 | virtual void attachLine() = 0; |
47 | |
48 | virtual bool isLineBreak() const { return renderer().isLineBreak(); } |
49 | |
50 | WEBCORE_EXPORT virtual void adjustPosition(float dx, float dy); |
51 | void adjustLogicalPosition(float deltaLogicalLeft, float deltaLogicalTop) |
52 | { |
53 | if (isHorizontal()) |
54 | adjustPosition(deltaLogicalLeft, deltaLogicalTop); |
55 | else |
56 | adjustPosition(deltaLogicalTop, deltaLogicalLeft); |
57 | } |
58 | void adjustLineDirectionPosition(float delta) |
59 | { |
60 | if (isHorizontal()) |
61 | adjustPosition(delta, 0); |
62 | else |
63 | adjustPosition(0, delta); |
64 | } |
65 | void adjustBlockDirectionPosition(float delta) |
66 | { |
67 | if (isHorizontal()) |
68 | adjustPosition(0, delta); |
69 | else |
70 | adjustPosition(delta, 0); |
71 | } |
72 | |
73 | virtual void paint(PaintInfo&, const LayoutPoint&, LayoutUnit lineTop, LayoutUnit lineBottom) = 0; |
74 | virtual bool nodeAtPoint(const HitTestRequest&, HitTestResult&, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom, HitTestAction) = 0; |
75 | |
76 | #if ENABLE(TREE_DEBUGGING) |
77 | void showNodeTreeForThis() const; |
78 | void showLineTreeForThis() const; |
79 | |
80 | virtual void outputLineTreeAndMark(WTF::TextStream&, const InlineBox* markedBox, int depth) const; |
81 | virtual void outputLineBox(WTF::TextStream&, bool mark, int depth) const; |
82 | virtual const char* boxName() const; |
83 | #endif |
84 | |
85 | bool behavesLikeText() const { return m_bitfields.behavesLikeText(); } |
86 | void setBehavesLikeText(bool behavesLikeText) { m_bitfields.setBehavesLikeText(behavesLikeText); } |
87 | |
88 | virtual bool isInlineElementBox() const { return false; } |
89 | virtual bool isInlineFlowBox() const { return false; } |
90 | virtual bool isInlineTextBox() const { return false; } |
91 | virtual bool isRootInlineBox() const { return false; } |
92 | virtual bool isSVGInlineTextBox() const { return false; } |
93 | virtual bool isSVGInlineFlowBox() const { return false; } |
94 | virtual bool isSVGRootInlineBox() const { return false; } |
95 | |
96 | bool hasVirtualLogicalHeight() const { return m_bitfields.hasVirtualLogicalHeight(); } |
97 | void setHasVirtualLogicalHeight() { m_bitfields.setHasVirtualLogicalHeight(true); } |
98 | virtual float virtualLogicalHeight() const |
99 | { |
100 | ASSERT_NOT_REACHED(); |
101 | return 0; |
102 | } |
103 | |
104 | bool isHorizontal() const { return m_bitfields.isHorizontal(); } |
105 | void setIsHorizontal(bool isHorizontal) { m_bitfields.setIsHorizontal(isHorizontal); } |
106 | |
107 | virtual FloatRect calculateBoundaries() const |
108 | { |
109 | ASSERT_NOT_REACHED(); |
110 | return FloatRect(); |
111 | } |
112 | |
113 | bool isConstructed() { return m_bitfields.constructed(); } |
114 | virtual void setConstructed() { m_bitfields.setConstructed(true); } |
115 | |
116 | void (bool = true) { m_bitfields.setExtracted(extracted); } |
117 | |
118 | void setIsFirstLine(bool firstLine) { m_bitfields.setFirstLine(firstLine); } |
119 | bool isFirstLine() const { return m_bitfields.firstLine(); } |
120 | |
121 | void removeFromParent(); |
122 | |
123 | InlineBox* nextOnLine() const { return m_next; } |
124 | InlineBox* prevOnLine() const { return m_prev; } |
125 | void setNextOnLine(InlineBox* next) |
126 | { |
127 | ASSERT(m_parent || !next); |
128 | m_next = next; |
129 | } |
130 | void setPrevOnLine(InlineBox* prev) |
131 | { |
132 | ASSERT(m_parent || !prev); |
133 | m_prev = prev; |
134 | } |
135 | bool nextOnLineExists() const; |
136 | bool previousOnLineExists() const; |
137 | |
138 | virtual bool isLeaf() const { return true; } |
139 | |
140 | InlineBox* nextLeafChild() const; |
141 | InlineBox* prevLeafChild() const; |
142 | |
143 | // Helper functions for editing and hit-testing code. |
144 | // FIXME: These two functions should be moved to RenderedPosition once the code to convert between |
145 | // Position and inline box, offset pair is moved to RenderedPosition. |
146 | InlineBox* nextLeafChildIgnoringLineBreak() const; |
147 | InlineBox* prevLeafChildIgnoringLineBreak() const; |
148 | |
149 | // FIXME: Hide this once all callers are using tighter types. |
150 | RenderObject& renderer() const { return m_renderer; } |
151 | |
152 | InlineFlowBox* parent() const |
153 | { |
154 | assertNotDeleted(); |
155 | ASSERT_WITH_SECURITY_IMPLICATION(!m_hasBadParent); |
156 | return m_parent; |
157 | } |
158 | void setParent(InlineFlowBox* par) { m_parent = par; } |
159 | |
160 | const RootInlineBox& root() const; |
161 | RootInlineBox& root(); |
162 | |
163 | // x() is the left side of the box in the containing block's coordinate system. |
164 | void setX(float x) { m_topLeft.setX(x); } |
165 | float x() const { return m_topLeft.x(); } |
166 | float left() const { return m_topLeft.x(); } |
167 | |
168 | // y() is the top side of the box in the containing block's coordinate system. |
169 | void setY(float y) { m_topLeft.setY(y); } |
170 | float y() const { return m_topLeft.y(); } |
171 | float top() const { return m_topLeft.y(); } |
172 | |
173 | const FloatPoint& topLeft() const { return m_topLeft; } |
174 | |
175 | float width() const { return isHorizontal() ? logicalWidth() : logicalHeight(); } |
176 | float height() const { return isHorizontal() ? logicalHeight() : logicalWidth(); } |
177 | FloatSize size() const { return FloatSize(width(), height()); } |
178 | float right() const { return left() + width(); } |
179 | float bottom() const { return top() + height(); } |
180 | |
181 | // The logicalLeft position is the left edge of the line box in a horizontal line and the top edge in a vertical line. |
182 | float logicalLeft() const { return isHorizontal() ? m_topLeft.x() : m_topLeft.y(); } |
183 | float logicalRight() const { return logicalLeft() + logicalWidth(); } |
184 | void setLogicalLeft(float left) |
185 | { |
186 | if (isHorizontal()) |
187 | setX(left); |
188 | else |
189 | setY(left); |
190 | } |
191 | |
192 | // The logicalTop[ position is the top edge of the line box in a horizontal line and the left edge in a vertical line. |
193 | float logicalTop() const { return isHorizontal() ? m_topLeft.y() : m_topLeft.x(); } |
194 | float logicalBottom() const { return logicalTop() + logicalHeight(); } |
195 | void setLogicalTop(float top) |
196 | { |
197 | if (isHorizontal()) |
198 | setY(top); |
199 | else |
200 | setX(top); |
201 | } |
202 | |
203 | // The logical width is our extent in the line's overall inline direction, i.e., width for horizontal text and height for vertical text. |
204 | void setLogicalWidth(float w) { m_logicalWidth = w; } |
205 | float logicalWidth() const { return m_logicalWidth; } |
206 | |
207 | // The logical height is our extent in the block flow direction, i.e., height for horizontal text and width for vertical text. |
208 | float logicalHeight() const; |
209 | |
210 | FloatRect logicalFrameRect() const { return isHorizontal() ? FloatRect(m_topLeft.x(), m_topLeft.y(), m_logicalWidth, logicalHeight()) : FloatRect(m_topLeft.y(), m_topLeft.x(), m_logicalWidth, logicalHeight()); } |
211 | FloatRect frameRect() const { return FloatRect(topLeft(), size()); } |
212 | |
213 | WEBCORE_EXPORT virtual int baselinePosition(FontBaseline baselineType) const; |
214 | WEBCORE_EXPORT virtual LayoutUnit lineHeight() const; |
215 | |
216 | WEBCORE_EXPORT virtual int caretMinOffset() const; |
217 | WEBCORE_EXPORT virtual int caretMaxOffset() const; |
218 | |
219 | unsigned char bidiLevel() const { return m_bitfields.bidiEmbeddingLevel(); } |
220 | void setBidiLevel(unsigned char level) { m_bitfields.setBidiEmbeddingLevel(level); } |
221 | TextDirection direction() const { return bidiLevel() % 2 ? TextDirection::RTL : TextDirection::LTR; } |
222 | bool isLeftToRightDirection() const { return direction() == TextDirection::LTR; } |
223 | int caretLeftmostOffset() const { return isLeftToRightDirection() ? caretMinOffset() : caretMaxOffset(); } |
224 | int caretRightmostOffset() const { return isLeftToRightDirection() ? caretMaxOffset() : caretMinOffset(); } |
225 | |
226 | virtual void clearTruncation() { } |
227 | |
228 | bool isDirty() const { return m_bitfields.dirty(); } |
229 | virtual void markDirty(bool dirty = true) { m_bitfields.setDirty(dirty); } |
230 | |
231 | WEBCORE_EXPORT virtual void dirtyLineBoxes(); |
232 | |
233 | WEBCORE_EXPORT virtual RenderObject::SelectionState selectionState(); |
234 | |
235 | WEBCORE_EXPORT virtual bool canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) const; |
236 | // visibleLeftEdge, visibleRightEdge are in the parent's coordinate system. |
237 | WEBCORE_EXPORT virtual float placeEllipsisBox(bool ltr, float visibleLeftEdge, float visibleRightEdge, float ellipsisWidth, float &truncatedWidth, bool&); |
238 | |
239 | #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED |
240 | void setHasBadParent(); |
241 | void invalidateParentChildList(); |
242 | #endif |
243 | |
244 | bool visibleToHitTesting() const { return renderer().style().visibility() == Visibility::Visible && renderer().style().pointerEvents() != PointerEvents::None; } |
245 | |
246 | const RenderStyle& lineStyle() const { return m_bitfields.firstLine() ? renderer().firstLineStyle() : renderer().style(); } |
247 | |
248 | VerticalAlign verticalAlign() const { return lineStyle().verticalAlign(); } |
249 | |
250 | // Use with caution! The type is not checked! |
251 | RenderBoxModelObject* boxModelObject() const |
252 | { |
253 | if (!is<RenderText>(m_renderer)) |
254 | return &downcast<RenderBoxModelObject>(m_renderer); |
255 | return nullptr; |
256 | } |
257 | |
258 | FloatPoint locationIncludingFlipping() const; |
259 | void flipForWritingMode(FloatRect&) const; |
260 | FloatPoint flipForWritingMode(const FloatPoint&) const; |
261 | void flipForWritingMode(LayoutRect&) const; |
262 | LayoutPoint flipForWritingMode(const LayoutPoint&) const; |
263 | |
264 | bool knownToHaveNoOverflow() const { return m_bitfields.knownToHaveNoOverflow(); } |
265 | void clearKnownToHaveNoOverflow(); |
266 | |
267 | bool dirOverride() const { return m_bitfields.dirOverride(); } |
268 | void setDirOverride(bool dirOverride) { m_bitfields.setDirOverride(dirOverride); } |
269 | |
270 | void setExpansion(float newExpansion) |
271 | { |
272 | m_logicalWidth -= m_expansion; |
273 | m_expansion = newExpansion; |
274 | m_logicalWidth += m_expansion; |
275 | } |
276 | void setExpansionWithoutGrowing(float newExpansion) |
277 | { |
278 | ASSERT(!m_expansion); |
279 | m_expansion = newExpansion; |
280 | } |
281 | float expansion() const { return m_expansion; } |
282 | |
283 | void setHasHyphen(bool hasHyphen) { m_bitfields.setHasEllipsisBoxOrHyphen(hasHyphen); } |
284 | void setCanHaveLeadingExpansion(bool canHaveLeadingExpansion) { m_bitfields.setHasSelectedChildrenOrCanHaveLeadingExpansion(canHaveLeadingExpansion); } |
285 | void setCanHaveTrailingExpansion(bool canHaveTrailingExpansion) { m_bitfields.setCanHaveTrailingExpansion(canHaveTrailingExpansion); } |
286 | void setForceTrailingExpansion() { m_bitfields.setForceTrailingExpansion(true); } |
287 | void setForceLeadingExpansion() { m_bitfields.setForceLeadingExpansion(true); } |
288 | |
289 | private: |
290 | InlineBox* m_next { nullptr }; // The next element on the same line as us. |
291 | InlineBox* m_prev { nullptr }; // The previous element on the same line as us. |
292 | |
293 | InlineFlowBox* m_parent { nullptr }; // The box that contains us. |
294 | |
295 | RenderObject& m_renderer; |
296 | |
297 | private: |
298 | float m_logicalWidth { 0 }; |
299 | float m_expansion { 0 }; |
300 | FloatPoint m_topLeft; |
301 | |
302 | #define ADD_BOOLEAN_BITFIELD(name, Name) \ |
303 | private:\ |
304 | unsigned m_##name : 1;\ |
305 | public:\ |
306 | bool name() const { return m_##name; }\ |
307 | void set##Name(bool name) { m_##name = name; }\ |
308 | |
309 | class InlineBoxBitfields { |
310 | public: |
311 | explicit InlineBoxBitfields(bool firstLine = false, bool constructed = false, bool dirty = false, bool = false, bool isHorizontal = true) |
312 | : m_firstLine(firstLine) |
313 | , m_constructed(constructed) |
314 | , m_bidiEmbeddingLevel(0) |
315 | , m_dirty(dirty) |
316 | , m_extracted(extracted) |
317 | , m_hasVirtualLogicalHeight(false) |
318 | , m_isHorizontal(isHorizontal) |
319 | , m_endsWithBreak(false) |
320 | , m_hasSelectedChildrenOrCanHaveLeadingExpansion(false) |
321 | , m_canHaveTrailingExpansion(false) |
322 | , m_knownToHaveNoOverflow(true) |
323 | , m_hasEllipsisBoxOrHyphen(false) |
324 | , m_dirOverride(false) |
325 | , m_behavesLikeText(false) |
326 | , m_forceTrailingExpansion(false) |
327 | , m_forceLeadingExpansion(false) |
328 | , m_determinedIfNextOnLineExists(false) |
329 | , m_nextOnLineExists(false) |
330 | { |
331 | } |
332 | |
333 | // Some of these bits are actually for subclasses and moved here to compact the structures. |
334 | // for this class |
335 | ADD_BOOLEAN_BITFIELD(firstLine, FirstLine); |
336 | ADD_BOOLEAN_BITFIELD(constructed, Constructed); |
337 | |
338 | private: |
339 | unsigned m_bidiEmbeddingLevel : 6; // The maximium bidi level is 62: http://unicode.org/reports/tr9/#Explicit_Levels_and_Directions |
340 | |
341 | public: |
342 | unsigned char bidiEmbeddingLevel() const { return m_bidiEmbeddingLevel; } |
343 | void setBidiEmbeddingLevel(unsigned char bidiEmbeddingLevel) { m_bidiEmbeddingLevel = bidiEmbeddingLevel; } |
344 | |
345 | ADD_BOOLEAN_BITFIELD(dirty, Dirty); |
346 | ADD_BOOLEAN_BITFIELD(, Extracted); |
347 | ADD_BOOLEAN_BITFIELD(hasVirtualLogicalHeight, HasVirtualLogicalHeight); |
348 | ADD_BOOLEAN_BITFIELD(isHorizontal, IsHorizontal); |
349 | // for RootInlineBox |
350 | ADD_BOOLEAN_BITFIELD(endsWithBreak, EndsWithBreak); // Whether the line ends with a <br>. |
351 | // shared between RootInlineBox and InlineTextBox |
352 | ADD_BOOLEAN_BITFIELD(hasSelectedChildrenOrCanHaveLeadingExpansion, HasSelectedChildrenOrCanHaveLeadingExpansion); |
353 | ADD_BOOLEAN_BITFIELD(canHaveTrailingExpansion, CanHaveTrailingExpansion); |
354 | ADD_BOOLEAN_BITFIELD(knownToHaveNoOverflow, KnownToHaveNoOverflow); |
355 | ADD_BOOLEAN_BITFIELD(hasEllipsisBoxOrHyphen, HasEllipsisBoxOrHyphen); |
356 | // for InlineTextBox |
357 | ADD_BOOLEAN_BITFIELD(dirOverride, DirOverride); |
358 | ADD_BOOLEAN_BITFIELD(behavesLikeText, BehavesLikeText); // Whether or not this object represents text with a non-zero height. Includes non-image list markers, text boxes, br. |
359 | ADD_BOOLEAN_BITFIELD(forceTrailingExpansion, ForceTrailingExpansion); |
360 | ADD_BOOLEAN_BITFIELD(forceLeadingExpansion, ForceLeadingExpansion); |
361 | |
362 | private: |
363 | mutable unsigned m_determinedIfNextOnLineExists : 1; |
364 | |
365 | public: |
366 | bool determinedIfNextOnLineExists() const { return m_determinedIfNextOnLineExists; } |
367 | void setDeterminedIfNextOnLineExists(bool determinedIfNextOnLineExists) const { m_determinedIfNextOnLineExists = determinedIfNextOnLineExists; } |
368 | |
369 | private: |
370 | mutable unsigned m_nextOnLineExists : 1; |
371 | |
372 | public: |
373 | bool nextOnLineExists() const { return m_nextOnLineExists; } |
374 | void setNextOnLineExists(bool nextOnLineExists) const { m_nextOnLineExists = nextOnLineExists; } |
375 | }; |
376 | #undef ADD_BOOLEAN_BITFIELD |
377 | |
378 | InlineBoxBitfields m_bitfields; |
379 | |
380 | protected: |
381 | explicit InlineBox(RenderObject& renderer) |
382 | : m_renderer(renderer) |
383 | { |
384 | } |
385 | |
386 | InlineBox(RenderObject& renderer, FloatPoint topLeft, float logicalWidth, bool firstLine, bool constructed, bool dirty, bool , bool isHorizontal, InlineBox* next, InlineBox* prev, InlineFlowBox* parent) |
387 | : m_next(next) |
388 | , m_prev(prev) |
389 | , m_parent(parent) |
390 | , m_renderer(renderer) |
391 | , m_logicalWidth(logicalWidth) |
392 | , m_topLeft(topLeft) |
393 | , m_bitfields(firstLine, constructed, dirty, extracted, isHorizontal) |
394 | { |
395 | } |
396 | |
397 | // For RootInlineBox |
398 | bool endsWithBreak() const { return m_bitfields.endsWithBreak(); } |
399 | void setEndsWithBreak(bool endsWithBreak) { m_bitfields.setEndsWithBreak(endsWithBreak); } |
400 | bool hasEllipsisBox() const { return m_bitfields.hasEllipsisBoxOrHyphen(); } |
401 | bool hasSelectedChildren() const { return m_bitfields.hasSelectedChildrenOrCanHaveLeadingExpansion(); } |
402 | void setHasSelectedChildren(bool hasSelectedChildren) { m_bitfields.setHasSelectedChildrenOrCanHaveLeadingExpansion(hasSelectedChildren); } |
403 | void setHasEllipsisBox(bool hasEllipsisBox) { m_bitfields.setHasEllipsisBoxOrHyphen(hasEllipsisBox); } |
404 | |
405 | // For InlineTextBox |
406 | bool hasHyphen() const { return m_bitfields.hasEllipsisBoxOrHyphen(); } |
407 | bool canHaveLeadingExpansion() const { return m_bitfields.hasSelectedChildrenOrCanHaveLeadingExpansion(); } |
408 | bool canHaveTrailingExpansion() const { return m_bitfields.canHaveTrailingExpansion(); } |
409 | bool forceTrailingExpansion() const { return m_bitfields.forceTrailingExpansion(); } |
410 | bool forceLeadingExpansion() const { return m_bitfields.forceLeadingExpansion(); } |
411 | |
412 | // For InlineFlowBox and InlineTextBox |
413 | bool () const { return m_bitfields.extracted(); } |
414 | |
415 | protected: |
416 | |
417 | #if !ASSERT_WITH_SECURITY_IMPLICATION_DISABLED |
418 | private: |
419 | static constexpr unsigned deletionSentinelNotDeletedValue = 0xF0F0F0F0U; |
420 | static constexpr unsigned deletionSentinelDeletedValue = 0xF0DEADF0U; |
421 | unsigned m_deletionSentinel { deletionSentinelNotDeletedValue }; |
422 | bool m_hasBadParent { false }; |
423 | protected: |
424 | bool m_isEverInChildList { true }; |
425 | #endif |
426 | }; |
427 | |
428 | #if ASSERT_WITH_SECURITY_IMPLICATION_DISABLED |
429 | |
430 | inline InlineBox::~InlineBox() |
431 | { |
432 | } |
433 | |
434 | inline void InlineBox::assertNotDeleted() const |
435 | { |
436 | } |
437 | |
438 | #endif |
439 | |
440 | } // namespace WebCore |
441 | |
442 | #define SPECIALIZE_TYPE_TRAITS_INLINE_BOX(ToValueTypeName, predicate) \ |
443 | SPECIALIZE_TYPE_TRAITS_BEGIN(WebCore::ToValueTypeName) \ |
444 | static bool isType(const WebCore::InlineBox& box) { return box.predicate; } \ |
445 | SPECIALIZE_TYPE_TRAITS_END() |
446 | |
447 | #if ENABLE(TREE_DEBUGGING) |
448 | // Outside the WebCore namespace for ease of invocation from the debugger. |
449 | void showNodeTree(const WebCore::InlineBox*); |
450 | void showLineTree(const WebCore::InlineBox*); |
451 | #endif |
452 | |