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