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