InlineBox.cpp revision cad810f21b803229eb11403f9209855525a25d57
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 "RenderArena.h"
26#include "RenderBlock.h"
27#include "RootInlineBox.h"
28
29using namespace std;
30
31namespace WebCore {
32
33#ifndef NDEBUG
34static bool inInlineBoxDetach;
35#endif
36
37#ifndef NDEBUG
38
39InlineBox::~InlineBox()
40{
41    if (!m_hasBadParent && m_parent)
42        m_parent->setHasBadChildList();
43}
44
45#endif
46
47void InlineBox::remove()
48{
49    if (parent())
50        parent()->removeChild(this);
51}
52
53void InlineBox::destroy(RenderArena* renderArena)
54{
55#ifndef NDEBUG
56    inInlineBoxDetach = true;
57#endif
58    delete this;
59#ifndef NDEBUG
60    inInlineBoxDetach = false;
61#endif
62
63    // Recover the size left there for us by operator delete and free the memory.
64    renderArena->free(*(size_t *)this, this);
65}
66
67void* InlineBox::operator new(size_t sz, RenderArena* renderArena) throw()
68{
69    return renderArena->allocate(sz);
70}
71
72void InlineBox::operator delete(void* ptr, size_t sz)
73{
74    ASSERT(inInlineBoxDetach);
75
76    // Stash size where destroy can find it.
77    *(size_t *)ptr = sz;
78}
79
80#ifndef NDEBUG
81void InlineBox::showTreeForThis() const
82{
83    if (m_renderer)
84        m_renderer->showTreeForThis();
85}
86#endif
87
88int InlineBox::logicalHeight() const
89{
90#if ENABLE(SVG)
91    if (hasVirtualLogicalHeight())
92        return virtualLogicalHeight();
93#endif
94
95    if (renderer()->isText())
96        return m_isText ? renderer()->style(m_firstLine)->font().height() : 0;
97    if (renderer()->isBox() && parent())
98        return isHorizontal() ? toRenderBox(m_renderer)->height() : toRenderBox(m_renderer)->width();
99
100    ASSERT(isInlineFlowBox());
101    RenderBoxModelObject* flowObject = boxModelObject();
102    const Font& font = renderer()->style(m_firstLine)->font();
103    int result = font.height();
104    if (parent())
105        result += flowObject->borderAndPaddingLogicalHeight();
106    return result;
107}
108
109int InlineBox::caretMinOffset() const
110{
111    return m_renderer->caretMinOffset();
112}
113
114int InlineBox::caretMaxOffset() const
115{
116    return m_renderer->caretMaxOffset();
117}
118
119unsigned InlineBox::caretMaxRenderedOffset() const
120{
121    return 1;
122}
123
124void InlineBox::dirtyLineBoxes()
125{
126    markDirty();
127    for (InlineFlowBox* curr = parent(); curr && !curr->isDirty(); curr = curr->parent())
128        curr->markDirty();
129}
130
131void InlineBox::deleteLine(RenderArena* arena)
132{
133    if (!m_extracted && m_renderer->isBox())
134        toRenderBox(m_renderer)->setInlineBoxWrapper(0);
135    destroy(arena);
136}
137
138void InlineBox::extractLine()
139{
140    m_extracted = true;
141    if (m_renderer->isBox())
142        toRenderBox(m_renderer)->setInlineBoxWrapper(0);
143}
144
145void InlineBox::attachLine()
146{
147    m_extracted = false;
148    if (m_renderer->isBox())
149        toRenderBox(m_renderer)->setInlineBoxWrapper(this);
150}
151
152void InlineBox::adjustPosition(int dx, int dy)
153{
154    m_x += dx;
155    m_y += dy;
156    if (m_renderer->isReplaced()) {
157        RenderBox* box = toRenderBox(m_renderer);
158        box->move(dx, dy);
159    }
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
280int InlineBox::placeEllipsisBox(bool, int, int, int, bool&)
281{
282    // Use -1 to mean "we didn't set the position."
283    return -1;
284}
285
286IntPoint InlineBox::locationIncludingFlipping()
287{
288    if (!renderer()->style()->isFlippedBlocksWritingMode())
289        return IntPoint(x(), y());
290    RenderBlock* block = root()->block();
291    if (block->style()->isHorizontalWritingMode())
292        return IntPoint(x(), block->height() - height() - y());
293    else
294        return IntPoint(block->width() - width() - x(), y());
295}
296
297void InlineBox::flipForWritingMode(IntRect& rect)
298{
299    if (!renderer()->style()->isFlippedBlocksWritingMode())
300        return;
301    root()->block()->flipForWritingMode(rect);
302}
303
304IntPoint InlineBox::flipForWritingMode(const IntPoint& point)
305{
306    if (!renderer()->style()->isFlippedBlocksWritingMode())
307        return point;
308    return root()->block()->flipForWritingMode(point);
309}
310
311} // namespace WebCore
312
313#ifndef NDEBUG
314
315void showTree(const WebCore::InlineBox* b)
316{
317    if (b)
318        b->showTreeForThis();
319}
320
321#endif
322