InlineBox.cpp revision 2fc2651226baac27029e38c9d6ef883fa32084db
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#include "config.h"
21#include "InlineBox.h"
22
23#include "HitTestResult.h"
24#include "InlineFlowBox.h"
25#include "PaintInfo.h"
26#include "RenderArena.h"
27#include "RenderBlock.h"
28#include "RootInlineBox.h"
29
30using namespace std;
31
32namespace WebCore {
33
34#ifndef NDEBUG
35static bool inInlineBoxDetach;
36#endif
37
38#ifndef NDEBUG
39
40InlineBox::~InlineBox()
41{
42    if (!m_hasBadParent && m_parent)
43        m_parent->setHasBadChildList();
44}
45
46#endif
47
48void InlineBox::remove()
49{
50    if (parent())
51        parent()->removeChild(this);
52}
53
54void InlineBox::destroy(RenderArena* renderArena)
55{
56#ifndef NDEBUG
57    inInlineBoxDetach = true;
58#endif
59    delete this;
60#ifndef NDEBUG
61    inInlineBoxDetach = false;
62#endif
63
64    // Recover the size left there for us by operator delete and free the memory.
65    renderArena->free(*(size_t *)this, this);
66}
67
68void* InlineBox::operator new(size_t sz, RenderArena* renderArena) throw()
69{
70    return renderArena->allocate(sz);
71}
72
73void InlineBox::operator delete(void* ptr, size_t sz)
74{
75    ASSERT(inInlineBoxDetach);
76
77    // Stash size where destroy can find it.
78    *(size_t *)ptr = sz;
79}
80
81#ifndef NDEBUG
82void InlineBox::showTreeForThis() const
83{
84    if (m_renderer)
85        m_renderer->showTreeForThis();
86}
87#endif
88
89int InlineBox::logicalHeight() const
90{
91#if ENABLE(SVG)
92    if (hasVirtualLogicalHeight())
93        return virtualLogicalHeight();
94#endif
95
96    if (renderer()->isText())
97        return m_isText ? renderer()->style(m_firstLine)->fontMetrics().height() : 0;
98    if (renderer()->isBox() && parent())
99        return isHorizontal() ? toRenderBox(m_renderer)->height() : toRenderBox(m_renderer)->width();
100
101    ASSERT(isInlineFlowBox());
102    RenderBoxModelObject* flowObject = boxModelObject();
103    const FontMetrics& fontMetrics = renderer()->style(m_firstLine)->fontMetrics();
104    int result = fontMetrics.height();
105    if (parent())
106        result += flowObject->borderAndPaddingLogicalHeight();
107    return result;
108}
109
110int InlineBox::caretMinOffset() const
111{
112    return m_renderer->caretMinOffset();
113}
114
115int InlineBox::caretMaxOffset() const
116{
117    return m_renderer->caretMaxOffset();
118}
119
120unsigned InlineBox::caretMaxRenderedOffset() const
121{
122    return 1;
123}
124
125void InlineBox::dirtyLineBoxes()
126{
127    markDirty();
128    for (InlineFlowBox* curr = parent(); curr && !curr->isDirty(); curr = curr->parent())
129        curr->markDirty();
130}
131
132void InlineBox::deleteLine(RenderArena* arena)
133{
134    if (!m_extracted && m_renderer->isBox())
135        toRenderBox(m_renderer)->setInlineBoxWrapper(0);
136    destroy(arena);
137}
138
139void InlineBox::extractLine()
140{
141    m_extracted = true;
142    if (m_renderer->isBox())
143        toRenderBox(m_renderer)->setInlineBoxWrapper(0);
144}
145
146void InlineBox::attachLine()
147{
148    m_extracted = false;
149    if (m_renderer->isBox())
150        toRenderBox(m_renderer)->setInlineBoxWrapper(this);
151}
152
153void InlineBox::adjustPosition(int dx, int dy)
154{
155    m_x += dx;
156    m_y += dy;
157    if (m_renderer->isReplaced()) {
158        RenderBox* box = toRenderBox(m_renderer);
159        box->move(dx, dy);
160    }
161}
162
163void InlineBox::paint(PaintInfo& paintInfo, int tx, int ty)
164{
165    if (!paintInfo.shouldPaintWithinRoot(renderer()) || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection))
166        return;
167
168    IntPoint childPoint = IntPoint(tx, ty);
169    if (parent()->renderer()->style()->isFlippedBlocksWritingMode()) // Faster than calling containingBlock().
170        childPoint = renderer()->containingBlock()->flipForWritingMode(toRenderBox(renderer()), childPoint, RenderBox::ParentToChildFlippingAdjustment);
171
172    // Paint all phases of replaced elements atomically, as though the replaced element established its
173    // own stacking context.  (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
174    // specification.)
175    bool preservePhase = paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip;
176    PaintInfo info(paintInfo);
177    info.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground;
178    renderer()->paint(info, childPoint.x(), childPoint.y());
179    if (!preservePhase) {
180        info.phase = PaintPhaseChildBlockBackgrounds;
181        renderer()->paint(info, childPoint.x(), childPoint.y());
182        info.phase = PaintPhaseFloat;
183        renderer()->paint(info, childPoint.x(), childPoint.y());
184        info.phase = PaintPhaseForeground;
185        renderer()->paint(info, childPoint.x(), childPoint.y());
186        info.phase = PaintPhaseOutline;
187        renderer()->paint(info, childPoint.x(), childPoint.y());
188    }
189}
190
191bool InlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty)
192{
193    // Hit test all phases of replaced elements atomically, as though the replaced element established its
194    // own stacking context.  (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
195    // specification.)
196    return renderer()->hitTest(request, result, IntPoint(x, y), tx, ty);
197}
198
199const RootInlineBox* InlineBox::root() const
200{
201    if (m_parent)
202        return m_parent->root();
203    ASSERT(isRootInlineBox());
204    return static_cast<const RootInlineBox*>(this);
205}
206
207RootInlineBox* InlineBox::root()
208{
209    if (m_parent)
210        return m_parent->root();
211    ASSERT(isRootInlineBox());
212    return static_cast<RootInlineBox*>(this);
213}
214
215bool InlineBox::nextOnLineExists() const
216{
217    if (!m_determinedIfNextOnLineExists) {
218        m_determinedIfNextOnLineExists = true;
219
220        if (!parent())
221            m_nextOnLineExists = false;
222        else if (nextOnLine())
223            m_nextOnLineExists = true;
224        else
225            m_nextOnLineExists = parent()->nextOnLineExists();
226    }
227    return m_nextOnLineExists;
228}
229
230bool InlineBox::prevOnLineExists() const
231{
232    if (!m_determinedIfPrevOnLineExists) {
233        m_determinedIfPrevOnLineExists = true;
234
235        if (!parent())
236            m_prevOnLineExists = false;
237        else if (prevOnLine())
238            m_prevOnLineExists = true;
239        else
240            m_prevOnLineExists = parent()->prevOnLineExists();
241    }
242    return m_prevOnLineExists;
243}
244
245InlineBox* InlineBox::nextLeafChild() const
246{
247    InlineBox* leaf = 0;
248    for (InlineBox* box = nextOnLine(); box && !leaf; box = box->nextOnLine())
249        leaf = box->isLeaf() ? box : static_cast<InlineFlowBox*>(box)->firstLeafChild();
250    if (!leaf && parent())
251        leaf = parent()->nextLeafChild();
252    return leaf;
253}
254
255InlineBox* InlineBox::prevLeafChild() const
256{
257    InlineBox* leaf = 0;
258    for (InlineBox* box = prevOnLine(); box && !leaf; box = box->prevOnLine())
259        leaf = box->isLeaf() ? box : static_cast<InlineFlowBox*>(box)->lastLeafChild();
260    if (!leaf && parent())
261        leaf = parent()->prevLeafChild();
262    return leaf;
263}
264
265RenderObject::SelectionState InlineBox::selectionState()
266{
267    return renderer()->selectionState();
268}
269
270bool InlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth)
271{
272    // Non-replaced elements can always accommodate an ellipsis.
273    if (!m_renderer || !m_renderer->isReplaced())
274        return true;
275
276    IntRect boxRect(m_x, 0, m_logicalWidth, 10);
277    IntRect ellipsisRect(ltr ? blockEdge - ellipsisWidth : blockEdge, 0, ellipsisWidth, 10);
278    return !(boxRect.intersects(ellipsisRect));
279}
280
281int InlineBox::placeEllipsisBox(bool, int, int, int, bool&)
282{
283    // Use -1 to mean "we didn't set the position."
284    return -1;
285}
286
287IntPoint InlineBox::locationIncludingFlipping()
288{
289    if (!renderer()->style()->isFlippedBlocksWritingMode())
290        return IntPoint(x(), y());
291    RenderBlock* block = root()->block();
292    if (block->style()->isHorizontalWritingMode())
293        return IntPoint(x(), block->height() - height() - y());
294    else
295        return IntPoint(block->width() - width() - x(), y());
296}
297
298void InlineBox::flipForWritingMode(IntRect& rect)
299{
300    if (!renderer()->style()->isFlippedBlocksWritingMode())
301        return;
302    root()->block()->flipForWritingMode(rect);
303}
304
305IntPoint InlineBox::flipForWritingMode(const IntPoint& point)
306{
307    if (!renderer()->style()->isFlippedBlocksWritingMode())
308        return point;
309    return root()->block()->flipForWritingMode(point);
310}
311
312} // namespace WebCore
313
314#ifndef NDEBUG
315
316void showTree(const WebCore::InlineBox* b)
317{
318    if (b)
319        b->showTreeForThis();
320}
321
322#endif
323