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