1 /*
2 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 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 "InlineFlowBox.h"
22
23 #include "CachedImage.h"
24 #include "Document.h"
25 #include "EllipsisBox.h"
26 #include "GraphicsContext.h"
27 #include "InlineTextBox.h"
28 #include "HitTestResult.h"
29 #include "RootInlineBox.h"
30 #include "RenderBlock.h"
31 #include "RenderInline.h"
32 #include "RenderLayer.h"
33 #include "RenderListMarker.h"
34 #include "RenderTableCell.h"
35 #include "RootInlineBox.h"
36 #include "Text.h"
37
38 #include <math.h>
39
40 using namespace std;
41
42 namespace WebCore {
43
44 #ifndef NDEBUG
45
~InlineFlowBox()46 InlineFlowBox::~InlineFlowBox()
47 {
48 if (!m_hasBadChildList)
49 for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
50 child->setHasBadParent();
51 }
52
53 #endif
54
getFlowSpacingWidth()55 int InlineFlowBox::getFlowSpacingWidth()
56 {
57 int totWidth = marginBorderPaddingLeft() + marginBorderPaddingRight();
58 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
59 if (curr->isInlineFlowBox())
60 totWidth += static_cast<InlineFlowBox*>(curr)->getFlowSpacingWidth();
61 }
62 return totWidth;
63 }
64
addToLine(InlineBox * child)65 void InlineFlowBox::addToLine(InlineBox* child)
66 {
67 ASSERT(!child->parent());
68 ASSERT(!child->nextOnLine());
69 ASSERT(!child->prevOnLine());
70 checkConsistency();
71
72 child->setParent(this);
73 if (!m_firstChild) {
74 m_firstChild = child;
75 m_lastChild = child;
76 } else {
77 m_lastChild->setNextOnLine(child);
78 child->setPrevOnLine(m_lastChild);
79 m_lastChild = child;
80 }
81 child->setFirstLineStyleBit(m_firstLine);
82 if (child->isText())
83 m_hasTextChildren = true;
84
85 checkConsistency();
86 }
87
removeChild(InlineBox * child)88 void InlineFlowBox::removeChild(InlineBox* child)
89 {
90 checkConsistency();
91
92 if (!m_dirty)
93 dirtyLineBoxes();
94
95 root()->childRemoved(child);
96
97 if (child == m_firstChild)
98 m_firstChild = child->nextOnLine();
99 if (child == m_lastChild)
100 m_lastChild = child->prevOnLine();
101 if (child->nextOnLine())
102 child->nextOnLine()->setPrevOnLine(child->prevOnLine());
103 if (child->prevOnLine())
104 child->prevOnLine()->setNextOnLine(child->nextOnLine());
105
106 child->setParent(0);
107
108 checkConsistency();
109 }
110
deleteLine(RenderArena * arena)111 void InlineFlowBox::deleteLine(RenderArena* arena)
112 {
113 InlineBox* child = firstChild();
114 InlineBox* next = 0;
115 while (child) {
116 ASSERT(this == child->parent());
117 next = child->nextOnLine();
118 #ifndef NDEBUG
119 child->setParent(0);
120 #endif
121 child->deleteLine(arena);
122 child = next;
123 }
124 #ifndef NDEBUG
125 m_firstChild = 0;
126 m_lastChild = 0;
127 #endif
128
129 removeLineBoxFromRenderObject();
130 destroy(arena);
131 }
132
removeLineBoxFromRenderObject()133 void InlineFlowBox::removeLineBoxFromRenderObject()
134 {
135 toRenderInline(renderer())->lineBoxes()->removeLineBox(this);
136 }
137
extractLine()138 void InlineFlowBox::extractLine()
139 {
140 if (!m_extracted)
141 extractLineBoxFromRenderObject();
142 for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
143 child->extractLine();
144 }
145
extractLineBoxFromRenderObject()146 void InlineFlowBox::extractLineBoxFromRenderObject()
147 {
148 toRenderInline(renderer())->lineBoxes()->extractLineBox(this);
149 }
150
attachLine()151 void InlineFlowBox::attachLine()
152 {
153 if (m_extracted)
154 attachLineBoxToRenderObject();
155 for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
156 child->attachLine();
157 }
158
attachLineBoxToRenderObject()159 void InlineFlowBox::attachLineBoxToRenderObject()
160 {
161 toRenderInline(renderer())->lineBoxes()->attachLineBox(this);
162 }
163
adjustPosition(int dx,int dy)164 void InlineFlowBox::adjustPosition(int dx, int dy)
165 {
166 InlineRunBox::adjustPosition(dx, dy);
167 for (InlineBox* child = firstChild(); child; child = child->nextOnLine())
168 child->adjustPosition(dx, dy);
169 if (m_overflow)
170 m_overflow->move(dx, dy);
171 }
172
rendererLineBoxes() const173 RenderLineBoxList* InlineFlowBox::rendererLineBoxes() const
174 {
175 return toRenderInline(renderer())->lineBoxes();
176 }
177
onEndChain(RenderObject * endObject)178 bool InlineFlowBox::onEndChain(RenderObject* endObject)
179 {
180 if (!endObject)
181 return false;
182
183 if (endObject == renderer())
184 return true;
185
186 RenderObject* curr = endObject;
187 RenderObject* parent = curr->parent();
188 while (parent && !parent->isRenderBlock()) {
189 if (parent->lastChild() != curr || parent == renderer())
190 return false;
191
192 curr = parent;
193 parent = curr->parent();
194 }
195
196 return true;
197 }
198
determineSpacingForFlowBoxes(bool lastLine,RenderObject * endObject)199 void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, RenderObject* endObject)
200 {
201 // All boxes start off open. They will not apply any margins/border/padding on
202 // any side.
203 bool includeLeftEdge = false;
204 bool includeRightEdge = false;
205
206 // The root inline box never has borders/margins/padding.
207 if (parent()) {
208 bool ltr = renderer()->style()->direction() == LTR;
209
210 // Check to see if all initial lines are unconstructed. If so, then
211 // we know the inline began on this line (unless we are a continuation).
212 RenderLineBoxList* lineBoxList = rendererLineBoxes();
213 if (!lineBoxList->firstLineBox()->isConstructed() && !renderer()->isInlineContinuation()) {
214 if (ltr && lineBoxList->firstLineBox() == this)
215 includeLeftEdge = true;
216 else if (!ltr && lineBoxList->lastLineBox() == this)
217 includeRightEdge = true;
218 }
219
220 // In order to determine if the inline ends on this line, we check three things:
221 // (1) If we are the last line and we don't have a continuation(), then we can
222 // close up.
223 // (2) If the last line box for the flow has an object following it on the line (ltr,
224 // reverse for rtl), then the inline has closed.
225 // (3) The line may end on the inline. If we are the last child (climbing up
226 // the end object's chain), then we just closed as well.
227 if (!lineBoxList->lastLineBox()->isConstructed()) {
228 RenderInline* inlineFlow = toRenderInline(renderer());
229 if (ltr) {
230 if (!nextLineBox() &&
231 ((lastLine && !inlineFlow->continuation()) || nextOnLineExists() || onEndChain(endObject)))
232 includeRightEdge = true;
233 } else {
234 if ((!prevLineBox() || prevLineBox()->isConstructed()) &&
235 ((lastLine && !inlineFlow->continuation()) || prevOnLineExists() || onEndChain(endObject)))
236 includeLeftEdge = true;
237 }
238 }
239 }
240
241 setEdges(includeLeftEdge, includeRightEdge);
242
243 // Recur into our children.
244 for (InlineBox* currChild = firstChild(); currChild; currChild = currChild->nextOnLine()) {
245 if (currChild->isInlineFlowBox()) {
246 InlineFlowBox* currFlow = static_cast<InlineFlowBox*>(currChild);
247 currFlow->determineSpacingForFlowBoxes(lastLine, endObject);
248 }
249 }
250 }
251
placeBoxesHorizontally(int xPos,bool & needsWordSpacing)252 int InlineFlowBox::placeBoxesHorizontally(int xPos, bool& needsWordSpacing)
253 {
254 // Set our x position.
255 setX(xPos);
256
257 int leftLayoutOverflow = xPos;
258 int rightLayoutOverflow = xPos;
259 int leftVisualOverflow = xPos;
260 int rightVisualOverflow = xPos;
261
262 int boxShadowLeft;
263 int boxShadowRight;
264 renderer()->style(m_firstLine)->getBoxShadowHorizontalExtent(boxShadowLeft, boxShadowRight);
265
266 leftVisualOverflow = min(xPos + boxShadowLeft, leftVisualOverflow);
267
268 int startX = xPos;
269 xPos += borderLeft() + paddingLeft();
270
271 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
272 if (curr->renderer()->isText()) {
273 InlineTextBox* text = static_cast<InlineTextBox*>(curr);
274 RenderText* rt = toRenderText(text->renderer());
275 if (rt->textLength()) {
276 if (needsWordSpacing && isSpaceOrNewline(rt->characters()[text->start()]))
277 xPos += rt->style(m_firstLine)->font().wordSpacing();
278 needsWordSpacing = !isSpaceOrNewline(rt->characters()[text->end()]);
279 }
280 text->setX(xPos);
281
282 int strokeOverflow = static_cast<int>(ceilf(rt->style()->textStrokeWidth() / 2.0f));
283
284 // If letter-spacing is negative, we should factor that into right layout overflow. (Even in RTL, letter-spacing is
285 // applied to the right, so this is not an issue with left overflow.
286 int letterSpacing = min(0, (int)rt->style(m_firstLine)->font().letterSpacing());
287 rightLayoutOverflow = max(xPos + text->width() - letterSpacing, rightLayoutOverflow);
288
289 int leftGlyphOverflow = -strokeOverflow;
290 int rightGlyphOverflow = strokeOverflow - letterSpacing;
291
292 int childOverflowLeft = leftGlyphOverflow;
293 int childOverflowRight = rightGlyphOverflow;
294 for (ShadowData* shadow = rt->style()->textShadow(); shadow; shadow = shadow->next) {
295 childOverflowLeft = min(childOverflowLeft, shadow->x - shadow->blur + leftGlyphOverflow);
296 childOverflowRight = max(childOverflowRight, shadow->x + shadow->blur + rightGlyphOverflow);
297 }
298
299 leftVisualOverflow = min(xPos + childOverflowLeft, leftVisualOverflow);
300 rightVisualOverflow = max(xPos + text->width() + childOverflowRight, rightVisualOverflow);
301
302 xPos += text->width();
303 } else {
304 if (curr->renderer()->isPositioned()) {
305 if (curr->renderer()->parent()->style()->direction() == LTR)
306 curr->setX(xPos);
307 else
308 // Our offset that we cache needs to be from the edge of the right border box and
309 // not the left border box. We have to subtract |x| from the width of the block
310 // (which can be obtained from the root line box).
311 curr->setX(root()->block()->width() - xPos);
312 continue; // The positioned object has no effect on the width.
313 }
314 if (curr->renderer()->isRenderInline()) {
315 InlineFlowBox* flow = static_cast<InlineFlowBox*>(curr);
316 xPos += flow->marginLeft();
317 xPos = flow->placeBoxesHorizontally(xPos, needsWordSpacing);
318 xPos += flow->marginRight();
319 leftLayoutOverflow = min(leftLayoutOverflow, flow->leftLayoutOverflow());
320 rightLayoutOverflow = max(rightLayoutOverflow, flow->rightLayoutOverflow());
321 leftVisualOverflow = min(leftVisualOverflow, flow->leftVisualOverflow());
322 rightVisualOverflow = max(rightVisualOverflow, flow->rightVisualOverflow());
323 } else if (!curr->renderer()->isListMarker() || toRenderListMarker(curr->renderer())->isInside()) {
324 xPos += curr->boxModelObject()->marginLeft();
325 curr->setX(xPos);
326
327 RenderBox* box = toRenderBox(curr->renderer());
328 int childLeftOverflow = box->hasOverflowClip() ? 0 : box->leftLayoutOverflow();
329 int childRightOverflow = box->hasOverflowClip() ? curr->width() : box->rightLayoutOverflow();
330
331 leftLayoutOverflow = min(xPos + childLeftOverflow, leftLayoutOverflow);
332 rightLayoutOverflow = max(xPos + childRightOverflow, rightLayoutOverflow);
333
334 leftVisualOverflow = min(xPos + box->leftVisualOverflow(), leftVisualOverflow);
335 rightVisualOverflow = max(xPos + box->rightVisualOverflow(), rightVisualOverflow);
336
337 xPos += curr->width() + curr->boxModelObject()->marginRight();
338 }
339 }
340 }
341
342 xPos += borderRight() + paddingRight();
343 setWidth(xPos - startX);
344 rightVisualOverflow = max(x() + width() + boxShadowRight, rightVisualOverflow);
345 rightLayoutOverflow = max(x() + width(), rightLayoutOverflow);
346
347 setHorizontalOverflowPositions(leftLayoutOverflow, rightLayoutOverflow, leftVisualOverflow, rightVisualOverflow);
348 return xPos;
349 }
350
adjustMaxAscentAndDescent(int & maxAscent,int & maxDescent,int maxPositionTop,int maxPositionBottom)351 void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent,
352 int maxPositionTop, int maxPositionBottom)
353 {
354 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
355 // The computed lineheight needs to be extended for the
356 // positioned elements
357 if (curr->renderer()->isPositioned())
358 continue; // Positioned placeholders don't affect calculations.
359 if (curr->y() == PositionTop || curr->y() == PositionBottom) {
360 int lineHeight = curr->lineHeight(false);
361 if (curr->y() == PositionTop) {
362 if (maxAscent + maxDescent < lineHeight)
363 maxDescent = lineHeight - maxAscent;
364 }
365 else {
366 if (maxAscent + maxDescent < lineHeight)
367 maxAscent = lineHeight - maxDescent;
368 }
369
370 if (maxAscent + maxDescent >= max(maxPositionTop, maxPositionBottom))
371 break;
372 }
373
374 if (curr->isInlineFlowBox())
375 static_cast<InlineFlowBox*>(curr)->adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom);
376 }
377 }
378
verticalPositionForBox(InlineBox * curr,bool firstLine)379 static int verticalPositionForBox(InlineBox* curr, bool firstLine)
380 {
381 if (curr->renderer()->isText())
382 return curr->parent()->y();
383 if (curr->renderer()->isBox())
384 return toRenderBox(curr->renderer())->verticalPosition(firstLine);
385 return toRenderInline(curr->renderer())->verticalPositionFromCache(firstLine);
386 }
387
computeLogicalBoxHeights(int & maxPositionTop,int & maxPositionBottom,int & maxAscent,int & maxDescent,bool strictMode)388 void InlineFlowBox::computeLogicalBoxHeights(int& maxPositionTop, int& maxPositionBottom,
389 int& maxAscent, int& maxDescent, bool strictMode)
390 {
391 if (isRootInlineBox()) {
392 // Examine our root box.
393 int height = lineHeight(true);
394 int baseline = baselinePosition(true);
395 if (hasTextChildren() || strictMode) {
396 int ascent = baseline;
397 int descent = height - ascent;
398 if (maxAscent < ascent)
399 maxAscent = ascent;
400 if (maxDescent < descent)
401 maxDescent = descent;
402 }
403 }
404
405 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
406 if (curr->renderer()->isPositioned())
407 continue; // Positioned placeholders don't affect calculations.
408
409 bool isInlineFlow = curr->isInlineFlowBox();
410
411 int lineHeight;
412 int baseline;
413 Vector<const SimpleFontData*> usedFonts;
414 if (curr->isInlineTextBox())
415 static_cast<InlineTextBox*>(curr)->takeFallbackFonts(usedFonts);
416
417 if (!usedFonts.isEmpty()) {
418 usedFonts.append(curr->renderer()->style(m_firstLine)->font().primaryFont());
419 Length parentLineHeight = curr->renderer()->parent()->style()->lineHeight();
420 if (parentLineHeight.isNegative()) {
421 int baselineToBottom = 0;
422 baseline = 0;
423 for (size_t i = 0; i < usedFonts.size(); ++i) {
424 int halfLeading = (usedFonts[i]->lineSpacing() - usedFonts[i]->ascent() - usedFonts[i]->descent()) / 2;
425 baseline = max(baseline, halfLeading + usedFonts[i]->ascent());
426 baselineToBottom = max(baselineToBottom, usedFonts[i]->lineSpacing() - usedFonts[i]->ascent() - usedFonts[i]->descent() - halfLeading);
427 }
428 lineHeight = baseline + baselineToBottom;
429 } else if (parentLineHeight.isPercent()) {
430 lineHeight = parentLineHeight.calcMinValue(curr->renderer()->style()->fontSize());
431 baseline = 0;
432 for (size_t i = 0; i < usedFonts.size(); ++i) {
433 int halfLeading = (lineHeight - usedFonts[i]->ascent() - usedFonts[i]->descent()) / 2;
434 baseline = max(baseline, halfLeading + usedFonts[i]->ascent());
435 }
436 } else {
437 lineHeight = parentLineHeight.value();
438 baseline = 0;
439 for (size_t i = 0; i < usedFonts.size(); ++i) {
440 int halfLeading = (lineHeight - usedFonts[i]->ascent() - usedFonts[i]->descent()) / 2;
441 baseline = max(baseline, halfLeading + usedFonts[i]->ascent());
442 }
443 }
444 } else {
445 lineHeight = curr->lineHeight(false);
446 baseline = curr->baselinePosition(false);
447 }
448
449 curr->setY(verticalPositionForBox(curr, m_firstLine));
450 if (curr->y() == PositionTop) {
451 if (maxPositionTop < lineHeight)
452 maxPositionTop = lineHeight;
453 } else if (curr->y() == PositionBottom) {
454 if (maxPositionBottom < lineHeight)
455 maxPositionBottom = lineHeight;
456 } else if ((!isInlineFlow || static_cast<InlineFlowBox*>(curr)->hasTextChildren()) || curr->boxModelObject()->hasHorizontalBordersOrPadding() || strictMode) {
457 int ascent = baseline - curr->y();
458 int descent = lineHeight - ascent;
459 if (maxAscent < ascent)
460 maxAscent = ascent;
461 if (maxDescent < descent)
462 maxDescent = descent;
463 }
464
465 if (curr->isInlineFlowBox())
466 static_cast<InlineFlowBox*>(curr)->computeLogicalBoxHeights(maxPositionTop, maxPositionBottom, maxAscent, maxDescent, strictMode);
467 }
468 }
469
placeBoxesVertically(int yPos,int maxHeight,int maxAscent,bool strictMode,int & selectionTop,int & selectionBottom)470 void InlineFlowBox::placeBoxesVertically(int yPos, int maxHeight, int maxAscent, bool strictMode, int& selectionTop, int& selectionBottom)
471 {
472 if (isRootInlineBox())
473 setY(yPos + maxAscent - baselinePosition(true)); // Place our root box.
474
475 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
476 if (curr->renderer()->isPositioned())
477 continue; // Positioned placeholders don't affect calculations.
478
479 // Adjust boxes to use their real box y/height and not the logical height (as dictated by
480 // line-height).
481 bool isInlineFlow = curr->isInlineFlowBox();
482 if (isInlineFlow)
483 static_cast<InlineFlowBox*>(curr)->placeBoxesVertically(yPos, maxHeight, maxAscent, strictMode, selectionTop, selectionBottom);
484
485 bool childAffectsTopBottomPos = true;
486 if (curr->y() == PositionTop)
487 curr->setY(yPos);
488 else if (curr->y() == PositionBottom)
489 curr->setY(yPos + maxHeight - curr->lineHeight(false));
490 else {
491 if ((isInlineFlow && !static_cast<InlineFlowBox*>(curr)->hasTextChildren()) && !curr->boxModelObject()->hasHorizontalBordersOrPadding() && !strictMode)
492 childAffectsTopBottomPos = false;
493 int posAdjust = maxAscent - curr->baselinePosition(false);
494 curr->setY(curr->y() + yPos + posAdjust);
495 }
496
497 int newY = curr->y();
498 if (curr->isText() || curr->isInlineFlowBox()) {
499 const Font& font = curr->renderer()->style(m_firstLine)->font();
500 newY += curr->baselinePosition(false) - font.ascent();
501 if (curr->isInlineFlowBox())
502 newY -= curr->boxModelObject()->borderTop() + curr->boxModelObject()->paddingTop();
503 } else if (!curr->renderer()->isBR()) {
504 RenderBox* box = toRenderBox(curr->renderer());
505 newY += box->marginTop();
506 }
507
508 curr->setY(newY);
509
510 if (childAffectsTopBottomPos) {
511 int boxHeight = curr->height();
512 selectionTop = min(selectionTop, newY);
513 selectionBottom = max(selectionBottom, newY + boxHeight);
514 }
515 }
516
517 if (isRootInlineBox()) {
518 const Font& font = renderer()->style(m_firstLine)->font();
519 setY(y() + baselinePosition(true) - font.ascent());
520 if (hasTextChildren() || strictMode) {
521 selectionTop = min(selectionTop, y());
522 selectionBottom = max(selectionBottom, y() + height());
523 }
524 }
525 }
526
computeVerticalOverflow(int lineTop,int lineBottom,bool strictMode)527 void InlineFlowBox::computeVerticalOverflow(int lineTop, int lineBottom, bool strictMode)
528 {
529 int boxHeight = height();
530
531 // Any spillage outside of the line top and bottom is not considered overflow. We just ignore this, since it only happens
532 // from the "your ascent/descent don't affect the line" quirk.
533 int topOverflow = max(y(), lineTop);
534 int bottomOverflow = min(y() + boxHeight, lineBottom);
535
536 int topLayoutOverflow = topOverflow;
537 int bottomLayoutOverflow = bottomOverflow;
538
539 int topVisualOverflow = topOverflow;
540 int bottomVisualOverflow = bottomOverflow;
541
542 // box-shadow on root line boxes is applying to the block and not to the lines.
543 if (parent()) {
544 int boxShadowTop;
545 int boxShadowBottom;
546 renderer()->style(m_firstLine)->getBoxShadowVerticalExtent(boxShadowTop, boxShadowBottom);
547
548 topVisualOverflow = min(y() + boxShadowTop, topVisualOverflow);
549 bottomVisualOverflow = max(y() + boxHeight + boxShadowBottom, bottomVisualOverflow);
550 }
551
552 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
553 if (curr->renderer()->isPositioned())
554 continue; // Positioned placeholders don't affect calculations.
555
556 if (curr->renderer()->isText()) {
557 InlineTextBox* text = static_cast<InlineTextBox*>(curr);
558 RenderText* rt = toRenderText(text->renderer());
559 if (rt->isBR())
560 continue;
561
562 int strokeOverflow = static_cast<int>(ceilf(rt->style()->textStrokeWidth() / 2.0f));
563
564 int topGlyphOverflow = -strokeOverflow;
565 int bottomGlyphOverflow = strokeOverflow;
566
567 int childOverflowTop = topGlyphOverflow;
568 int childOverflowBottom = bottomGlyphOverflow;
569 for (ShadowData* shadow = rt->style()->textShadow(); shadow; shadow = shadow->next) {
570 childOverflowTop = min(childOverflowTop, shadow->y - shadow->blur + topGlyphOverflow);
571 childOverflowBottom = max(childOverflowBottom, shadow->y + shadow->blur + bottomGlyphOverflow);
572 }
573
574 topVisualOverflow = min(curr->y() + childOverflowTop, topVisualOverflow);
575 bottomVisualOverflow = max(curr->y() + text->height() + childOverflowBottom, bottomVisualOverflow);
576 } else if (curr->renderer()->isRenderInline()) {
577 InlineFlowBox* flow = static_cast<InlineFlowBox*>(curr);
578 flow->computeVerticalOverflow(lineTop, lineBottom, strictMode);
579 topLayoutOverflow = min(topLayoutOverflow, flow->topLayoutOverflow());
580 bottomLayoutOverflow = max(bottomLayoutOverflow, flow->bottomLayoutOverflow());
581 topVisualOverflow = min(topVisualOverflow, flow->topVisualOverflow());
582 bottomVisualOverflow = max(bottomVisualOverflow, flow->bottomVisualOverflow());
583 } else if (!curr->boxModelObject()->hasSelfPaintingLayer()){
584 // Only include overflow from replaced inlines if they do not paint themselves.
585 RenderBox* box = toRenderBox(curr->renderer());
586 int boxY = curr->y();
587 int childTopOverflow = box->hasOverflowClip() ? 0 : box->topLayoutOverflow();
588 int childBottomOverflow = box->hasOverflowClip() ? curr->height() : box->bottomLayoutOverflow();
589 topLayoutOverflow = min(boxY + childTopOverflow, topLayoutOverflow);
590 bottomLayoutOverflow = max(boxY + childBottomOverflow, bottomLayoutOverflow);
591 topVisualOverflow = min(boxY + box->topVisualOverflow(), topVisualOverflow);
592 bottomVisualOverflow = max(boxY + box->bottomVisualOverflow(), bottomVisualOverflow);
593 }
594 }
595
596 setVerticalOverflowPositions(topLayoutOverflow, bottomLayoutOverflow, topVisualOverflow, bottomVisualOverflow, boxHeight);
597 }
598
nodeAtPoint(const HitTestRequest & request,HitTestResult & result,int x,int y,int tx,int ty)599 bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, int x, int y, int tx, int ty)
600 {
601 IntRect overflowRect(visibleOverflowRect());
602 overflowRect.move(tx, ty);
603 if (!overflowRect.contains(x, y))
604 return false;
605
606 // Check children first.
607 for (InlineBox* curr = lastChild(); curr; curr = curr->prevOnLine()) {
608 if ((curr->renderer()->isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) && curr->nodeAtPoint(request, result, x, y, tx, ty)) {
609 renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty));
610 return true;
611 }
612 }
613
614 // Now check ourselves.
615 IntRect rect(tx + m_x, ty + m_y, m_width, height());
616 if (visibleToHitTesting() && rect.contains(x, y)) {
617 renderer()->updateHitTestResult(result, IntPoint(x - tx, y - ty)); // Don't add in m_x or m_y here, we want coords in the containing block's space.
618 return true;
619 }
620
621 return false;
622 }
623
paint(RenderObject::PaintInfo & paintInfo,int tx,int ty)624 void InlineFlowBox::paint(RenderObject::PaintInfo& paintInfo, int tx, int ty)
625 {
626 IntRect overflowRect(visibleOverflowRect());
627 overflowRect.inflate(renderer()->maximalOutlineSize(paintInfo.phase));
628 overflowRect.move(tx, ty);
629
630 if (!paintInfo.rect.intersects(overflowRect))
631 return;
632
633 if (paintInfo.phase != PaintPhaseChildOutlines) {
634 if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) {
635 // Add ourselves to the paint info struct's list of inlines that need to paint their
636 // outlines.
637 if (renderer()->style()->visibility() == VISIBLE && renderer()->hasOutline() && !isRootInlineBox()) {
638 RenderInline* inlineFlow = toRenderInline(renderer());
639
640 RenderBlock* cb = 0;
641 bool containingBlockPaintsContinuationOutline = inlineFlow->continuation() || inlineFlow->isInlineContinuation();
642 if (containingBlockPaintsContinuationOutline) {
643 cb = renderer()->containingBlock()->containingBlock();
644
645 for (RenderBoxModelObject* box = boxModelObject(); box != cb; box = box->parent()->enclosingBoxModelObject()) {
646 if (box->hasSelfPaintingLayer()) {
647 containingBlockPaintsContinuationOutline = false;
648 break;
649 }
650 }
651 }
652
653 if (containingBlockPaintsContinuationOutline) {
654 // Add ourselves to the containing block of the entire continuation so that it can
655 // paint us atomically.
656 cb->addContinuationWithOutline(toRenderInline(renderer()->node()->renderer()));
657 } else if (!inlineFlow->isInlineContinuation())
658 paintInfo.outlineObjects->add(inlineFlow);
659 }
660 } else if (paintInfo.phase == PaintPhaseMask) {
661 paintMask(paintInfo, tx, ty);
662 return;
663 } else {
664 // 1. Paint our background, border and box-shadow.
665 paintBoxDecorations(paintInfo, tx, ty);
666
667 // 2. Paint our underline and overline.
668 paintTextDecorations(paintInfo, tx, ty, false);
669 }
670 }
671
672 if (paintInfo.phase == PaintPhaseMask)
673 return;
674
675 PaintPhase paintPhase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase;
676 RenderObject::PaintInfo childInfo(paintInfo);
677 childInfo.phase = paintPhase;
678 childInfo.paintingRoot = renderer()->paintingRootForChildren(paintInfo);
679
680 // 3. Paint our children.
681 if (paintPhase != PaintPhaseSelfOutline) {
682 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) {
683 if (curr->renderer()->isText() || !curr->boxModelObject()->hasSelfPaintingLayer())
684 curr->paint(childInfo, tx, ty);
685 }
686 }
687
688 // 4. Paint our strike-through
689 if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseSelection)
690 paintTextDecorations(paintInfo, tx, ty, true);
691 }
692
paintFillLayers(const RenderObject::PaintInfo & paintInfo,const Color & c,const FillLayer * fillLayer,int _tx,int _ty,int w,int h,CompositeOperator op)693 void InlineFlowBox::paintFillLayers(const RenderObject::PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int _tx, int _ty, int w, int h, CompositeOperator op)
694 {
695 if (!fillLayer)
696 return;
697 paintFillLayers(paintInfo, c, fillLayer->next(), _tx, _ty, w, h, op);
698 paintFillLayer(paintInfo, c, fillLayer, _tx, _ty, w, h, op);
699 }
700
paintFillLayer(const RenderObject::PaintInfo & paintInfo,const Color & c,const FillLayer * fillLayer,int tx,int ty,int w,int h,CompositeOperator op)701 void InlineFlowBox::paintFillLayer(const RenderObject::PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, int tx, int ty, int w, int h, CompositeOperator op)
702 {
703 StyleImage* img = fillLayer->image();
704 bool hasFillImage = img && img->canRender(renderer()->style()->effectiveZoom());
705 if ((!hasFillImage && !renderer()->style()->hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent())
706 boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, tx, ty, w, h, this, op);
707 else {
708 // We have a fill image that spans multiple lines.
709 // We need to adjust _tx and _ty by the width of all previous lines.
710 // Think of background painting on inlines as though you had one long line, a single continuous
711 // strip. Even though that strip has been broken up across multiple lines, you still paint it
712 // as though you had one single line. This means each line has to pick up the background where
713 // the previous line left off.
714 // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right,
715 // but it isn't even clear how this should work at all.
716 int xOffsetOnLine = 0;
717 for (InlineRunBox* curr = prevLineBox(); curr; curr = curr->prevLineBox())
718 xOffsetOnLine += curr->width();
719 int startX = tx - xOffsetOnLine;
720 int totalWidth = xOffsetOnLine;
721 for (InlineRunBox* curr = this; curr; curr = curr->nextLineBox())
722 totalWidth += curr->width();
723 paintInfo.context->save();
724 paintInfo.context->clip(IntRect(tx, ty, width(), height()));
725 boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, startX, ty, totalWidth, h, this, op);
726 paintInfo.context->restore();
727 }
728 }
729
paintBoxShadow(GraphicsContext * context,RenderStyle * s,ShadowStyle shadowStyle,int tx,int ty,int w,int h)730 void InlineFlowBox::paintBoxShadow(GraphicsContext* context, RenderStyle* s, ShadowStyle shadowStyle, int tx, int ty, int w, int h)
731 {
732 if ((!prevLineBox() && !nextLineBox()) || !parent())
733 boxModelObject()->paintBoxShadow(context, tx, ty, w, h, s, shadowStyle);
734 else {
735 // FIXME: We can do better here in the multi-line case. We want to push a clip so that the shadow doesn't
736 // protrude incorrectly at the edges, and we want to possibly include shadows cast from the previous/following lines
737 boxModelObject()->paintBoxShadow(context, tx, ty, w, h, s, shadowStyle, includeLeftEdge(), includeRightEdge());
738 }
739 }
740
paintBoxDecorations(RenderObject::PaintInfo & paintInfo,int tx,int ty)741 void InlineFlowBox::paintBoxDecorations(RenderObject::PaintInfo& paintInfo, int tx, int ty)
742 {
743 if (!renderer()->shouldPaintWithinRoot(paintInfo) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground)
744 return;
745
746 int x = m_x;
747 int y = m_y;
748 int w = width();
749 int h = height();
750
751 // Constrain our background/border painting to the line top and bottom if necessary.
752 bool strictMode = renderer()->document()->inStrictMode();
753 if (!hasTextChildren() && !strictMode) {
754 RootInlineBox* rootBox = root();
755 int bottom = min(rootBox->lineBottom(), y + h);
756 y = max(rootBox->lineTop(), y);
757 h = bottom - y;
758 }
759
760 // Move x/y to our coordinates.
761 tx += x;
762 ty += y;
763
764 GraphicsContext* context = paintInfo.context;
765
766 // You can use p::first-line to specify a background. If so, the root line boxes for
767 // a line may actually have to paint a background.
768 RenderStyle* styleToUse = renderer()->style(m_firstLine);
769 if ((!parent() && m_firstLine && styleToUse != renderer()->style()) || (parent() && renderer()->hasBoxDecorations())) {
770 // Shadow comes first and is behind the background and border.
771 if (styleToUse->boxShadow())
772 paintBoxShadow(context, styleToUse, Normal, tx, ty, w, h);
773
774 Color c = styleToUse->backgroundColor();
775 paintFillLayers(paintInfo, c, styleToUse->backgroundLayers(), tx, ty, w, h);
776
777 if (styleToUse->boxShadow())
778 paintBoxShadow(context, styleToUse, Inset, tx, ty, w, h);
779
780 // :first-line cannot be used to put borders on a line. Always paint borders with our
781 // non-first-line style.
782 if (parent() && renderer()->style()->hasBorder()) {
783 StyleImage* borderImage = renderer()->style()->borderImage().image();
784 bool hasBorderImage = borderImage && borderImage->canRender(styleToUse->effectiveZoom());
785 if (hasBorderImage && !borderImage->isLoaded())
786 return; // Don't paint anything while we wait for the image to load.
787
788 // The simple case is where we either have no border image or we are the only box for this object. In those
789 // cases only a single call to draw is required.
790 if (!hasBorderImage || (!prevLineBox() && !nextLineBox()))
791 boxModelObject()->paintBorder(context, tx, ty, w, h, renderer()->style(), includeLeftEdge(), includeRightEdge());
792 else {
793 // We have a border image that spans multiple lines.
794 // We need to adjust _tx and _ty by the width of all previous lines.
795 // Think of border image painting on inlines as though you had one long line, a single continuous
796 // strip. Even though that strip has been broken up across multiple lines, you still paint it
797 // as though you had one single line. This means each line has to pick up the image where
798 // the previous line left off.
799 // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right,
800 // but it isn't even clear how this should work at all.
801 int xOffsetOnLine = 0;
802 for (InlineRunBox* curr = prevLineBox(); curr; curr = curr->prevLineBox())
803 xOffsetOnLine += curr->width();
804 int startX = tx - xOffsetOnLine;
805 int totalWidth = xOffsetOnLine;
806 for (InlineRunBox* curr = this; curr; curr = curr->nextLineBox())
807 totalWidth += curr->width();
808 context->save();
809 context->clip(IntRect(tx, ty, w, h));
810 boxModelObject()->paintBorder(context, startX, ty, totalWidth, h, renderer()->style());
811 context->restore();
812 }
813 }
814 }
815 }
816
paintMask(RenderObject::PaintInfo & paintInfo,int tx,int ty)817 void InlineFlowBox::paintMask(RenderObject::PaintInfo& paintInfo, int tx, int ty)
818 {
819 if (!renderer()->shouldPaintWithinRoot(paintInfo) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask)
820 return;
821
822 int x = m_x;
823 int y = m_y;
824 int w = width();
825 int h = height();
826
827 // Constrain our background/border painting to the line top and bottom if necessary.
828 bool strictMode = renderer()->document()->inStrictMode();
829 if (!hasTextChildren() && !strictMode) {
830 RootInlineBox* rootBox = root();
831 int bottom = min(rootBox->lineBottom(), y + h);
832 y = max(rootBox->lineTop(), y);
833 h = bottom - y;
834 }
835
836 // Move x/y to our coordinates.
837 tx += x;
838 ty += y;
839
840 const NinePieceImage& maskNinePieceImage = renderer()->style()->maskBoxImage();
841 StyleImage* maskBoxImage = renderer()->style()->maskBoxImage().image();
842
843 // Figure out if we need to push a transparency layer to render our mask.
844 bool pushTransparencyLayer = false;
845 bool compositedMask = renderer()->hasLayer() && boxModelObject()->layer()->hasCompositedMask();
846 CompositeOperator compositeOp = CompositeSourceOver;
847 if (!compositedMask) {
848 if ((maskBoxImage && renderer()->style()->maskLayers()->hasImage()) || renderer()->style()->maskLayers()->next())
849 pushTransparencyLayer = true;
850
851 compositeOp = CompositeDestinationIn;
852 if (pushTransparencyLayer) {
853 paintInfo.context->setCompositeOperation(CompositeDestinationIn);
854 paintInfo.context->beginTransparencyLayer(1.0f);
855 compositeOp = CompositeSourceOver;
856 }
857 }
858
859 paintFillLayers(paintInfo, Color(), renderer()->style()->maskLayers(), tx, ty, w, h, compositeOp);
860
861 bool hasBoxImage = maskBoxImage && maskBoxImage->canRender(renderer()->style()->effectiveZoom());
862 if (!hasBoxImage || !maskBoxImage->isLoaded())
863 return; // Don't paint anything while we wait for the image to load.
864
865 // The simple case is where we are the only box for this object. In those
866 // cases only a single call to draw is required.
867 if (!prevLineBox() && !nextLineBox()) {
868 boxModelObject()->paintNinePieceImage(paintInfo.context, tx, ty, w, h, renderer()->style(), maskNinePieceImage, compositeOp);
869 } else {
870 // We have a mask image that spans multiple lines.
871 // We need to adjust _tx and _ty by the width of all previous lines.
872 int xOffsetOnLine = 0;
873 for (InlineRunBox* curr = prevLineBox(); curr; curr = curr->prevLineBox())
874 xOffsetOnLine += curr->width();
875 int startX = tx - xOffsetOnLine;
876 int totalWidth = xOffsetOnLine;
877 for (InlineRunBox* curr = this; curr; curr = curr->nextLineBox())
878 totalWidth += curr->width();
879 paintInfo.context->save();
880 paintInfo.context->clip(IntRect(tx, ty, w, h));
881 boxModelObject()->paintNinePieceImage(paintInfo.context, startX, ty, totalWidth, h, renderer()->style(), maskNinePieceImage, compositeOp);
882 paintInfo.context->restore();
883 }
884
885 if (pushTransparencyLayer)
886 paintInfo.context->endTransparencyLayer();
887 }
888
shouldDrawTextDecoration(RenderObject * obj)889 static bool shouldDrawTextDecoration(RenderObject* obj)
890 {
891 for (RenderObject* curr = obj->firstChild(); curr; curr = curr->nextSibling()) {
892 if (curr->isRenderInline())
893 return true;
894 if (curr->isText() && !curr->isBR()) {
895 if (!curr->style()->collapseWhiteSpace())
896 return true;
897 Node* currElement = curr->node();
898 if (!currElement)
899 return true;
900 if (!currElement->isTextNode())
901 return true;
902 if (!static_cast<Text*>(currElement)->containsOnlyWhitespace())
903 return true;
904 }
905 }
906 return false;
907 }
908
paintTextDecorations(RenderObject::PaintInfo & paintInfo,int tx,int ty,bool paintedChildren)909 void InlineFlowBox::paintTextDecorations(RenderObject::PaintInfo& paintInfo, int tx, int ty, bool paintedChildren)
910 {
911 // Paint text decorations like underlines/overlines. We only do this if we aren't in quirks mode (i.e., in
912 // almost-strict mode or strict mode).
913 if (renderer()->style()->htmlHacks() || !renderer()->shouldPaintWithinRoot(paintInfo) ||
914 renderer()->style()->visibility() != VISIBLE)
915 return;
916
917 // We don't want underlines or other decorations when we're trying to draw nothing but the selection as white text.
918 if (paintInfo.phase == PaintPhaseSelection && paintInfo.forceBlackText)
919 return;
920
921 GraphicsContext* context = paintInfo.context;
922 tx += m_x;
923 ty += m_y;
924 RenderStyle* styleToUse = renderer()->style(m_firstLine);
925 int deco = parent() ? styleToUse->textDecoration() : styleToUse->textDecorationsInEffect();
926 if (deco != TDNONE &&
927 ((!paintedChildren && ((deco & UNDERLINE) || (deco & OVERLINE))) || (paintedChildren && (deco & LINE_THROUGH))) &&
928 shouldDrawTextDecoration(renderer())) {
929 int x = m_x + borderLeft() + paddingLeft();
930 int w = m_width - (borderLeft() + paddingLeft() + borderRight() + paddingRight());
931 RootInlineBox* rootLine = root();
932 if (rootLine->ellipsisBox()) {
933 int ellipsisX = m_x + rootLine->ellipsisBox()->x();
934 int ellipsisWidth = rootLine->ellipsisBox()->width();
935 bool ltr = renderer()->style()->direction() == LTR;
936 if (rootLine == this) {
937 // Trim w and x so that the underline isn't drawn underneath the ellipsis.
938 // ltr: is our right edge farther right than the right edge of the ellipsis.
939 // rtl: is the left edge of our box farther left than the left edge of the ellipsis.
940 bool ltrTruncation = ltr && (x + w >= ellipsisX + ellipsisWidth);
941 bool rtlTruncation = !ltr && (x <= ellipsisX + ellipsisWidth);
942 if (ltrTruncation)
943 w -= (x + w) - (ellipsisX + ellipsisWidth);
944 else if (rtlTruncation) {
945 int dx = m_x - ((ellipsisX - m_x) + ellipsisWidth);
946 tx -= dx;
947 w += dx;
948 }
949 } else {
950 bool ltrPastEllipsis = ltr && x >= ellipsisX;
951 bool rtlPastEllipsis = !ltr && (x + w) <= (ellipsisX + ellipsisWidth);
952 if (ltrPastEllipsis || rtlPastEllipsis)
953 return;
954
955 bool ltrTruncation = ltr && x + w >= ellipsisX;
956 bool rtlTruncation = !ltr && x <= ellipsisX;
957 if (ltrTruncation)
958 w -= (x + w - ellipsisX);
959 else if (rtlTruncation) {
960 int dx = m_x - ((ellipsisX - m_x) + ellipsisWidth);
961 tx -= dx;
962 w += dx;
963 }
964 }
965 }
966
967 // We must have child boxes and have decorations defined.
968 tx += borderLeft() + paddingLeft();
969
970 Color underline, overline, linethrough;
971 underline = overline = linethrough = styleToUse->color();
972 if (!parent())
973 renderer()->getTextDecorationColors(deco, underline, overline, linethrough);
974
975 bool isPrinting = renderer()->document()->printing();
976 context->setStrokeThickness(1.0f); // FIXME: We should improve this rule and not always just assume 1.
977
978 bool paintUnderline = deco & UNDERLINE && !paintedChildren;
979 bool paintOverline = deco & OVERLINE && !paintedChildren;
980 bool paintLineThrough = deco & LINE_THROUGH && paintedChildren;
981
982 bool linesAreOpaque = !isPrinting && (!paintUnderline || underline.alpha() == 255) && (!paintOverline || overline.alpha() == 255) && (!paintLineThrough || linethrough.alpha() == 255);
983
984 int baselinePos = renderer()->style(m_firstLine)->font().ascent();
985 if (!isRootInlineBox())
986 baselinePos += borderTop() + paddingTop();
987
988 bool setClip = false;
989 int extraOffset = 0;
990 ShadowData* shadow = styleToUse->textShadow();
991 if (!linesAreOpaque && shadow && shadow->next) {
992 IntRect clipRect(tx, ty, w, baselinePos + 2);
993 for (ShadowData* s = shadow; s; s = s->next) {
994 IntRect shadowRect(tx, ty, w, baselinePos + 2);
995 shadowRect.inflate(s->blur);
996 shadowRect.move(s->x, s->y);
997 clipRect.unite(shadowRect);
998 extraOffset = max(extraOffset, max(0, s->y) + s->blur);
999 }
1000 context->save();
1001 context->clip(clipRect);
1002 extraOffset += baselinePos + 2;
1003 ty += extraOffset;
1004 setClip = true;
1005 }
1006
1007 ColorSpace colorSpace = renderer()->style()->colorSpace();
1008 bool setShadow = false;
1009 do {
1010 if (shadow) {
1011 if (!shadow->next) {
1012 // The last set of lines paints normally inside the clip.
1013 ty -= extraOffset;
1014 extraOffset = 0;
1015 }
1016 context->setShadow(IntSize(shadow->x, shadow->y - extraOffset), shadow->blur, shadow->color, colorSpace);
1017 setShadow = true;
1018 shadow = shadow->next;
1019 }
1020
1021 if (paintUnderline) {
1022 context->setStrokeColor(underline, colorSpace);
1023 context->setStrokeStyle(SolidStroke);
1024 // Leave one pixel of white between the baseline and the underline.
1025 context->drawLineForText(IntPoint(tx, ty + baselinePos + 1), w, isPrinting);
1026 }
1027 if (paintOverline) {
1028 context->setStrokeColor(overline, colorSpace);
1029 context->setStrokeStyle(SolidStroke);
1030 context->drawLineForText(IntPoint(tx, ty), w, isPrinting);
1031 }
1032 if (paintLineThrough) {
1033 context->setStrokeColor(linethrough, colorSpace);
1034 context->setStrokeStyle(SolidStroke);
1035 context->drawLineForText(IntPoint(tx, ty + 2 * baselinePos / 3), w, isPrinting);
1036 }
1037 } while (shadow);
1038
1039 if (setClip)
1040 context->restore();
1041 else if (setShadow)
1042 context->clearShadow();
1043 }
1044 }
1045
firstLeafChild() const1046 InlineBox* InlineFlowBox::firstLeafChild() const
1047 {
1048 InlineBox* leaf = 0;
1049 for (InlineBox* child = firstChild(); child && !leaf; child = child->nextOnLine())
1050 leaf = child->isLeaf() ? child : static_cast<InlineFlowBox*>(child)->firstLeafChild();
1051 return leaf;
1052 }
1053
lastLeafChild() const1054 InlineBox* InlineFlowBox::lastLeafChild() const
1055 {
1056 InlineBox* leaf = 0;
1057 for (InlineBox* child = lastChild(); child && !leaf; child = child->prevOnLine())
1058 leaf = child->isLeaf() ? child : static_cast<InlineFlowBox*>(child)->lastLeafChild();
1059 return leaf;
1060 }
1061
selectionState()1062 RenderObject::SelectionState InlineFlowBox::selectionState()
1063 {
1064 return RenderObject::SelectionNone;
1065 }
1066
canAccommodateEllipsis(bool ltr,int blockEdge,int ellipsisWidth)1067 bool InlineFlowBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth)
1068 {
1069 for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) {
1070 if (!box->canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth))
1071 return false;
1072 }
1073 return true;
1074 }
1075
placeEllipsisBox(bool ltr,int blockLeftEdge,int blockRightEdge,int ellipsisWidth,bool & foundBox)1076 int InlineFlowBox::placeEllipsisBox(bool ltr, int blockLeftEdge, int blockRightEdge, int ellipsisWidth, bool& foundBox)
1077 {
1078 int result = -1;
1079 // We iterate over all children, the foundBox variable tells us when we've found the
1080 // box containing the ellipsis. All boxes after that one in the flow are hidden.
1081 // If our flow is ltr then iterate over the boxes from left to right, otherwise iterate
1082 // from right to left. Varying the order allows us to correctly hide the boxes following the ellipsis.
1083 InlineBox *box = ltr ? firstChild() : lastChild();
1084
1085 // NOTE: these will cross after foundBox = true.
1086 int visibleLeftEdge = blockLeftEdge;
1087 int visibleRightEdge = blockRightEdge;
1088
1089 while (box) {
1090 int currResult = box->placeEllipsisBox(ltr, visibleLeftEdge, visibleRightEdge, ellipsisWidth, foundBox);
1091 if (currResult != -1 && result == -1)
1092 result = currResult;
1093
1094 if (ltr) {
1095 visibleLeftEdge += box->width();
1096 box = box->nextOnLine();
1097 }
1098 else {
1099 visibleRightEdge -= box->width();
1100 box = box->prevOnLine();
1101 }
1102 }
1103 return result;
1104 }
1105
clearTruncation()1106 void InlineFlowBox::clearTruncation()
1107 {
1108 for (InlineBox *box = firstChild(); box; box = box->nextOnLine())
1109 box->clearTruncation();
1110 }
1111
1112 #ifndef NDEBUG
1113
checkConsistency() const1114 void InlineFlowBox::checkConsistency() const
1115 {
1116 #ifdef CHECK_CONSISTENCY
1117 ASSERT(!m_hasBadChildList);
1118 const InlineBox* prev = 0;
1119 for (const InlineBox* child = m_firstChild; child; child = child->nextOnLine()) {
1120 ASSERT(child->parent() == this);
1121 ASSERT(child->prevOnLine() == prev);
1122 prev = child;
1123 }
1124 ASSERT(prev == m_lastChild);
1125 #endif
1126 }
1127
1128 #endif
1129
1130 } // namespace WebCore
1131