InlineBox.cpp revision 2bde8e466a4451c7319e3a072d118917957d6554
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(float dx, float dy)
154{
155    m_x += dx;
156    m_y += dy;
157
158    if (m_renderer->isReplaced())
159        toRenderBox(m_renderer)->move(dx, dy);
160}
161
162void InlineBox::paint(PaintInfo& paintInfo, int tx, int ty)
163{
164    if (!paintInfo.shouldPaintWithinRoot(renderer()) || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection))
165        return;
166
167    IntPoint childPoint = IntPoint(tx, ty);
168    if (parent()->renderer()->style()->isFlippedBlocksWritingMode()) // Faster than calling containingBlock().
169        childPoint = renderer()->containingBlock()->flipForWritingMode(toRenderBox(renderer()), childPoint, RenderBox::ParentToChildFlippingAdjustment);
170
171    // Paint all phases of replaced elements atomically, as though the replaced element established its
172    // own stacking context.  (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
173    // specification.)
174    bool preservePhase = paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip;
175    PaintInfo info(paintInfo);
176    info.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground;
177    renderer()->paint(info, childPoint.x(), childPoint.y());
178    if (!preservePhase) {
179        info.phase = PaintPhaseChildBlockBackgrounds;
180        renderer()->paint(info, childPoint.x(), childPoint.y());
181        info.phase = PaintPhaseFloat;
182        renderer()->paint(info, childPoint.x(), childPoint.y());
183        info.phase = PaintPhaseForeground;
184        renderer()->paint(info, childPoint.x(), childPoint.y());
185        info.phase = PaintPhaseOutline;
186        renderer()->paint(info, childPoint.x(), childPoint.y());
187    }
188}
189
190bool InlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty)
191{
192    // Hit test all phases of replaced elements atomically, as though the replaced element established its
193    // own stacking context.  (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
194    // specification.)
195    return renderer()->hitTest(request, result, IntPoint(x, y), tx, ty);
196}
197
198const RootInlineBox* InlineBox::root() const
199{
200    if (m_parent)
201        return m_parent->root();
202    ASSERT(isRootInlineBox());
203    return static_cast<const RootInlineBox*>(this);
204}
205
206RootInlineBox* InlineBox::root()
207{
208    if (m_parent)
209        return m_parent->root();
210    ASSERT(isRootInlineBox());
211    return static_cast<RootInlineBox*>(this);
212}
213
214bool InlineBox::nextOnLineExists() const
215{
216    if (!m_determinedIfNextOnLineExists) {
217        m_determinedIfNextOnLineExists = true;
218
219        if (!parent())
220            m_nextOnLineExists = false;
221        else if (nextOnLine())
222            m_nextOnLineExists = true;
223        else
224            m_nextOnLineExists = parent()->nextOnLineExists();
225    }
226    return m_nextOnLineExists;
227}
228
229bool InlineBox::prevOnLineExists() const
230{
231    if (!m_determinedIfPrevOnLineExists) {
232        m_determinedIfPrevOnLineExists = true;
233
234        if (!parent())
235            m_prevOnLineExists = false;
236        else if (prevOnLine())
237            m_prevOnLineExists = true;
238        else
239            m_prevOnLineExists = parent()->prevOnLineExists();
240    }
241    return m_prevOnLineExists;
242}
243
244InlineBox* InlineBox::nextLeafChild() const
245{
246    InlineBox* leaf = 0;
247    for (InlineBox* box = nextOnLine(); box && !leaf; box = box->nextOnLine())
248        leaf = box->isLeaf() ? box : static_cast<InlineFlowBox*>(box)->firstLeafChild();
249    if (!leaf && parent())
250        leaf = parent()->nextLeafChild();
251    return leaf;
252}
253
254InlineBox* InlineBox::prevLeafChild() const
255{
256    InlineBox* leaf = 0;
257    for (InlineBox* box = prevOnLine(); box && !leaf; box = box->prevOnLine())
258        leaf = box->isLeaf() ? box : static_cast<InlineFlowBox*>(box)->lastLeafChild();
259    if (!leaf && parent())
260        leaf = parent()->prevLeafChild();
261    return leaf;
262}
263
264RenderObject::SelectionState InlineBox::selectionState()
265{
266    return renderer()->selectionState();
267}
268
269bool InlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth)
270{
271    // Non-replaced elements can always accommodate an ellipsis.
272    if (!m_renderer || !m_renderer->isReplaced())
273        return true;
274
275    IntRect boxRect(m_x, 0, m_logicalWidth, 10);
276    IntRect ellipsisRect(ltr ? blockEdge - ellipsisWidth : blockEdge, 0, ellipsisWidth, 10);
277    return !(boxRect.intersects(ellipsisRect));
278}
279
280float InlineBox::placeEllipsisBox(bool, float, float, float, bool&)
281{
282    // Use -1 to mean "we didn't set the position."
283    return -1;
284}
285
286FloatPoint InlineBox::locationIncludingFlipping()
287{
288    if (!renderer()->style()->isFlippedBlocksWritingMode())
289        return FloatPoint(x(), y());
290    RenderBlock* block = root()->block();
291    if (block->style()->isHorizontalWritingMode())
292        return FloatPoint(x(), block->height() - height() - y());
293    else
294        return FloatPoint(block->width() - width() - x(), y());
295}
296
297void InlineBox::flipForWritingMode(FloatRect& rect)
298{
299    if (!renderer()->style()->isFlippedBlocksWritingMode())
300        return;
301    root()->block()->flipForWritingMode(rect);
302}
303
304FloatPoint InlineBox::flipForWritingMode(const FloatPoint& point)
305{
306    if (!renderer()->style()->isFlippedBlocksWritingMode())
307        return point;
308    return root()->block()->flipForWritingMode(point);
309}
310
311void InlineBox::flipForWritingMode(IntRect& rect)
312{
313    if (!renderer()->style()->isFlippedBlocksWritingMode())
314        return;
315    root()->block()->flipForWritingMode(rect);
316}
317
318IntPoint InlineBox::flipForWritingMode(const IntPoint& point)
319{
320    if (!renderer()->style()->isFlippedBlocksWritingMode())
321        return point;
322    return root()->block()->flipForWritingMode(point);
323}
324
325} // namespace WebCore
326
327#ifndef NDEBUG
328
329void showTree(const WebCore::InlineBox* b)
330{
331    if (b)
332        b->showTreeForThis();
333}
334
335#endif
336