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