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