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 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 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 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 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 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 133 void InlineFlowBox::removeLineBoxFromRenderObject() 134 { 135 toRenderInline(renderer())->lineBoxes()->removeLineBox(this); 136 } 137 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 146 void InlineFlowBox::extractLineBoxFromRenderObject() 147 { 148 toRenderInline(renderer())->lineBoxes()->extractLineBox(this); 149 } 150 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 159 void InlineFlowBox::attachLineBoxToRenderObject() 160 { 161 toRenderInline(renderer())->lineBoxes()->attachLineBox(this); 162 } 163 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 173 RenderLineBoxList* InlineFlowBox::rendererLineBoxes() const 174 { 175 return toRenderInline(renderer())->lineBoxes(); 176 } 177 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 1046 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 1054 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 1062 RenderObject::SelectionState InlineFlowBox::selectionState() 1063 { 1064 return RenderObject::SelectionNone; 1065 } 1066 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 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 1106 void InlineFlowBox::clearTruncation() 1107 { 1108 for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) 1109 box->clearTruncation(); 1110 } 1111 1112 #ifndef NDEBUG 1113 1114 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