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