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
showTreeForThis() const82 void InlineBox::showTreeForThis() const
83 {
84 if (m_renderer)
85 m_renderer->showTreeForThis();
86 }
87 #endif
88
logicalHeight() const89 int InlineBox::logicalHeight() const
90 {
91 #if ENABLE(SVG)
92 if (hasVirtualLogicalHeight())
93 return virtualLogicalHeight();
94 #endif
95
96 if (renderer()->isText())
97 return m_isText ? renderer()->style(m_firstLine)->fontMetrics().height() : 0;
98 if (renderer()->isBox() && parent())
99 return isHorizontal() ? toRenderBox(m_renderer)->height() : toRenderBox(m_renderer)->width();
100
101 ASSERT(isInlineFlowBox());
102 RenderBoxModelObject* flowObject = boxModelObject();
103 const FontMetrics& fontMetrics = renderer()->style(m_firstLine)->fontMetrics();
104 int result = fontMetrics.height();
105 if (parent())
106 result += flowObject->borderAndPaddingLogicalHeight();
107 return result;
108 }
109
caretMinOffset() const110 int InlineBox::caretMinOffset() const
111 {
112 return m_renderer->caretMinOffset();
113 }
114
caretMaxOffset() const115 int InlineBox::caretMaxOffset() const
116 {
117 return m_renderer->caretMaxOffset();
118 }
119
caretMaxRenderedOffset() const120 unsigned InlineBox::caretMaxRenderedOffset() const
121 {
122 return 1;
123 }
124
dirtyLineBoxes()125 void InlineBox::dirtyLineBoxes()
126 {
127 markDirty();
128 for (InlineFlowBox* curr = parent(); curr && !curr->isDirty(); curr = curr->parent())
129 curr->markDirty();
130 }
131
deleteLine(RenderArena * arena)132 void InlineBox::deleteLine(RenderArena* arena)
133 {
134 if (!m_extracted && m_renderer->isBox())
135 toRenderBox(m_renderer)->setInlineBoxWrapper(0);
136 destroy(arena);
137 }
138
extractLine()139 void InlineBox::extractLine()
140 {
141 m_extracted = true;
142 if (m_renderer->isBox())
143 toRenderBox(m_renderer)->setInlineBoxWrapper(0);
144 }
145
attachLine()146 void InlineBox::attachLine()
147 {
148 m_extracted = false;
149 if (m_renderer->isBox())
150 toRenderBox(m_renderer)->setInlineBoxWrapper(this);
151 }
152
adjustPosition(float dx,float dy)153 void InlineBox::adjustPosition(float dx, float dy)
154 {
155 m_x += dx;
156 m_y += dy;
157
158 if (m_renderer->isReplaced())
159 toRenderBox(m_renderer)->move(dx, dy);
160 }
161
paint(PaintInfo & paintInfo,int tx,int ty,int,int)162 void InlineBox::paint(PaintInfo& paintInfo, int tx, int ty, int /* lineTop */, int /*lineBottom*/)
163 {
164 if (!paintInfo.shouldPaintWithinRoot(renderer()) || (paintInfo.phase != PaintPhaseForeground && paintInfo.phase != PaintPhaseSelection))
165 return;
166
167 IntPoint childPoint = IntPoint(tx, ty);
168 if (parent()->renderer()->style()->isFlippedBlocksWritingMode()) // Faster than calling containingBlock().
169 childPoint = renderer()->containingBlock()->flipForWritingMode(toRenderBox(renderer()), childPoint, RenderBox::ParentToChildFlippingAdjustment);
170
171 // Paint all phases of replaced elements atomically, as though the replaced element established its
172 // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
173 // specification.)
174 bool preservePhase = paintInfo.phase == PaintPhaseSelection || paintInfo.phase == PaintPhaseTextClip;
175 PaintInfo info(paintInfo);
176 info.phase = preservePhase ? paintInfo.phase : PaintPhaseBlockBackground;
177 renderer()->paint(info, childPoint.x(), childPoint.y());
178 if (!preservePhase) {
179 info.phase = PaintPhaseChildBlockBackgrounds;
180 renderer()->paint(info, childPoint.x(), childPoint.y());
181 info.phase = PaintPhaseFloat;
182 renderer()->paint(info, childPoint.x(), childPoint.y());
183 info.phase = PaintPhaseForeground;
184 renderer()->paint(info, childPoint.x(), childPoint.y());
185 info.phase = PaintPhaseOutline;
186 renderer()->paint(info, childPoint.x(), childPoint.y());
187 }
188 }
189
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int x,int y,int tx,int ty,int,int)190 bool InlineBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty, int /* lineTop */, int /*lineBottom*/)
191 {
192 // Hit test all phases of replaced elements atomically, as though the replaced element established its
193 // own stacking context. (See Appendix E.2, section 6.4 on inline block/table elements in the CSS2.1
194 // specification.)
195 return renderer()->hitTest(request, result, IntPoint(x, y), tx, ty);
196 }
197
root() const198 const RootInlineBox* InlineBox::root() const
199 {
200 if (m_parent)
201 return m_parent->root();
202 ASSERT(isRootInlineBox());
203 return static_cast<const RootInlineBox*>(this);
204 }
205
root()206 RootInlineBox* InlineBox::root()
207 {
208 if (m_parent)
209 return m_parent->root();
210 ASSERT(isRootInlineBox());
211 return static_cast<RootInlineBox*>(this);
212 }
213
nextOnLineExists() const214 bool InlineBox::nextOnLineExists() const
215 {
216 if (!m_determinedIfNextOnLineExists) {
217 m_determinedIfNextOnLineExists = true;
218
219 if (!parent())
220 m_nextOnLineExists = false;
221 else if (nextOnLine())
222 m_nextOnLineExists = true;
223 else
224 m_nextOnLineExists = parent()->nextOnLineExists();
225 }
226 return m_nextOnLineExists;
227 }
228
prevOnLineExists() const229 bool InlineBox::prevOnLineExists() const
230 {
231 if (!m_determinedIfPrevOnLineExists) {
232 m_determinedIfPrevOnLineExists = true;
233
234 if (!parent())
235 m_prevOnLineExists = false;
236 else if (prevOnLine())
237 m_prevOnLineExists = true;
238 else
239 m_prevOnLineExists = parent()->prevOnLineExists();
240 }
241 return m_prevOnLineExists;
242 }
243
nextLeafChild() const244 InlineBox* InlineBox::nextLeafChild() const
245 {
246 InlineBox* leaf = 0;
247 for (InlineBox* box = nextOnLine(); box && !leaf; box = box->nextOnLine())
248 leaf = box->isLeaf() ? box : static_cast<InlineFlowBox*>(box)->firstLeafChild();
249 if (!leaf && parent())
250 leaf = parent()->nextLeafChild();
251 return leaf;
252 }
253
prevLeafChild() const254 InlineBox* InlineBox::prevLeafChild() const
255 {
256 InlineBox* leaf = 0;
257 for (InlineBox* box = prevOnLine(); box && !leaf; box = box->prevOnLine())
258 leaf = box->isLeaf() ? box : static_cast<InlineFlowBox*>(box)->lastLeafChild();
259 if (!leaf && parent())
260 leaf = parent()->prevLeafChild();
261 return leaf;
262 }
263
selectionState()264 RenderObject::SelectionState InlineBox::selectionState()
265 {
266 return renderer()->selectionState();
267 }
268
canAccommodateEllipsis(bool ltr,int blockEdge,int ellipsisWidth)269 bool InlineBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth)
270 {
271 // Non-replaced elements can always accommodate an ellipsis.
272 if (!m_renderer || !m_renderer->isReplaced())
273 return true;
274
275 IntRect boxRect(m_x, 0, m_logicalWidth, 10);
276 IntRect ellipsisRect(ltr ? blockEdge - ellipsisWidth : blockEdge, 0, ellipsisWidth, 10);
277 return !(boxRect.intersects(ellipsisRect));
278 }
279
placeEllipsisBox(bool,float,float,float,bool &)280 float InlineBox::placeEllipsisBox(bool, float, float, float, bool&)
281 {
282 // Use -1 to mean "we didn't set the position."
283 return -1;
284 }
285
clearKnownToHaveNoOverflow()286 void InlineBox::clearKnownToHaveNoOverflow()
287 {
288 m_knownToHaveNoOverflow = false;
289 if (parent() && parent()->knownToHaveNoOverflow())
290 parent()->clearKnownToHaveNoOverflow();
291 }
292
locationIncludingFlipping()293 FloatPoint InlineBox::locationIncludingFlipping()
294 {
295 if (!renderer()->style()->isFlippedBlocksWritingMode())
296 return FloatPoint(x(), y());
297 RenderBlock* block = root()->block();
298 if (block->style()->isHorizontalWritingMode())
299 return FloatPoint(x(), block->height() - height() - y());
300 else
301 return FloatPoint(block->width() - width() - x(), y());
302 }
303
flipForWritingMode(FloatRect & rect)304 void InlineBox::flipForWritingMode(FloatRect& rect)
305 {
306 if (!renderer()->style()->isFlippedBlocksWritingMode())
307 return;
308 root()->block()->flipForWritingMode(rect);
309 }
310
flipForWritingMode(const FloatPoint & point)311 FloatPoint InlineBox::flipForWritingMode(const FloatPoint& point)
312 {
313 if (!renderer()->style()->isFlippedBlocksWritingMode())
314 return point;
315 return root()->block()->flipForWritingMode(point);
316 }
317
flipForWritingMode(IntRect & rect)318 void InlineBox::flipForWritingMode(IntRect& rect)
319 {
320 if (!renderer()->style()->isFlippedBlocksWritingMode())
321 return;
322 root()->block()->flipForWritingMode(rect);
323 }
324
flipForWritingMode(const IntPoint & point)325 IntPoint InlineBox::flipForWritingMode(const IntPoint& point)
326 {
327 if (!renderer()->style()->isFlippedBlocksWritingMode())
328 return point;
329 return root()->block()->flipForWritingMode(point);
330 }
331
332 } // namespace WebCore
333
334 #ifndef NDEBUG
335
showTree(const WebCore::InlineBox * b)336 void showTree(const WebCore::InlineBox* b)
337 {
338 if (b)
339 b->showTreeForThis();
340 }
341
342 #endif
343