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