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