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 "core/rendering/InlineBox.h"
22
23#include "core/paint/BlockPainter.h"
24#include "core/rendering/InlineFlowBox.h"
25#include "core/rendering/PaintInfo.h"
26#include "core/rendering/RenderBlockFlow.h"
27#include "core/rendering/RenderObjectInlines.h"
28#include "core/rendering/RootInlineBox.h"
29#include "platform/Partitions.h"
30#include "platform/fonts/FontMetrics.h"
31
32#ifndef NDEBUG
33#include <stdio.h>
34#endif
35
36namespace blink {
37
38struct SameSizeAsInlineBox {
39    virtual ~SameSizeAsInlineBox() { }
40    void* a[4];
41    FloatPoint b;
42    float c;
43    uint32_t d : 32;
44#if ENABLE(ASSERT)
45    bool f;
46#endif
47};
48
49COMPILE_ASSERT(sizeof(InlineBox) == sizeof(SameSizeAsInlineBox), InlineBox_size_guard);
50
51#if ENABLE(ASSERT)
52
53InlineBox::~InlineBox()
54{
55    if (!m_hasBadParent && m_parent)
56        m_parent->setHasBadChildList();
57}
58
59#endif
60
61void InlineBox::remove(MarkLineBoxes markLineBoxes)
62{
63    if (parent())
64        parent()->removeChild(this, markLineBoxes);
65}
66
67void* InlineBox::operator new(size_t sz)
68{
69    return partitionAlloc(Partitions::getRenderingPartition(), sz);
70}
71
72void InlineBox::operator delete(void* ptr)
73{
74    partitionFree(ptr);
75}
76
77#ifndef NDEBUG
78const char* InlineBox::boxName() const
79{
80    return "InlineBox";
81}
82
83void InlineBox::showTreeForThis() const
84{
85    renderer().showTreeForThis();
86}
87
88void InlineBox::showLineTreeForThis() const
89{
90    renderer().containingBlock()->showLineTreeAndMark(this, "*");
91}
92
93void InlineBox::showLineTreeAndMark(const InlineBox* markedBox1, const char* markedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const RenderObject* obj, int depth) const
94{
95    int printedCharacters = 0;
96    if (this == markedBox1)
97        printedCharacters += fprintf(stderr, "%s", markedLabel1);
98    if (this == markedBox2)
99        printedCharacters += fprintf(stderr, "%s", markedLabel2);
100    if (&renderer() == obj)
101        printedCharacters += fprintf(stderr, "*");
102    for (; printedCharacters < depth * 2; printedCharacters++)
103        fputc(' ', stderr);
104
105    showBox(printedCharacters);
106}
107
108void InlineBox::showBox(int printedCharacters) const
109{
110    printedCharacters += fprintf(stderr, "%s\t%p", boxName(), this);
111    for (; printedCharacters < showTreeCharacterOffset; printedCharacters++)
112        fputc(' ', stderr);
113    fprintf(stderr, "\t%s %p {pos=%g,%g size=%g,%g} baseline=%i/%i\n",
114        renderer().renderName(), &renderer(), x(), y(), width(), height(),
115        baselinePosition(AlphabeticBaseline),
116        baselinePosition(IdeographicBaseline));
117}
118#endif
119
120float InlineBox::logicalHeight() const
121{
122    if (hasVirtualLogicalHeight())
123        return virtualLogicalHeight();
124
125    if (renderer().isText())
126        return m_bitfields.isText() ? renderer().style(isFirstLineStyle())->fontMetrics().height() : 0;
127    if (renderer().isBox() && parent())
128        return isHorizontal() ? toRenderBox(renderer()).height().toFloat() : toRenderBox(renderer()).width().toFloat();
129
130    ASSERT(isInlineFlowBox());
131    RenderBoxModelObject* flowObject = boxModelObject();
132    const FontMetrics& fontMetrics = renderer().style(isFirstLineStyle())->fontMetrics();
133    float result = fontMetrics.height();
134    if (parent())
135        result += flowObject->borderAndPaddingLogicalHeight();
136    return result;
137}
138
139int InlineBox::baselinePosition(FontBaseline baselineType) const
140{
141    return boxModelObject()->baselinePosition(baselineType, m_bitfields.firstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
142}
143
144LayoutUnit InlineBox::lineHeight() const
145{
146    return boxModelObject()->lineHeight(m_bitfields.firstLine(), isHorizontal() ? HorizontalLine : VerticalLine, PositionOnContainingLine);
147}
148
149int InlineBox::caretMinOffset() const
150{
151    return renderer().caretMinOffset();
152}
153
154int InlineBox::caretMaxOffset() const
155{
156    return renderer().caretMaxOffset();
157}
158
159void InlineBox::dirtyLineBoxes()
160{
161    markDirty();
162    for (InlineFlowBox* curr = parent(); curr && !curr->isDirty(); curr = curr->parent())
163        curr->markDirty();
164}
165
166void InlineBox::deleteLine()
167{
168    if (!m_bitfields.extracted() && renderer().isBox())
169        toRenderBox(renderer()).setInlineBoxWrapper(0);
170    destroy();
171}
172
173void InlineBox::extractLine()
174{
175    m_bitfields.setExtracted(true);
176    if (renderer().isBox())
177        toRenderBox(renderer()).setInlineBoxWrapper(0);
178}
179
180void InlineBox::attachLine()
181{
182    m_bitfields.setExtracted(false);
183    if (renderer().isBox())
184        toRenderBox(renderer()).setInlineBoxWrapper(this);
185}
186
187void InlineBox::adjustPosition(float dx, float dy)
188{
189    m_topLeft.move(dx, dy);
190
191    if (renderer().isReplaced())
192        toRenderBox(renderer()).move(dx, dy);
193}
194
195void InlineBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/)
196{
197    BlockPainter::paintInlineBox(*this, paintInfo, paintOffset);
198}
199
200bool InlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit /* lineTop */, LayoutUnit /*lineBottom*/)
201{
202    // Hit test all phases of replaced elements atomically, as though the replaced element established its
203    // own stacking context.  (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
204    // specification.)
205    LayoutPoint childPoint = accumulatedOffset;
206    if (parent()->renderer().style()->isFlippedBlocksWritingMode()) // Faster than calling containingBlock().
207        childPoint = renderer().containingBlock()->flipForWritingModeForChild(&toRenderBox(renderer()), childPoint);
208
209    return renderer().hitTest(request, result, locationInContainer, childPoint);
210}
211
212const RootInlineBox& InlineBox::root() const
213{
214    if (m_parent)
215        return m_parent->root();
216    ASSERT(isRootInlineBox());
217    return static_cast<const RootInlineBox&>(*this);
218}
219
220RootInlineBox& InlineBox::root()
221{
222    if (m_parent)
223        return m_parent->root();
224    ASSERT(isRootInlineBox());
225    return static_cast<RootInlineBox&>(*this);
226}
227
228bool InlineBox::nextOnLineExists() const
229{
230    if (!m_bitfields.determinedIfNextOnLineExists()) {
231        m_bitfields.setDeterminedIfNextOnLineExists(true);
232
233        if (!parent())
234            m_bitfields.setNextOnLineExists(false);
235        else if (nextOnLine())
236            m_bitfields.setNextOnLineExists(true);
237        else
238            m_bitfields.setNextOnLineExists(parent()->nextOnLineExists());
239    }
240    return m_bitfields.nextOnLineExists();
241}
242
243InlineBox* InlineBox::nextLeafChild() const
244{
245    InlineBox* leaf = 0;
246    for (InlineBox* box = nextOnLine(); box && !leaf; box = box->nextOnLine())
247        leaf = box->isLeaf() ? box : toInlineFlowBox(box)->firstLeafChild();
248    if (!leaf && parent())
249        leaf = parent()->nextLeafChild();
250    return leaf;
251}
252
253InlineBox* InlineBox::prevLeafChild() const
254{
255    InlineBox* leaf = 0;
256    for (InlineBox* box = prevOnLine(); box && !leaf; box = box->prevOnLine())
257        leaf = box->isLeaf() ? box : toInlineFlowBox(box)->lastLeafChild();
258    if (!leaf && parent())
259        leaf = parent()->prevLeafChild();
260    return leaf;
261}
262
263InlineBox* InlineBox::nextLeafChildIgnoringLineBreak() const
264{
265    InlineBox* leaf = nextLeafChild();
266    if (leaf && leaf->isLineBreak())
267        return 0;
268    return leaf;
269}
270
271InlineBox* InlineBox::prevLeafChildIgnoringLineBreak() const
272{
273    InlineBox* leaf = prevLeafChild();
274    if (leaf && leaf->isLineBreak())
275        return 0;
276    return leaf;
277}
278
279RenderObject::SelectionState InlineBox::selectionState() const
280{
281    return renderer().selectionState();
282}
283
284bool InlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) const
285{
286    // Non-replaced elements can always accommodate an ellipsis.
287    if (!renderer().isReplaced())
288        return true;
289
290    IntRect boxRect(left(), 0, m_logicalWidth, 10);
291    IntRect ellipsisRect(ltr ? blockEdge - ellipsisWidth : blockEdge, 0, ellipsisWidth, 10);
292    return !(boxRect.intersects(ellipsisRect));
293}
294
295float InlineBox::placeEllipsisBox(bool, float, float, float, float& truncatedWidth, bool&)
296{
297    // Use -1 to mean "we didn't set the position."
298    truncatedWidth += logicalWidth();
299    return -1;
300}
301
302void InlineBox::clearKnownToHaveNoOverflow()
303{
304    m_bitfields.setKnownToHaveNoOverflow(false);
305    if (parent() && parent()->knownToHaveNoOverflow())
306        parent()->clearKnownToHaveNoOverflow();
307}
308
309FloatPoint InlineBox::locationIncludingFlipping()
310{
311    if (!renderer().style()->isFlippedBlocksWritingMode())
312        return FloatPoint(x(), y());
313    RenderBlockFlow& block = root().block();
314    if (block.style()->isHorizontalWritingMode())
315        return FloatPoint(x(), block.height() - height() - y());
316
317    return FloatPoint(block.width() - width() - x(), y());
318}
319
320void InlineBox::flipForWritingMode(FloatRect& rect)
321{
322    if (!renderer().style()->isFlippedBlocksWritingMode())
323        return;
324    root().block().flipForWritingMode(rect);
325}
326
327FloatPoint InlineBox::flipForWritingMode(const FloatPoint& point)
328{
329    if (!renderer().style()->isFlippedBlocksWritingMode())
330        return point;
331    return root().block().flipForWritingMode(point);
332}
333
334void InlineBox::flipForWritingMode(LayoutRect& rect)
335{
336    if (!renderer().style()->isFlippedBlocksWritingMode())
337        return;
338    root().block().flipForWritingMode(rect);
339}
340
341LayoutPoint InlineBox::flipForWritingMode(const LayoutPoint& point)
342{
343    if (!renderer().style()->isFlippedBlocksWritingMode())
344        return point;
345    return root().block().flipForWritingMode(point);
346}
347
348} // namespace blink
349
350#ifndef NDEBUG
351
352void showTree(const blink::InlineBox* b)
353{
354    if (b)
355        b->showTreeForThis();
356}
357
358void showLineTree(const blink::InlineBox* b)
359{
360    if (b)
361        b->showLineTreeForThis();
362}
363
364#endif
365