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 "core/rendering/InlineFlowBox.h" 22 23 #include "CSSPropertyNames.h" 24 #include "core/dom/Document.h" 25 #include "core/platform/graphics/Font.h" 26 #include "core/platform/graphics/GraphicsContextStateSaver.h" 27 #include "core/rendering/HitTestResult.h" 28 #include "core/rendering/InlineTextBox.h" 29 #include "core/rendering/RenderBlock.h" 30 #include "core/rendering/RenderInline.h" 31 #include "core/rendering/RenderLayer.h" 32 #include "core/rendering/RenderListMarker.h" 33 #include "core/rendering/RenderRubyBase.h" 34 #include "core/rendering/RenderRubyRun.h" 35 #include "core/rendering/RenderRubyText.h" 36 #include "core/rendering/RenderView.h" 37 #include "core/rendering/RootInlineBox.h" 38 39 #include <math.h> 40 41 using namespace std; 42 43 namespace WebCore { 44 45 struct SameSizeAsInlineFlowBox : public InlineBox { 46 void* pointers[5]; 47 uint32_t bitfields : 23; 48 }; 49 50 COMPILE_ASSERT(sizeof(InlineFlowBox) == sizeof(SameSizeAsInlineFlowBox), InlineFlowBox_should_stay_small); 51 52 #ifndef NDEBUG 53 54 InlineFlowBox::~InlineFlowBox() 55 { 56 if (!m_hasBadChildList) 57 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) 58 child->setHasBadParent(); 59 } 60 61 #endif 62 63 LayoutUnit InlineFlowBox::getFlowSpacingLogicalWidth() 64 { 65 LayoutUnit totWidth = marginBorderPaddingLogicalLeft() + marginBorderPaddingLogicalRight(); 66 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { 67 if (curr->isInlineFlowBox()) 68 totWidth += toInlineFlowBox(curr)->getFlowSpacingLogicalWidth(); 69 } 70 return totWidth; 71 } 72 73 IntRect InlineFlowBox::roundedFrameRect() const 74 { 75 // Begin by snapping the x and y coordinates to the nearest pixel. 76 int snappedX = lroundf(x()); 77 int snappedY = lroundf(y()); 78 79 int snappedMaxX = lroundf(x() + width()); 80 int snappedMaxY = lroundf(y() + height()); 81 82 return IntRect(snappedX, snappedY, snappedMaxX - snappedX, snappedMaxY - snappedY); 83 } 84 85 static void setHasTextDescendantsOnAncestors(InlineFlowBox* box) 86 { 87 while (box && !box->hasTextDescendants()) { 88 box->setHasTextDescendants(); 89 box = box->parent(); 90 } 91 } 92 93 void InlineFlowBox::addToLine(InlineBox* child) 94 { 95 ASSERT(!child->parent()); 96 ASSERT(!child->nextOnLine()); 97 ASSERT(!child->prevOnLine()); 98 checkConsistency(); 99 100 child->setParent(this); 101 if (!m_firstChild) { 102 m_firstChild = child; 103 m_lastChild = child; 104 } else { 105 m_lastChild->setNextOnLine(child); 106 child->setPrevOnLine(m_lastChild); 107 m_lastChild = child; 108 } 109 child->setFirstLineStyleBit(isFirstLineStyle()); 110 child->setIsHorizontal(isHorizontal()); 111 if (child->isText()) { 112 if (child->renderer()->parent() == renderer()) 113 m_hasTextChildren = true; 114 setHasTextDescendantsOnAncestors(this); 115 } else if (child->isInlineFlowBox()) { 116 if (toInlineFlowBox(child)->hasTextDescendants()) 117 setHasTextDescendantsOnAncestors(this); 118 } 119 120 if (descendantsHaveSameLineHeightAndBaseline() && !child->renderer()->isOutOfFlowPositioned()) { 121 RenderStyle* parentStyle = renderer()->style(isFirstLineStyle()); 122 RenderStyle* childStyle = child->renderer()->style(isFirstLineStyle()); 123 bool shouldClearDescendantsHaveSameLineHeightAndBaseline = false; 124 if (child->renderer()->isReplaced()) 125 shouldClearDescendantsHaveSameLineHeightAndBaseline = true; 126 else if (child->isText()) { 127 if (child->renderer()->isBR() || child->renderer()->parent() != renderer()) { 128 if (!parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle->font().fontMetrics()) 129 || parentStyle->lineHeight() != childStyle->lineHeight() 130 || (parentStyle->verticalAlign() != BASELINE && !isRootInlineBox()) || childStyle->verticalAlign() != BASELINE) 131 shouldClearDescendantsHaveSameLineHeightAndBaseline = true; 132 } 133 if (childStyle->hasTextCombine() || childStyle->textEmphasisMark() != TextEmphasisMarkNone) 134 shouldClearDescendantsHaveSameLineHeightAndBaseline = true; 135 } else { 136 if (child->renderer()->isBR()) { 137 // FIXME: This is dumb. We only turn off because current layout test results expect the <br> to be 0-height on the baseline. 138 // Other than making a zillion tests have to regenerate results, there's no reason to ditch the optimization here. 139 shouldClearDescendantsHaveSameLineHeightAndBaseline = true; 140 } else { 141 ASSERT(isInlineFlowBox()); 142 InlineFlowBox* childFlowBox = toInlineFlowBox(child); 143 // Check the child's bit, and then also check for differences in font, line-height, vertical-align 144 if (!childFlowBox->descendantsHaveSameLineHeightAndBaseline() 145 || !parentStyle->font().fontMetrics().hasIdenticalAscentDescentAndLineGap(childStyle->font().fontMetrics()) 146 || parentStyle->lineHeight() != childStyle->lineHeight() 147 || (parentStyle->verticalAlign() != BASELINE && !isRootInlineBox()) || childStyle->verticalAlign() != BASELINE 148 || childStyle->hasBorder() || childStyle->hasPadding() || childStyle->hasTextCombine()) 149 shouldClearDescendantsHaveSameLineHeightAndBaseline = true; 150 } 151 } 152 153 if (shouldClearDescendantsHaveSameLineHeightAndBaseline) 154 clearDescendantsHaveSameLineHeightAndBaseline(); 155 } 156 157 if (!child->renderer()->isOutOfFlowPositioned()) { 158 if (child->isText()) { 159 RenderStyle* childStyle = child->renderer()->style(isFirstLineStyle()); 160 if (childStyle->letterSpacing() < 0 || childStyle->textShadow() || childStyle->textEmphasisMark() != TextEmphasisMarkNone || childStyle->textStrokeWidth()) 161 child->clearKnownToHaveNoOverflow(); 162 } else if (child->renderer()->isReplaced()) { 163 RenderBox* box = toRenderBox(child->renderer()); 164 if (box->hasRenderOverflow() || box->hasSelfPaintingLayer()) 165 child->clearKnownToHaveNoOverflow(); 166 } else if (!child->renderer()->isBR() && (child->renderer()->style(isFirstLineStyle())->boxShadow() || child->boxModelObject()->hasSelfPaintingLayer() 167 || (child->renderer()->isListMarker() && !toRenderListMarker(child->renderer())->isInside()) 168 || child->renderer()->style(isFirstLineStyle())->hasBorderImageOutsets())) 169 child->clearKnownToHaveNoOverflow(); 170 171 if (knownToHaveNoOverflow() && child->isInlineFlowBox() && !toInlineFlowBox(child)->knownToHaveNoOverflow()) 172 clearKnownToHaveNoOverflow(); 173 } 174 175 checkConsistency(); 176 } 177 178 void InlineFlowBox::removeChild(InlineBox* child) 179 { 180 checkConsistency(); 181 182 if (!isDirty()) 183 dirtyLineBoxes(); 184 185 root()->childRemoved(child); 186 187 if (child == m_firstChild) 188 m_firstChild = child->nextOnLine(); 189 if (child == m_lastChild) 190 m_lastChild = child->prevOnLine(); 191 if (child->nextOnLine()) 192 child->nextOnLine()->setPrevOnLine(child->prevOnLine()); 193 if (child->prevOnLine()) 194 child->prevOnLine()->setNextOnLine(child->nextOnLine()); 195 196 child->setParent(0); 197 198 checkConsistency(); 199 } 200 201 void InlineFlowBox::deleteLine() 202 { 203 InlineBox* child = firstChild(); 204 InlineBox* next = 0; 205 while (child) { 206 ASSERT(this == child->parent()); 207 next = child->nextOnLine(); 208 #ifndef NDEBUG 209 child->setParent(0); 210 #endif 211 child->deleteLine(); 212 child = next; 213 } 214 #ifndef NDEBUG 215 m_firstChild = 0; 216 m_lastChild = 0; 217 #endif 218 219 removeLineBoxFromRenderObject(); 220 destroy(); 221 } 222 223 void InlineFlowBox::removeLineBoxFromRenderObject() 224 { 225 toRenderInline(renderer())->lineBoxes()->removeLineBox(this); 226 } 227 228 void InlineFlowBox::extractLine() 229 { 230 if (!extracted()) 231 extractLineBoxFromRenderObject(); 232 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) 233 child->extractLine(); 234 } 235 236 void InlineFlowBox::extractLineBoxFromRenderObject() 237 { 238 toRenderInline(renderer())->lineBoxes()->extractLineBox(this); 239 } 240 241 void InlineFlowBox::attachLine() 242 { 243 if (extracted()) 244 attachLineBoxToRenderObject(); 245 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) 246 child->attachLine(); 247 } 248 249 void InlineFlowBox::attachLineBoxToRenderObject() 250 { 251 toRenderInline(renderer())->lineBoxes()->attachLineBox(this); 252 } 253 254 void InlineFlowBox::adjustPosition(float dx, float dy) 255 { 256 InlineBox::adjustPosition(dx, dy); 257 for (InlineBox* child = firstChild(); child; child = child->nextOnLine()) 258 child->adjustPosition(dx, dy); 259 if (m_overflow) 260 m_overflow->move(dx, dy); // FIXME: Rounding error here since overflow was pixel snapped, but nobody other than list markers passes non-integral values here. 261 } 262 263 RenderLineBoxList* InlineFlowBox::rendererLineBoxes() const 264 { 265 return toRenderInline(renderer())->lineBoxes(); 266 } 267 268 static inline bool isLastChildForRenderer(RenderObject* ancestor, RenderObject* child) 269 { 270 if (!child) 271 return false; 272 273 if (child == ancestor) 274 return true; 275 276 RenderObject* curr = child; 277 RenderObject* parent = curr->parent(); 278 while (parent && (!parent->isRenderBlock() || parent->isInline())) { 279 if (parent->lastChild() != curr) 280 return false; 281 if (parent == ancestor) 282 return true; 283 284 curr = parent; 285 parent = curr->parent(); 286 } 287 288 return true; 289 } 290 291 static bool isAnsectorAndWithinBlock(RenderObject* ancestor, RenderObject* child) 292 { 293 RenderObject* object = child; 294 while (object && (!object->isRenderBlock() || object->isInline())) { 295 if (object == ancestor) 296 return true; 297 object = object->parent(); 298 } 299 return false; 300 } 301 302 void InlineFlowBox::determineSpacingForFlowBoxes(bool lastLine, bool isLogicallyLastRunWrapped, RenderObject* logicallyLastRunRenderer) 303 { 304 // All boxes start off open. They will not apply any margins/border/padding on 305 // any side. 306 bool includeLeftEdge = false; 307 bool includeRightEdge = false; 308 309 // The root inline box never has borders/margins/padding. 310 if (parent()) { 311 bool ltr = renderer()->style()->isLeftToRightDirection(); 312 313 // Check to see if all initial lines are unconstructed. If so, then 314 // we know the inline began on this line (unless we are a continuation). 315 RenderLineBoxList* lineBoxList = rendererLineBoxes(); 316 if (!lineBoxList->firstLineBox()->isConstructed() && !renderer()->isInlineElementContinuation()) { 317 if (renderer()->style()->boxDecorationBreak() == DCLONE) 318 includeLeftEdge = includeRightEdge = true; 319 else if (ltr && lineBoxList->firstLineBox() == this) 320 includeLeftEdge = true; 321 else if (!ltr && lineBoxList->lastLineBox() == this) 322 includeRightEdge = true; 323 } 324 325 if (!lineBoxList->lastLineBox()->isConstructed()) { 326 RenderInline* inlineFlow = toRenderInline(renderer()); 327 bool isLastObjectOnLine = !isAnsectorAndWithinBlock(renderer(), logicallyLastRunRenderer) || (isLastChildForRenderer(renderer(), logicallyLastRunRenderer) && !isLogicallyLastRunWrapped); 328 329 // We include the border under these conditions: 330 // (1) The next line was not created, or it is constructed. We check the previous line for rtl. 331 // (2) The logicallyLastRun is not a descendant of this renderer. 332 // (3) The logicallyLastRun is a descendant of this renderer, but it is the last child of this renderer and it does not wrap to the next line. 333 // (4) The decoration break is set to clone therefore there will be borders on every sides. 334 if (renderer()->style()->boxDecorationBreak() == DCLONE) 335 includeLeftEdge = includeRightEdge = true; 336 else if (ltr) { 337 if (!nextLineBox() 338 && ((lastLine || isLastObjectOnLine) && !inlineFlow->continuation())) 339 includeRightEdge = true; 340 } else { 341 if ((!prevLineBox() || prevLineBox()->isConstructed()) 342 && ((lastLine || isLastObjectOnLine) && !inlineFlow->continuation())) 343 includeLeftEdge = true; 344 } 345 } 346 } 347 348 setEdges(includeLeftEdge, includeRightEdge); 349 350 // Recur into our children. 351 for (InlineBox* currChild = firstChild(); currChild; currChild = currChild->nextOnLine()) { 352 if (currChild->isInlineFlowBox()) { 353 InlineFlowBox* currFlow = toInlineFlowBox(currChild); 354 currFlow->determineSpacingForFlowBoxes(lastLine, isLogicallyLastRunWrapped, logicallyLastRunRenderer); 355 } 356 } 357 } 358 359 float InlineFlowBox::placeBoxesInInlineDirection(float logicalLeft, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) 360 { 361 // Set our x position. 362 beginPlacingBoxRangesInInlineDirection(logicalLeft); 363 364 float startLogicalLeft = logicalLeft; 365 logicalLeft += borderLogicalLeft() + paddingLogicalLeft(); 366 367 float minLogicalLeft = startLogicalLeft; 368 float maxLogicalRight = logicalLeft; 369 370 placeBoxRangeInInlineDirection(firstChild(), 0, logicalLeft, minLogicalLeft, maxLogicalRight, needsWordSpacing, textBoxDataMap); 371 372 logicalLeft += borderLogicalRight() + paddingLogicalRight(); 373 endPlacingBoxRangesInInlineDirection(startLogicalLeft, logicalLeft, minLogicalLeft, maxLogicalRight); 374 return logicalLeft; 375 } 376 377 float InlineFlowBox::placeBoxRangeInInlineDirection(InlineBox* firstChild, InlineBox* lastChild, float& logicalLeft, float& minLogicalLeft, float& maxLogicalRight, bool& needsWordSpacing, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) 378 { 379 for (InlineBox* curr = firstChild; curr && curr != lastChild; curr = curr->nextOnLine()) { 380 if (curr->renderer()->isText()) { 381 InlineTextBox* text = toInlineTextBox(curr); 382 RenderText* rt = toRenderText(text->renderer()); 383 if (rt->textLength()) { 384 if (needsWordSpacing && isSpaceOrNewline(rt->characterAt(text->start()))) 385 logicalLeft += rt->style(isFirstLineStyle())->font().wordSpacing(); 386 needsWordSpacing = !isSpaceOrNewline(rt->characterAt(text->end())); 387 } 388 text->setLogicalLeft(logicalLeft); 389 if (knownToHaveNoOverflow()) 390 minLogicalLeft = min(logicalLeft, minLogicalLeft); 391 logicalLeft += text->logicalWidth(); 392 if (knownToHaveNoOverflow()) 393 maxLogicalRight = max(logicalLeft, maxLogicalRight); 394 } else { 395 if (curr->renderer()->isOutOfFlowPositioned()) { 396 if (curr->renderer()->parent()->style()->isLeftToRightDirection()) 397 curr->setLogicalLeft(logicalLeft); 398 else 399 // Our offset that we cache needs to be from the edge of the right border box and 400 // not the left border box. We have to subtract |x| from the width of the block 401 // (which can be obtained from the root line box). 402 curr->setLogicalLeft(root()->block()->logicalWidth() - logicalLeft); 403 continue; // The positioned object has no effect on the width. 404 } 405 if (curr->renderer()->isRenderInline()) { 406 InlineFlowBox* flow = toInlineFlowBox(curr); 407 logicalLeft += flow->marginLogicalLeft(); 408 if (knownToHaveNoOverflow()) 409 minLogicalLeft = min(logicalLeft, minLogicalLeft); 410 logicalLeft = flow->placeBoxesInInlineDirection(logicalLeft, needsWordSpacing, textBoxDataMap); 411 if (knownToHaveNoOverflow()) 412 maxLogicalRight = max(logicalLeft, maxLogicalRight); 413 logicalLeft += flow->marginLogicalRight(); 414 } else if (!curr->renderer()->isListMarker() || toRenderListMarker(curr->renderer())->isInside()) { 415 // The box can have a different writing-mode than the overall line, so this is a bit complicated. 416 // Just get all the physical margin and overflow values by hand based off |isVertical|. 417 LayoutUnit logicalLeftMargin = isHorizontal() ? curr->boxModelObject()->marginLeft() : curr->boxModelObject()->marginTop(); 418 LayoutUnit logicalRightMargin = isHorizontal() ? curr->boxModelObject()->marginRight() : curr->boxModelObject()->marginBottom(); 419 420 logicalLeft += logicalLeftMargin; 421 curr->setLogicalLeft(logicalLeft); 422 if (knownToHaveNoOverflow()) 423 minLogicalLeft = min(logicalLeft, minLogicalLeft); 424 logicalLeft += curr->logicalWidth(); 425 if (knownToHaveNoOverflow()) 426 maxLogicalRight = max(logicalLeft, maxLogicalRight); 427 logicalLeft += logicalRightMargin; 428 // If we encounter any space after this inline block then ensure it is treated as the space between two words. 429 needsWordSpacing = true; 430 } 431 } 432 } 433 return logicalLeft; 434 } 435 436 bool InlineFlowBox::requiresIdeographicBaseline(const GlyphOverflowAndFallbackFontsMap& textBoxDataMap) const 437 { 438 if (isHorizontal()) 439 return false; 440 441 if (renderer()->style(isFirstLineStyle())->fontDescription().nonCJKGlyphOrientation() == NonCJKGlyphOrientationUpright 442 || renderer()->style(isFirstLineStyle())->font().primaryFont()->hasVerticalGlyphs()) 443 return true; 444 445 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { 446 if (curr->renderer()->isOutOfFlowPositioned()) 447 continue; // Positioned placeholders don't affect calculations. 448 449 if (curr->isInlineFlowBox()) { 450 if (toInlineFlowBox(curr)->requiresIdeographicBaseline(textBoxDataMap)) 451 return true; 452 } else { 453 if (curr->renderer()->style(isFirstLineStyle())->font().primaryFont()->hasVerticalGlyphs()) 454 return true; 455 456 const Vector<const SimpleFontData*>* usedFonts = 0; 457 if (curr->isInlineTextBox()) { 458 GlyphOverflowAndFallbackFontsMap::const_iterator it = textBoxDataMap.find(toInlineTextBox(curr)); 459 usedFonts = it == textBoxDataMap.end() ? 0 : &it->value.first; 460 } 461 462 if (usedFonts) { 463 for (size_t i = 0; i < usedFonts->size(); ++i) { 464 if (usedFonts->at(i)->hasVerticalGlyphs()) 465 return true; 466 } 467 } 468 } 469 } 470 471 return false; 472 } 473 474 void InlineFlowBox::adjustMaxAscentAndDescent(int& maxAscent, int& maxDescent, int maxPositionTop, int maxPositionBottom) 475 { 476 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { 477 // The computed lineheight needs to be extended for the 478 // positioned elements 479 if (curr->renderer()->isOutOfFlowPositioned()) 480 continue; // Positioned placeholders don't affect calculations. 481 if (curr->verticalAlign() == TOP || curr->verticalAlign() == BOTTOM) { 482 int lineHeight = curr->lineHeight(); 483 if (curr->verticalAlign() == TOP) { 484 if (maxAscent + maxDescent < lineHeight) 485 maxDescent = lineHeight - maxAscent; 486 } 487 else { 488 if (maxAscent + maxDescent < lineHeight) 489 maxAscent = lineHeight - maxDescent; 490 } 491 492 if (maxAscent + maxDescent >= max(maxPositionTop, maxPositionBottom)) 493 break; 494 } 495 496 if (curr->isInlineFlowBox()) 497 toInlineFlowBox(curr)->adjustMaxAscentAndDescent(maxAscent, maxDescent, maxPositionTop, maxPositionBottom); 498 } 499 } 500 501 void InlineFlowBox::computeLogicalBoxHeights(RootInlineBox* rootBox, LayoutUnit& maxPositionTop, LayoutUnit& maxPositionBottom, 502 int& maxAscent, int& maxDescent, bool& setMaxAscent, bool& setMaxDescent, 503 bool strictMode, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, 504 FontBaseline baselineType, VerticalPositionCache& verticalPositionCache) 505 { 506 // The primary purpose of this function is to compute the maximal ascent and descent values for 507 // a line. These values are computed based off the block's line-box-contain property, which indicates 508 // what parts of descendant boxes have to fit within the line. 509 // 510 // The maxAscent value represents the distance of the highest point of any box (typically including line-height) from 511 // the root box's baseline. The maxDescent value represents the distance of the lowest point of any box 512 // (also typically including line-height) from the root box baseline. These values can be negative. 513 // 514 // A secondary purpose of this function is to store the offset of every box's baseline from the root box's 515 // baseline. This information is cached in the logicalTop() of every box. We're effectively just using 516 // the logicalTop() as scratch space. 517 // 518 // Because a box can be positioned such that it ends up fully above or fully below the 519 // root line box, we only consider it to affect the maxAscent and maxDescent values if some 520 // part of the box (EXCLUDING leading) is above (for ascent) or below (for descent) the root box's baseline. 521 bool affectsAscent = false; 522 bool affectsDescent = false; 523 bool checkChildren = !descendantsHaveSameLineHeightAndBaseline(); 524 525 if (isRootInlineBox()) { 526 // Examine our root box. 527 int ascent = 0; 528 int descent = 0; 529 rootBox->ascentAndDescentForBox(rootBox, textBoxDataMap, ascent, descent, affectsAscent, affectsDescent); 530 if (strictMode || hasTextChildren() || (!checkChildren && hasTextDescendants())) { 531 if (maxAscent < ascent || !setMaxAscent) { 532 maxAscent = ascent; 533 setMaxAscent = true; 534 } 535 if (maxDescent < descent || !setMaxDescent) { 536 maxDescent = descent; 537 setMaxDescent = true; 538 } 539 } 540 } 541 542 if (!checkChildren) 543 return; 544 545 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { 546 if (curr->renderer()->isOutOfFlowPositioned()) 547 continue; // Positioned placeholders don't affect calculations. 548 549 InlineFlowBox* inlineFlowBox = curr->isInlineFlowBox() ? toInlineFlowBox(curr) : 0; 550 551 bool affectsAscent = false; 552 bool affectsDescent = false; 553 554 // The verticalPositionForBox function returns the distance between the child box's baseline 555 // and the root box's baseline. The value is negative if the child box's baseline is above the 556 // root box's baseline, and it is positive if the child box's baseline is below the root box's baseline. 557 curr->setLogicalTop(rootBox->verticalPositionForBox(curr, verticalPositionCache)); 558 559 int ascent = 0; 560 int descent = 0; 561 rootBox->ascentAndDescentForBox(curr, textBoxDataMap, ascent, descent, affectsAscent, affectsDescent); 562 563 LayoutUnit boxHeight = ascent + descent; 564 if (curr->verticalAlign() == TOP) { 565 if (maxPositionTop < boxHeight) 566 maxPositionTop = boxHeight; 567 } else if (curr->verticalAlign() == BOTTOM) { 568 if (maxPositionBottom < boxHeight) 569 maxPositionBottom = boxHeight; 570 } else if (!inlineFlowBox || strictMode || inlineFlowBox->hasTextChildren() || (inlineFlowBox->descendantsHaveSameLineHeightAndBaseline() && inlineFlowBox->hasTextDescendants()) 571 || inlineFlowBox->boxModelObject()->hasInlineDirectionBordersOrPadding()) { 572 // Note that these values can be negative. Even though we only affect the maxAscent and maxDescent values 573 // if our box (excluding line-height) was above (for ascent) or below (for descent) the root baseline, once you factor in line-height 574 // the final box can end up being fully above or fully below the root box's baseline! This is ok, but what it 575 // means is that ascent and descent (including leading), can end up being negative. The setMaxAscent and 576 // setMaxDescent booleans are used to ensure that we're willing to initially set maxAscent/Descent to negative 577 // values. 578 ascent -= curr->logicalTop(); 579 descent += curr->logicalTop(); 580 if (affectsAscent && (maxAscent < ascent || !setMaxAscent)) { 581 maxAscent = ascent; 582 setMaxAscent = true; 583 } 584 585 if (affectsDescent && (maxDescent < descent || !setMaxDescent)) { 586 maxDescent = descent; 587 setMaxDescent = true; 588 } 589 } 590 591 if (inlineFlowBox) 592 inlineFlowBox->computeLogicalBoxHeights(rootBox, maxPositionTop, maxPositionBottom, maxAscent, maxDescent, 593 setMaxAscent, setMaxDescent, strictMode, textBoxDataMap, 594 baselineType, verticalPositionCache); 595 } 596 } 597 598 void InlineFlowBox::placeBoxesInBlockDirection(LayoutUnit top, LayoutUnit maxHeight, int maxAscent, bool strictMode, LayoutUnit& lineTop, LayoutUnit& lineBottom, bool& setLineTop, 599 LayoutUnit& lineTopIncludingMargins, LayoutUnit& lineBottomIncludingMargins, bool& hasAnnotationsBefore, bool& hasAnnotationsAfter, FontBaseline baselineType) 600 { 601 bool isRootBox = isRootInlineBox(); 602 if (isRootBox) { 603 const FontMetrics& fontMetrics = renderer()->style(isFirstLineStyle())->fontMetrics(); 604 // RootInlineBoxes are always placed on at pixel boundaries in their logical y direction. Not doing 605 // so results in incorrect rendering of text decorations, most notably underlines. 606 setLogicalTop(roundToInt(top + maxAscent - fontMetrics.ascent(baselineType))); 607 } 608 609 LayoutUnit adjustmentForChildrenWithSameLineHeightAndBaseline = 0; 610 if (descendantsHaveSameLineHeightAndBaseline()) { 611 adjustmentForChildrenWithSameLineHeightAndBaseline = logicalTop(); 612 if (parent()) 613 adjustmentForChildrenWithSameLineHeightAndBaseline += (boxModelObject()->borderBefore() + boxModelObject()->paddingBefore()); 614 } 615 616 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { 617 if (curr->renderer()->isOutOfFlowPositioned()) 618 continue; // Positioned placeholders don't affect calculations. 619 620 if (descendantsHaveSameLineHeightAndBaseline()) { 621 curr->adjustBlockDirectionPosition(adjustmentForChildrenWithSameLineHeightAndBaseline); 622 continue; 623 } 624 625 InlineFlowBox* inlineFlowBox = curr->isInlineFlowBox() ? toInlineFlowBox(curr) : 0; 626 bool childAffectsTopBottomPos = true; 627 if (curr->verticalAlign() == TOP) 628 curr->setLogicalTop(top); 629 else if (curr->verticalAlign() == BOTTOM) 630 curr->setLogicalTop(top + maxHeight - curr->lineHeight()); 631 else { 632 if (!strictMode && inlineFlowBox && !inlineFlowBox->hasTextChildren() && !curr->boxModelObject()->hasInlineDirectionBordersOrPadding() 633 && !(inlineFlowBox->descendantsHaveSameLineHeightAndBaseline() && inlineFlowBox->hasTextDescendants())) 634 childAffectsTopBottomPos = false; 635 LayoutUnit posAdjust = maxAscent - curr->baselinePosition(baselineType); 636 curr->setLogicalTop(curr->logicalTop() + top + posAdjust); 637 } 638 639 LayoutUnit newLogicalTop = curr->logicalTop(); 640 LayoutUnit newLogicalTopIncludingMargins = newLogicalTop; 641 LayoutUnit boxHeight = curr->logicalHeight(); 642 LayoutUnit boxHeightIncludingMargins = boxHeight; 643 644 if (curr->isText() || curr->isInlineFlowBox()) { 645 const FontMetrics& fontMetrics = curr->renderer()->style(isFirstLineStyle())->fontMetrics(); 646 newLogicalTop += curr->baselinePosition(baselineType) - fontMetrics.ascent(baselineType); 647 if (curr->isInlineFlowBox()) { 648 RenderBoxModelObject* boxObject = toRenderBoxModelObject(curr->renderer()); 649 newLogicalTop -= boxObject->style(isFirstLineStyle())->isHorizontalWritingMode() ? boxObject->borderTop() + boxObject->paddingTop() : 650 boxObject->borderRight() + boxObject->paddingRight(); 651 } 652 newLogicalTopIncludingMargins = newLogicalTop; 653 } else if (!curr->renderer()->isBR()) { 654 RenderBox* box = toRenderBox(curr->renderer()); 655 newLogicalTopIncludingMargins = newLogicalTop; 656 LayoutUnit overSideMargin = curr->isHorizontal() ? box->marginTop() : box->marginRight(); 657 LayoutUnit underSideMargin = curr->isHorizontal() ? box->marginBottom() : box->marginLeft(); 658 newLogicalTop += overSideMargin; 659 boxHeightIncludingMargins += overSideMargin + underSideMargin; 660 } 661 662 curr->setLogicalTop(newLogicalTop); 663 664 if (childAffectsTopBottomPos) { 665 if (curr->renderer()->isRubyRun()) { 666 // Treat the leading on the first and last lines of ruby runs as not being part of the overall lineTop/lineBottom. 667 // Really this is a workaround hack for the fact that ruby should have been done as line layout and not done using 668 // inline-block. 669 if (renderer()->style()->isFlippedLinesWritingMode() == (curr->renderer()->style()->rubyPosition() == RubyPositionAfter)) 670 hasAnnotationsBefore = true; 671 else 672 hasAnnotationsAfter = true; 673 674 RenderRubyRun* rubyRun = toRenderRubyRun(curr->renderer()); 675 if (RenderRubyBase* rubyBase = rubyRun->rubyBase()) { 676 LayoutUnit bottomRubyBaseLeading = (curr->logicalHeight() - rubyBase->logicalBottom()) + rubyBase->logicalHeight() - (rubyBase->lastRootBox() ? rubyBase->lastRootBox()->lineBottom() : LayoutUnit()); 677 LayoutUnit topRubyBaseLeading = rubyBase->logicalTop() + (rubyBase->firstRootBox() ? rubyBase->firstRootBox()->lineTop() : LayoutUnit()); 678 newLogicalTop += !renderer()->style()->isFlippedLinesWritingMode() ? topRubyBaseLeading : bottomRubyBaseLeading; 679 boxHeight -= (topRubyBaseLeading + bottomRubyBaseLeading); 680 } 681 } 682 if (curr->isInlineTextBox()) { 683 TextEmphasisPosition emphasisMarkPosition; 684 if (toInlineTextBox(curr)->getEmphasisMarkPosition(curr->renderer()->style(isFirstLineStyle()), emphasisMarkPosition)) { 685 bool emphasisMarkIsOver = emphasisMarkPosition == TextEmphasisPositionOver; 686 if (emphasisMarkIsOver != curr->renderer()->style(isFirstLineStyle())->isFlippedLinesWritingMode()) 687 hasAnnotationsBefore = true; 688 else 689 hasAnnotationsAfter = true; 690 } 691 } 692 693 if (!setLineTop) { 694 setLineTop = true; 695 lineTop = newLogicalTop; 696 lineTopIncludingMargins = min(lineTop, newLogicalTopIncludingMargins); 697 } else { 698 lineTop = min(lineTop, newLogicalTop); 699 lineTopIncludingMargins = min(lineTop, min(lineTopIncludingMargins, newLogicalTopIncludingMargins)); 700 } 701 lineBottom = max(lineBottom, newLogicalTop + boxHeight); 702 lineBottomIncludingMargins = max(lineBottom, max(lineBottomIncludingMargins, newLogicalTopIncludingMargins + boxHeightIncludingMargins)); 703 } 704 705 // Adjust boxes to use their real box y/height and not the logical height (as dictated by 706 // line-height). 707 if (inlineFlowBox) 708 inlineFlowBox->placeBoxesInBlockDirection(top, maxHeight, maxAscent, strictMode, lineTop, lineBottom, setLineTop, 709 lineTopIncludingMargins, lineBottomIncludingMargins, hasAnnotationsBefore, hasAnnotationsAfter, baselineType); 710 } 711 712 if (isRootBox) { 713 if (strictMode || hasTextChildren() || (descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) { 714 if (!setLineTop) { 715 setLineTop = true; 716 lineTop = pixelSnappedLogicalTop(); 717 lineTopIncludingMargins = lineTop; 718 } else { 719 lineTop = min<LayoutUnit>(lineTop, pixelSnappedLogicalTop()); 720 lineTopIncludingMargins = min(lineTop, lineTopIncludingMargins); 721 } 722 lineBottom = max<LayoutUnit>(lineBottom, pixelSnappedLogicalBottom()); 723 lineBottomIncludingMargins = max(lineBottom, lineBottomIncludingMargins); 724 } 725 726 if (renderer()->style()->isFlippedLinesWritingMode()) 727 flipLinesInBlockDirection(lineTopIncludingMargins, lineBottomIncludingMargins); 728 } 729 } 730 731 #if ENABLE(CSS3_TEXT) 732 void InlineFlowBox::computeMaxLogicalTop(float& maxLogicalTop) const 733 { 734 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { 735 if (curr->renderer()->isOutOfFlowPositioned()) 736 continue; // Positioned placeholders don't affect calculations. 737 738 if (descendantsHaveSameLineHeightAndBaseline()) 739 continue; 740 741 maxLogicalTop = max<float>(maxLogicalTop, curr->y()); 742 float localMaxLogicalTop = 0; 743 if (curr->isInlineFlowBox()) 744 toInlineFlowBox(curr)->computeMaxLogicalTop(localMaxLogicalTop); 745 maxLogicalTop = max<float>(maxLogicalTop, localMaxLogicalTop); 746 } 747 } 748 #endif // CSS3_TEXT 749 750 void InlineFlowBox::flipLinesInBlockDirection(LayoutUnit lineTop, LayoutUnit lineBottom) 751 { 752 // Flip the box on the line such that the top is now relative to the lineBottom instead of the lineTop. 753 setLogicalTop(lineBottom - (logicalTop() - lineTop) - logicalHeight()); 754 755 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { 756 if (curr->renderer()->isOutOfFlowPositioned()) 757 continue; // Positioned placeholders aren't affected here. 758 759 if (curr->isInlineFlowBox()) 760 toInlineFlowBox(curr)->flipLinesInBlockDirection(lineTop, lineBottom); 761 else 762 curr->setLogicalTop(lineBottom - (curr->logicalTop() - lineTop) - curr->logicalHeight()); 763 } 764 } 765 766 inline void InlineFlowBox::addBoxShadowVisualOverflow(LayoutRect& logicalVisualOverflow) 767 { 768 // box-shadow on root line boxes is applying to the block and not to the lines. 769 if (!parent()) 770 return; 771 772 RenderStyle* style = renderer()->style(isFirstLineStyle()); 773 if (!style->boxShadow()) 774 return; 775 776 LayoutUnit boxShadowLogicalTop; 777 LayoutUnit boxShadowLogicalBottom; 778 style->getBoxShadowBlockDirectionExtent(boxShadowLogicalTop, boxShadowLogicalBottom); 779 780 // Similar to how glyph overflow works, if our lines are flipped, then it's actually the opposite shadow that applies, since 781 // the line is "upside down" in terms of block coordinates. 782 LayoutUnit shadowLogicalTop = style->isFlippedLinesWritingMode() ? -boxShadowLogicalBottom : boxShadowLogicalTop; 783 LayoutUnit shadowLogicalBottom = style->isFlippedLinesWritingMode() ? -boxShadowLogicalTop : boxShadowLogicalBottom; 784 785 LayoutUnit logicalTopVisualOverflow = min(pixelSnappedLogicalTop() + shadowLogicalTop, logicalVisualOverflow.y()); 786 LayoutUnit logicalBottomVisualOverflow = max(pixelSnappedLogicalBottom() + shadowLogicalBottom, logicalVisualOverflow.maxY()); 787 788 LayoutUnit boxShadowLogicalLeft; 789 LayoutUnit boxShadowLogicalRight; 790 style->getBoxShadowInlineDirectionExtent(boxShadowLogicalLeft, boxShadowLogicalRight); 791 792 LayoutUnit logicalLeftVisualOverflow = min(pixelSnappedLogicalLeft() + boxShadowLogicalLeft, logicalVisualOverflow.x()); 793 LayoutUnit logicalRightVisualOverflow = max(pixelSnappedLogicalRight() + boxShadowLogicalRight, logicalVisualOverflow.maxX()); 794 795 logicalVisualOverflow = LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow, 796 logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow); 797 } 798 799 inline void InlineFlowBox::addBorderOutsetVisualOverflow(LayoutRect& logicalVisualOverflow) 800 { 801 // border-image-outset on root line boxes is applying to the block and not to the lines. 802 if (!parent()) 803 return; 804 805 RenderStyle* style = renderer()->style(isFirstLineStyle()); 806 if (!style->hasBorderImageOutsets()) 807 return; 808 809 LayoutBoxExtent borderOutsets = style->borderImageOutsets(); 810 811 LayoutUnit borderOutsetLogicalTop = borderOutsets.logicalTop(style->writingMode()); 812 LayoutUnit borderOutsetLogicalBottom = borderOutsets.logicalBottom(style->writingMode()); 813 LayoutUnit borderOutsetLogicalLeft = borderOutsets.logicalLeft(style->writingMode()); 814 LayoutUnit borderOutsetLogicalRight = borderOutsets.logicalRight(style->writingMode()); 815 816 // Similar to how glyph overflow works, if our lines are flipped, then it's actually the opposite border that applies, since 817 // the line is "upside down" in terms of block coordinates. vertical-rl and horizontal-bt are the flipped line modes. 818 LayoutUnit outsetLogicalTop = style->isFlippedLinesWritingMode() ? borderOutsetLogicalBottom : borderOutsetLogicalTop; 819 LayoutUnit outsetLogicalBottom = style->isFlippedLinesWritingMode() ? borderOutsetLogicalTop : borderOutsetLogicalBottom; 820 821 LayoutUnit logicalTopVisualOverflow = min(pixelSnappedLogicalTop() - outsetLogicalTop, logicalVisualOverflow.y()); 822 LayoutUnit logicalBottomVisualOverflow = max(pixelSnappedLogicalBottom() + outsetLogicalBottom, logicalVisualOverflow.maxY()); 823 824 LayoutUnit outsetLogicalLeft = includeLogicalLeftEdge() ? borderOutsetLogicalLeft : LayoutUnit(); 825 LayoutUnit outsetLogicalRight = includeLogicalRightEdge() ? borderOutsetLogicalRight : LayoutUnit(); 826 827 LayoutUnit logicalLeftVisualOverflow = min(pixelSnappedLogicalLeft() - outsetLogicalLeft, logicalVisualOverflow.x()); 828 LayoutUnit logicalRightVisualOverflow = max(pixelSnappedLogicalRight() + outsetLogicalRight, logicalVisualOverflow.maxX()); 829 830 logicalVisualOverflow = LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow, 831 logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow); 832 } 833 834 inline void InlineFlowBox::addTextBoxVisualOverflow(InlineTextBox* textBox, GlyphOverflowAndFallbackFontsMap& textBoxDataMap, LayoutRect& logicalVisualOverflow) 835 { 836 if (textBox->knownToHaveNoOverflow()) 837 return; 838 839 RenderStyle* style = textBox->renderer()->style(isFirstLineStyle()); 840 841 GlyphOverflowAndFallbackFontsMap::iterator it = textBoxDataMap.find(textBox); 842 GlyphOverflow* glyphOverflow = it == textBoxDataMap.end() ? 0 : &it->value.second; 843 bool isFlippedLine = style->isFlippedLinesWritingMode(); 844 845 int topGlyphEdge = glyphOverflow ? (isFlippedLine ? glyphOverflow->bottom : glyphOverflow->top) : 0; 846 int bottomGlyphEdge = glyphOverflow ? (isFlippedLine ? glyphOverflow->top : glyphOverflow->bottom) : 0; 847 int leftGlyphEdge = glyphOverflow ? glyphOverflow->left : 0; 848 int rightGlyphEdge = glyphOverflow ? glyphOverflow->right : 0; 849 850 int strokeOverflow = static_cast<int>(ceilf(style->textStrokeWidth() / 2.0f)); 851 int topGlyphOverflow = -strokeOverflow - topGlyphEdge; 852 int bottomGlyphOverflow = strokeOverflow + bottomGlyphEdge; 853 int leftGlyphOverflow = -strokeOverflow - leftGlyphEdge; 854 int rightGlyphOverflow = strokeOverflow + rightGlyphEdge; 855 856 TextEmphasisPosition emphasisMarkPosition; 857 if (style->textEmphasisMark() != TextEmphasisMarkNone && textBox->getEmphasisMarkPosition(style, emphasisMarkPosition)) { 858 int emphasisMarkHeight = style->font().emphasisMarkHeight(style->textEmphasisMarkString()); 859 if ((emphasisMarkPosition == TextEmphasisPositionOver) == (!style->isFlippedLinesWritingMode())) 860 topGlyphOverflow = min(topGlyphOverflow, -emphasisMarkHeight); 861 else 862 bottomGlyphOverflow = max(bottomGlyphOverflow, emphasisMarkHeight); 863 } 864 865 // If letter-spacing is negative, we should factor that into right layout overflow. (Even in RTL, letter-spacing is 866 // applied to the right, so this is not an issue with left overflow. 867 rightGlyphOverflow -= min(0, (int)style->font().letterSpacing()); 868 869 LayoutUnit textShadowLogicalTop; 870 LayoutUnit textShadowLogicalBottom; 871 style->getTextShadowBlockDirectionExtent(textShadowLogicalTop, textShadowLogicalBottom); 872 873 LayoutUnit childOverflowLogicalTop = min<LayoutUnit>(textShadowLogicalTop + topGlyphOverflow, topGlyphOverflow); 874 LayoutUnit childOverflowLogicalBottom = max<LayoutUnit>(textShadowLogicalBottom + bottomGlyphOverflow, bottomGlyphOverflow); 875 876 LayoutUnit textShadowLogicalLeft; 877 LayoutUnit textShadowLogicalRight; 878 style->getTextShadowInlineDirectionExtent(textShadowLogicalLeft, textShadowLogicalRight); 879 880 LayoutUnit childOverflowLogicalLeft = min<LayoutUnit>(textShadowLogicalLeft + leftGlyphOverflow, leftGlyphOverflow); 881 LayoutUnit childOverflowLogicalRight = max<LayoutUnit>(textShadowLogicalRight + rightGlyphOverflow, rightGlyphOverflow); 882 883 LayoutUnit logicalTopVisualOverflow = min(textBox->pixelSnappedLogicalTop() + childOverflowLogicalTop, logicalVisualOverflow.y()); 884 LayoutUnit logicalBottomVisualOverflow = max(textBox->pixelSnappedLogicalBottom() + childOverflowLogicalBottom, logicalVisualOverflow.maxY()); 885 LayoutUnit logicalLeftVisualOverflow = min(textBox->pixelSnappedLogicalLeft() + childOverflowLogicalLeft, logicalVisualOverflow.x()); 886 LayoutUnit logicalRightVisualOverflow = max(textBox->pixelSnappedLogicalRight() + childOverflowLogicalRight, logicalVisualOverflow.maxX()); 887 888 logicalVisualOverflow = LayoutRect(logicalLeftVisualOverflow, logicalTopVisualOverflow, 889 logicalRightVisualOverflow - logicalLeftVisualOverflow, logicalBottomVisualOverflow - logicalTopVisualOverflow); 890 891 textBox->setLogicalOverflowRect(logicalVisualOverflow); 892 } 893 894 inline void InlineFlowBox::addReplacedChildOverflow(const InlineBox* inlineBox, LayoutRect& logicalLayoutOverflow, LayoutRect& logicalVisualOverflow) 895 { 896 RenderBox* box = toRenderBox(inlineBox->renderer()); 897 898 // Visual overflow only propagates if the box doesn't have a self-painting layer. This rectangle does not include 899 // transforms or relative positioning (since those objects always have self-painting layers), but it does need to be adjusted 900 // for writing-mode differences. 901 if (!box->hasSelfPaintingLayer()) { 902 LayoutRect childLogicalVisualOverflow = box->logicalVisualOverflowRectForPropagation(renderer()->style()); 903 childLogicalVisualOverflow.move(inlineBox->logicalLeft(), inlineBox->logicalTop()); 904 logicalVisualOverflow.unite(childLogicalVisualOverflow); 905 } 906 907 // Layout overflow internal to the child box only propagates if the child box doesn't have overflow clip set. 908 // Otherwise the child border box propagates as layout overflow. This rectangle must include transforms and relative positioning 909 // and be adjusted for writing-mode differences. 910 LayoutRect childLogicalLayoutOverflow = box->logicalLayoutOverflowRectForPropagation(renderer()->style()); 911 childLogicalLayoutOverflow.move(inlineBox->logicalLeft(), inlineBox->logicalTop()); 912 logicalLayoutOverflow.unite(childLogicalLayoutOverflow); 913 } 914 915 void InlineFlowBox::computeOverflow(LayoutUnit lineTop, LayoutUnit lineBottom, GlyphOverflowAndFallbackFontsMap& textBoxDataMap) 916 { 917 // If we know we have no overflow, we can just bail. 918 if (knownToHaveNoOverflow()) 919 return; 920 921 // Visual overflow just includes overflow for stuff we need to repaint ourselves. Self-painting layers are ignored. 922 // Layout overflow is used to determine scrolling extent, so it still includes child layers and also factors in 923 // transforms, relative positioning, etc. 924 LayoutRect logicalLayoutOverflow(enclosingLayoutRect(logicalFrameRectIncludingLineHeight(lineTop, lineBottom))); 925 LayoutRect logicalVisualOverflow(logicalLayoutOverflow); 926 927 addBoxShadowVisualOverflow(logicalVisualOverflow); 928 addBorderOutsetVisualOverflow(logicalVisualOverflow); 929 930 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { 931 if (curr->renderer()->isOutOfFlowPositioned()) 932 continue; // Positioned placeholders don't affect calculations. 933 934 if (curr->renderer()->isText()) { 935 InlineTextBox* text = toInlineTextBox(curr); 936 RenderText* rt = toRenderText(text->renderer()); 937 if (rt->isBR()) 938 continue; 939 LayoutRect textBoxOverflow(enclosingLayoutRect(text->logicalFrameRect())); 940 addTextBoxVisualOverflow(text, textBoxDataMap, textBoxOverflow); 941 logicalVisualOverflow.unite(textBoxOverflow); 942 } else if (curr->renderer()->isRenderInline()) { 943 InlineFlowBox* flow = toInlineFlowBox(curr); 944 flow->computeOverflow(lineTop, lineBottom, textBoxDataMap); 945 if (!flow->boxModelObject()->hasSelfPaintingLayer()) 946 logicalVisualOverflow.unite(flow->logicalVisualOverflowRect(lineTop, lineBottom)); 947 LayoutRect childLayoutOverflow = flow->logicalLayoutOverflowRect(lineTop, lineBottom); 948 childLayoutOverflow.move(flow->boxModelObject()->relativePositionLogicalOffset()); 949 logicalLayoutOverflow.unite(childLayoutOverflow); 950 } else 951 addReplacedChildOverflow(curr, logicalLayoutOverflow, logicalVisualOverflow); 952 } 953 954 setOverflowFromLogicalRects(logicalLayoutOverflow, logicalVisualOverflow, lineTop, lineBottom); 955 } 956 957 void InlineFlowBox::setLayoutOverflow(const LayoutRect& rect, LayoutUnit lineTop, LayoutUnit lineBottom) 958 { 959 LayoutRect frameBox = enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom)); 960 if (frameBox.contains(rect) || rect.isEmpty()) 961 return; 962 963 if (!m_overflow) 964 m_overflow = adoptPtr(new RenderOverflow(frameBox, frameBox)); 965 966 m_overflow->setLayoutOverflow(rect); 967 } 968 969 void InlineFlowBox::setVisualOverflow(const LayoutRect& rect, LayoutUnit lineTop, LayoutUnit lineBottom) 970 { 971 LayoutRect frameBox = enclosingLayoutRect(frameRectIncludingLineHeight(lineTop, lineBottom)); 972 if (frameBox.contains(rect) || rect.isEmpty()) 973 return; 974 975 if (!m_overflow) 976 m_overflow = adoptPtr(new RenderOverflow(frameBox, frameBox)); 977 978 m_overflow->setVisualOverflow(rect); 979 } 980 981 void InlineFlowBox::setOverflowFromLogicalRects(const LayoutRect& logicalLayoutOverflow, const LayoutRect& logicalVisualOverflow, LayoutUnit lineTop, LayoutUnit lineBottom) 982 { 983 LayoutRect layoutOverflow(isHorizontal() ? logicalLayoutOverflow : logicalLayoutOverflow.transposedRect()); 984 setLayoutOverflow(layoutOverflow, lineTop, lineBottom); 985 986 LayoutRect visualOverflow(isHorizontal() ? logicalVisualOverflow : logicalVisualOverflow.transposedRect()); 987 setVisualOverflow(visualOverflow, lineTop, lineBottom); 988 } 989 990 bool InlineFlowBox::nodeAtPoint(const HitTestRequest& request, HitTestResult& result, const HitTestLocation& locationInContainer, const LayoutPoint& accumulatedOffset, LayoutUnit lineTop, LayoutUnit lineBottom) 991 { 992 LayoutRect overflowRect(visualOverflowRect(lineTop, lineBottom)); 993 flipForWritingMode(overflowRect); 994 overflowRect.moveBy(accumulatedOffset); 995 if (!locationInContainer.intersects(overflowRect)) 996 return false; 997 998 // Check children first. 999 // We need to account for culled inline parents of the hit-tested nodes, so that they may also get included in area-based hit-tests. 1000 RenderObject* culledParent = 0; 1001 for (InlineBox* curr = lastChild(); curr; curr = curr->prevOnLine()) { 1002 if (curr->renderer()->isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) { 1003 RenderObject* newParent = 0; 1004 // Culled parents are only relevant for area-based hit-tests, so ignore it in point-based ones. 1005 if (locationInContainer.isRectBasedTest()) { 1006 newParent = curr->renderer()->parent(); 1007 if (newParent == renderer()) 1008 newParent = 0; 1009 } 1010 // Check the culled parent after all its children have been checked, to do this we wait until 1011 // we are about to test an element with a different parent. 1012 if (newParent != culledParent) { 1013 if (!newParent || !newParent->isDescendantOf(culledParent)) { 1014 while (culledParent && culledParent != renderer() && culledParent != newParent) { 1015 if (culledParent->isRenderInline() && toRenderInline(culledParent)->hitTestCulledInline(request, result, locationInContainer, accumulatedOffset)) 1016 return true; 1017 culledParent = culledParent->parent(); 1018 } 1019 } 1020 culledParent = newParent; 1021 } 1022 if (curr->nodeAtPoint(request, result, locationInContainer, accumulatedOffset, lineTop, lineBottom)) { 1023 renderer()->updateHitTestResult(result, locationInContainer.point() - toLayoutSize(accumulatedOffset)); 1024 return true; 1025 } 1026 } 1027 } 1028 // Check any culled ancestor of the final children tested. 1029 while (culledParent && culledParent != renderer()) { 1030 if (culledParent->isRenderInline() && toRenderInline(culledParent)->hitTestCulledInline(request, result, locationInContainer, accumulatedOffset)) 1031 return true; 1032 culledParent = culledParent->parent(); 1033 } 1034 1035 // Now check ourselves. Pixel snap hit testing. 1036 LayoutRect frameRect = roundedFrameRect(); 1037 LayoutUnit minX = frameRect.x(); 1038 LayoutUnit minY = frameRect.y(); 1039 LayoutUnit width = frameRect.width(); 1040 LayoutUnit height = frameRect.height(); 1041 1042 // Constrain our hit testing to the line top and bottom if necessary. 1043 bool noQuirksMode = renderer()->document()->inNoQuirksMode(); 1044 if (!noQuirksMode && !hasTextChildren() && !(descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) { 1045 RootInlineBox* rootBox = root(); 1046 LayoutUnit& top = isHorizontal() ? minY : minX; 1047 LayoutUnit& logicalHeight = isHorizontal() ? height : width; 1048 LayoutUnit bottom = min(rootBox->lineBottom(), top + logicalHeight); 1049 top = max(rootBox->lineTop(), top); 1050 logicalHeight = bottom - top; 1051 } 1052 1053 // Move x/y to our coordinates. 1054 LayoutRect rect(minX, minY, width, height); 1055 flipForWritingMode(rect); 1056 rect.moveBy(accumulatedOffset); 1057 1058 if (visibleToHitTestRequest(request) && locationInContainer.intersects(rect)) { 1059 renderer()->updateHitTestResult(result, flipForWritingMode(locationInContainer.point() - toLayoutSize(accumulatedOffset))); // Don't add in m_x or m_y here, we want coords in the containing block's space. 1060 if (!result.addNodeToRectBasedTestResult(renderer()->node(), request, locationInContainer, rect)) 1061 return true; 1062 } 1063 1064 return false; 1065 } 1066 1067 void InlineFlowBox::paint(PaintInfo& paintInfo, const LayoutPoint& paintOffset, LayoutUnit lineTop, LayoutUnit lineBottom) 1068 { 1069 LayoutRect overflowRect(visualOverflowRect(lineTop, lineBottom)); 1070 overflowRect.inflate(renderer()->maximalOutlineSize(paintInfo.phase)); 1071 flipForWritingMode(overflowRect); 1072 overflowRect.moveBy(paintOffset); 1073 1074 if (!paintInfo.rect.intersects(pixelSnappedIntRect(overflowRect))) 1075 return; 1076 1077 if (paintInfo.phase != PaintPhaseChildOutlines) { 1078 if (paintInfo.phase == PaintPhaseOutline || paintInfo.phase == PaintPhaseSelfOutline) { 1079 // Add ourselves to the paint info struct's list of inlines that need to paint their 1080 // outlines. 1081 if (renderer()->style()->visibility() == VISIBLE && renderer()->hasOutline() && !isRootInlineBox()) { 1082 RenderInline* inlineFlow = toRenderInline(renderer()); 1083 1084 RenderBlock* cb = 0; 1085 bool containingBlockPaintsContinuationOutline = inlineFlow->continuation() || inlineFlow->isInlineElementContinuation(); 1086 if (containingBlockPaintsContinuationOutline) { 1087 // FIXME: See https://bugs.webkit.org/show_bug.cgi?id=54690. We currently don't reconnect inline continuations 1088 // after a child removal. As a result, those merged inlines do not get seperated and hence not get enclosed by 1089 // anonymous blocks. In this case, it is better to bail out and paint it ourself. 1090 RenderBlock* enclosingAnonymousBlock = renderer()->containingBlock(); 1091 if (!enclosingAnonymousBlock->isAnonymousBlock()) 1092 containingBlockPaintsContinuationOutline = false; 1093 else { 1094 cb = enclosingAnonymousBlock->containingBlock(); 1095 for (RenderBoxModelObject* box = boxModelObject(); box != cb; box = box->parent()->enclosingBoxModelObject()) { 1096 if (box->hasSelfPaintingLayer()) { 1097 containingBlockPaintsContinuationOutline = false; 1098 break; 1099 } 1100 } 1101 } 1102 } 1103 1104 if (containingBlockPaintsContinuationOutline) { 1105 // Add ourselves to the containing block of the entire continuation so that it can 1106 // paint us atomically. 1107 cb->addContinuationWithOutline(toRenderInline(renderer()->node()->renderer())); 1108 } else if (!inlineFlow->isInlineElementContinuation()) 1109 paintInfo.outlineObjects->add(inlineFlow); 1110 } 1111 } else if (paintInfo.phase == PaintPhaseMask) { 1112 paintMask(paintInfo, paintOffset); 1113 return; 1114 } else { 1115 // Paint our background, border and box-shadow. 1116 paintBoxDecorations(paintInfo, paintOffset); 1117 } 1118 } 1119 1120 if (paintInfo.phase == PaintPhaseMask) 1121 return; 1122 1123 PaintPhase paintPhase = paintInfo.phase == PaintPhaseChildOutlines ? PaintPhaseOutline : paintInfo.phase; 1124 PaintInfo childInfo(paintInfo); 1125 childInfo.phase = paintPhase; 1126 childInfo.updatePaintingRootForChildren(renderer()); 1127 1128 // Paint our children. 1129 if (paintPhase != PaintPhaseSelfOutline) { 1130 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { 1131 if (curr->renderer()->isText() || !curr->boxModelObject()->hasSelfPaintingLayer()) 1132 curr->paint(childInfo, paintOffset, lineTop, lineBottom); 1133 } 1134 } 1135 } 1136 1137 void InlineFlowBox::paintFillLayers(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect, CompositeOperator op) 1138 { 1139 if (!fillLayer) 1140 return; 1141 paintFillLayers(paintInfo, c, fillLayer->next(), rect, op); 1142 paintFillLayer(paintInfo, c, fillLayer, rect, op); 1143 } 1144 1145 bool InlineFlowBox::boxShadowCanBeAppliedToBackground(const FillLayer& lastBackgroundLayer) const 1146 { 1147 // The checks here match how paintFillLayer() decides whether to clip (if it does, the shadow 1148 // would be clipped out, so it has to be drawn separately). 1149 StyleImage* image = lastBackgroundLayer.image(); 1150 bool hasFillImage = image && image->canRender(renderer(), renderer()->style()->effectiveZoom()); 1151 return (!hasFillImage && !renderer()->style()->hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent(); 1152 } 1153 1154 void InlineFlowBox::paintFillLayer(const PaintInfo& paintInfo, const Color& c, const FillLayer* fillLayer, const LayoutRect& rect, CompositeOperator op) 1155 { 1156 StyleImage* img = fillLayer->image(); 1157 bool hasFillImage = img && img->canRender(renderer(), renderer()->style()->effectiveZoom()); 1158 if ((!hasFillImage && !renderer()->style()->hasBorderRadius()) || (!prevLineBox() && !nextLineBox()) || !parent()) 1159 boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, rect, BackgroundBleedNone, this, rect.size(), op); 1160 else if (renderer()->style()->boxDecorationBreak() == DCLONE) { 1161 GraphicsContextStateSaver stateSaver(*paintInfo.context); 1162 paintInfo.context->clip(LayoutRect(rect.x(), rect.y(), width(), height())); 1163 boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, rect, BackgroundBleedNone, this, rect.size(), op); 1164 } else { 1165 // We have a fill image that spans multiple lines. 1166 // We need to adjust tx and ty by the width of all previous lines. 1167 // Think of background painting on inlines as though you had one long line, a single continuous 1168 // strip. Even though that strip has been broken up across multiple lines, you still paint it 1169 // as though you had one single line. This means each line has to pick up the background where 1170 // the previous line left off. 1171 LayoutUnit logicalOffsetOnLine = 0; 1172 LayoutUnit totalLogicalWidth; 1173 if (renderer()->style()->direction() == LTR) { 1174 for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox()) 1175 logicalOffsetOnLine += curr->logicalWidth(); 1176 totalLogicalWidth = logicalOffsetOnLine; 1177 for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox()) 1178 totalLogicalWidth += curr->logicalWidth(); 1179 } else { 1180 for (InlineFlowBox* curr = nextLineBox(); curr; curr = curr->nextLineBox()) 1181 logicalOffsetOnLine += curr->logicalWidth(); 1182 totalLogicalWidth = logicalOffsetOnLine; 1183 for (InlineFlowBox* curr = this; curr; curr = curr->prevLineBox()) 1184 totalLogicalWidth += curr->logicalWidth(); 1185 } 1186 LayoutUnit stripX = rect.x() - (isHorizontal() ? logicalOffsetOnLine : LayoutUnit()); 1187 LayoutUnit stripY = rect.y() - (isHorizontal() ? LayoutUnit() : logicalOffsetOnLine); 1188 LayoutUnit stripWidth = isHorizontal() ? totalLogicalWidth : static_cast<LayoutUnit>(width()); 1189 LayoutUnit stripHeight = isHorizontal() ? static_cast<LayoutUnit>(height()) : totalLogicalWidth; 1190 1191 GraphicsContextStateSaver stateSaver(*paintInfo.context); 1192 paintInfo.context->clip(LayoutRect(rect.x(), rect.y(), width(), height())); 1193 boxModelObject()->paintFillLayerExtended(paintInfo, c, fillLayer, LayoutRect(stripX, stripY, stripWidth, stripHeight), BackgroundBleedNone, this, rect.size(), op); 1194 } 1195 } 1196 1197 void InlineFlowBox::paintBoxShadow(const PaintInfo& info, RenderStyle* s, ShadowStyle shadowStyle, const LayoutRect& paintRect) 1198 { 1199 if ((!prevLineBox() && !nextLineBox()) || !parent()) 1200 boxModelObject()->paintBoxShadow(info, paintRect, s, shadowStyle); 1201 else { 1202 // FIXME: We can do better here in the multi-line case. We want to push a clip so that the shadow doesn't 1203 // protrude incorrectly at the edges, and we want to possibly include shadows cast from the previous/following lines 1204 boxModelObject()->paintBoxShadow(info, paintRect, s, shadowStyle, includeLogicalLeftEdge(), includeLogicalRightEdge()); 1205 } 1206 } 1207 1208 void InlineFlowBox::constrainToLineTopAndBottomIfNeeded(LayoutRect& rect) const 1209 { 1210 bool noQuirksMode = renderer()->document()->inNoQuirksMode(); 1211 if (!noQuirksMode && !hasTextChildren() && !(descendantsHaveSameLineHeightAndBaseline() && hasTextDescendants())) { 1212 const RootInlineBox* rootBox = root(); 1213 LayoutUnit logicalTop = isHorizontal() ? rect.y() : rect.x(); 1214 LayoutUnit logicalHeight = isHorizontal() ? rect.height() : rect.width(); 1215 LayoutUnit bottom = min(rootBox->lineBottom(), logicalTop + logicalHeight); 1216 logicalTop = max(rootBox->lineTop(), logicalTop); 1217 logicalHeight = bottom - logicalTop; 1218 if (isHorizontal()) { 1219 rect.setY(logicalTop); 1220 rect.setHeight(logicalHeight); 1221 } else { 1222 rect.setX(logicalTop); 1223 rect.setWidth(logicalHeight); 1224 } 1225 } 1226 } 1227 1228 static LayoutRect clipRectForNinePieceImageStrip(InlineFlowBox* box, const NinePieceImage& image, const LayoutRect& paintRect) 1229 { 1230 LayoutRect clipRect(paintRect); 1231 RenderStyle* style = box->renderer()->style(); 1232 LayoutBoxExtent outsets = style->imageOutsets(image); 1233 if (box->isHorizontal()) { 1234 clipRect.setY(paintRect.y() - outsets.top()); 1235 clipRect.setHeight(paintRect.height() + outsets.top() + outsets.bottom()); 1236 if (box->includeLogicalLeftEdge()) { 1237 clipRect.setX(paintRect.x() - outsets.left()); 1238 clipRect.setWidth(paintRect.width() + outsets.left()); 1239 } 1240 if (box->includeLogicalRightEdge()) 1241 clipRect.setWidth(clipRect.width() + outsets.right()); 1242 } else { 1243 clipRect.setX(paintRect.x() - outsets.left()); 1244 clipRect.setWidth(paintRect.width() + outsets.left() + outsets.right()); 1245 if (box->includeLogicalLeftEdge()) { 1246 clipRect.setY(paintRect.y() - outsets.top()); 1247 clipRect.setHeight(paintRect.height() + outsets.top()); 1248 } 1249 if (box->includeLogicalRightEdge()) 1250 clipRect.setHeight(clipRect.height() + outsets.bottom()); 1251 } 1252 return clipRect; 1253 } 1254 1255 void InlineFlowBox::paintBoxDecorations(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1256 { 1257 if (!paintInfo.shouldPaintWithinRoot(renderer()) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseForeground) 1258 return; 1259 1260 // Pixel snap background/border painting. 1261 LayoutRect frameRect = roundedFrameRect(); 1262 1263 constrainToLineTopAndBottomIfNeeded(frameRect); 1264 1265 // Move x/y to our coordinates. 1266 LayoutRect localRect(frameRect); 1267 flipForWritingMode(localRect); 1268 LayoutPoint adjustedPaintoffset = paintOffset + localRect.location(); 1269 1270 GraphicsContext* context = paintInfo.context; 1271 1272 // You can use p::first-line to specify a background. If so, the root line boxes for 1273 // a line may actually have to paint a background. 1274 RenderStyle* styleToUse = renderer()->style(isFirstLineStyle()); 1275 if ((!parent() && isFirstLineStyle() && styleToUse != renderer()->style()) || (parent() && renderer()->hasBoxDecorations())) { 1276 LayoutRect paintRect = LayoutRect(adjustedPaintoffset, frameRect.size()); 1277 // Shadow comes first and is behind the background and border. 1278 if (!boxModelObject()->boxShadowShouldBeAppliedToBackground(BackgroundBleedNone, this)) 1279 paintBoxShadow(paintInfo, styleToUse, Normal, paintRect); 1280 1281 Color c = renderer()->resolveColor(styleToUse, CSSPropertyBackgroundColor); 1282 paintFillLayers(paintInfo, c, styleToUse->backgroundLayers(), paintRect); 1283 paintBoxShadow(paintInfo, styleToUse, Inset, paintRect); 1284 1285 // :first-line cannot be used to put borders on a line. Always paint borders with our 1286 // non-first-line style. 1287 if (parent() && renderer()->style()->hasBorder()) { 1288 const NinePieceImage& borderImage = renderer()->style()->borderImage(); 1289 StyleImage* borderImageSource = borderImage.image(); 1290 bool hasBorderImage = borderImageSource && borderImageSource->canRender(renderer(), styleToUse->effectiveZoom()); 1291 if (hasBorderImage && !borderImageSource->isLoaded()) 1292 return; // Don't paint anything while we wait for the image to load. 1293 1294 // The simple case is where we either have no border image or we are the only box for this object. In those 1295 // cases only a single call to draw is required. 1296 if (!hasBorderImage || (!prevLineBox() && !nextLineBox())) 1297 boxModelObject()->paintBorder(paintInfo, paintRect, renderer()->style(isFirstLineStyle()), BackgroundBleedNone, includeLogicalLeftEdge(), includeLogicalRightEdge()); 1298 else { 1299 // We have a border image that spans multiple lines. 1300 // We need to adjust tx and ty by the width of all previous lines. 1301 // Think of border image painting on inlines as though you had one long line, a single continuous 1302 // strip. Even though that strip has been broken up across multiple lines, you still paint it 1303 // as though you had one single line. This means each line has to pick up the image where 1304 // the previous line left off. 1305 // FIXME: What the heck do we do with RTL here? The math we're using is obviously not right, 1306 // but it isn't even clear how this should work at all. 1307 LayoutUnit logicalOffsetOnLine = 0; 1308 for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox()) 1309 logicalOffsetOnLine += curr->logicalWidth(); 1310 LayoutUnit totalLogicalWidth = logicalOffsetOnLine; 1311 for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox()) 1312 totalLogicalWidth += curr->logicalWidth(); 1313 LayoutUnit stripX = adjustedPaintoffset.x() - (isHorizontal() ? logicalOffsetOnLine : LayoutUnit()); 1314 LayoutUnit stripY = adjustedPaintoffset.y() - (isHorizontal() ? LayoutUnit() : logicalOffsetOnLine); 1315 LayoutUnit stripWidth = isHorizontal() ? totalLogicalWidth : frameRect.width(); 1316 LayoutUnit stripHeight = isHorizontal() ? frameRect.height() : totalLogicalWidth; 1317 1318 LayoutRect clipRect = clipRectForNinePieceImageStrip(this, borderImage, paintRect); 1319 GraphicsContextStateSaver stateSaver(*context); 1320 context->clip(clipRect); 1321 boxModelObject()->paintBorder(paintInfo, LayoutRect(stripX, stripY, stripWidth, stripHeight), renderer()->style(isFirstLineStyle())); 1322 } 1323 } 1324 } 1325 } 1326 1327 void InlineFlowBox::paintMask(PaintInfo& paintInfo, const LayoutPoint& paintOffset) 1328 { 1329 if (!paintInfo.shouldPaintWithinRoot(renderer()) || renderer()->style()->visibility() != VISIBLE || paintInfo.phase != PaintPhaseMask) 1330 return; 1331 1332 // Pixel snap mask painting. 1333 LayoutRect frameRect = roundedFrameRect(); 1334 1335 constrainToLineTopAndBottomIfNeeded(frameRect); 1336 1337 // Move x/y to our coordinates. 1338 LayoutRect localRect(frameRect); 1339 flipForWritingMode(localRect); 1340 LayoutPoint adjustedPaintOffset = paintOffset + localRect.location(); 1341 1342 const NinePieceImage& maskNinePieceImage = renderer()->style()->maskBoxImage(); 1343 StyleImage* maskBoxImage = renderer()->style()->maskBoxImage().image(); 1344 1345 // Figure out if we need to push a transparency layer to render our mask. 1346 bool pushTransparencyLayer = false; 1347 bool compositedMask = renderer()->hasLayer() && boxModelObject()->layer()->hasCompositedMask(); 1348 bool flattenCompositingLayers = renderer()->view()->frameView() && renderer()->view()->frameView()->paintBehavior() & PaintBehaviorFlattenCompositingLayers; 1349 CompositeOperator compositeOp = CompositeSourceOver; 1350 if (!compositedMask || flattenCompositingLayers) { 1351 if ((maskBoxImage && renderer()->style()->maskLayers()->hasImage()) || renderer()->style()->maskLayers()->next()) 1352 pushTransparencyLayer = true; 1353 1354 compositeOp = CompositeDestinationIn; 1355 if (pushTransparencyLayer) { 1356 paintInfo.context->setCompositeOperation(CompositeDestinationIn); 1357 paintInfo.context->beginTransparencyLayer(1.0f); 1358 compositeOp = CompositeSourceOver; 1359 } 1360 } 1361 1362 LayoutRect paintRect = LayoutRect(adjustedPaintOffset, frameRect.size()); 1363 paintFillLayers(paintInfo, Color(), renderer()->style()->maskLayers(), paintRect, compositeOp); 1364 1365 bool hasBoxImage = maskBoxImage && maskBoxImage->canRender(renderer(), renderer()->style()->effectiveZoom()); 1366 if (!hasBoxImage || !maskBoxImage->isLoaded()) { 1367 if (pushTransparencyLayer) 1368 paintInfo.context->endTransparencyLayer(); 1369 return; // Don't paint anything while we wait for the image to load. 1370 } 1371 1372 // The simple case is where we are the only box for this object. In those 1373 // cases only a single call to draw is required. 1374 if (!prevLineBox() && !nextLineBox()) { 1375 boxModelObject()->paintNinePieceImage(paintInfo.context, LayoutRect(adjustedPaintOffset, frameRect.size()), renderer()->style(), maskNinePieceImage, compositeOp); 1376 } else { 1377 // We have a mask image that spans multiple lines. 1378 // We need to adjust _tx and _ty by the width of all previous lines. 1379 LayoutUnit logicalOffsetOnLine = 0; 1380 for (InlineFlowBox* curr = prevLineBox(); curr; curr = curr->prevLineBox()) 1381 logicalOffsetOnLine += curr->logicalWidth(); 1382 LayoutUnit totalLogicalWidth = logicalOffsetOnLine; 1383 for (InlineFlowBox* curr = this; curr; curr = curr->nextLineBox()) 1384 totalLogicalWidth += curr->logicalWidth(); 1385 LayoutUnit stripX = adjustedPaintOffset.x() - (isHorizontal() ? logicalOffsetOnLine : LayoutUnit()); 1386 LayoutUnit stripY = adjustedPaintOffset.y() - (isHorizontal() ? LayoutUnit() : logicalOffsetOnLine); 1387 LayoutUnit stripWidth = isHorizontal() ? totalLogicalWidth : frameRect.width(); 1388 LayoutUnit stripHeight = isHorizontal() ? frameRect.height() : totalLogicalWidth; 1389 1390 LayoutRect clipRect = clipRectForNinePieceImageStrip(this, maskNinePieceImage, paintRect); 1391 GraphicsContextStateSaver stateSaver(*paintInfo.context); 1392 paintInfo.context->clip(clipRect); 1393 boxModelObject()->paintNinePieceImage(paintInfo.context, LayoutRect(stripX, stripY, stripWidth, stripHeight), renderer()->style(), maskNinePieceImage, compositeOp); 1394 } 1395 1396 if (pushTransparencyLayer) 1397 paintInfo.context->endTransparencyLayer(); 1398 } 1399 1400 InlineBox* InlineFlowBox::firstLeafChild() const 1401 { 1402 InlineBox* leaf = 0; 1403 for (InlineBox* child = firstChild(); child && !leaf; child = child->nextOnLine()) 1404 leaf = child->isLeaf() ? child : toInlineFlowBox(child)->firstLeafChild(); 1405 return leaf; 1406 } 1407 1408 InlineBox* InlineFlowBox::lastLeafChild() const 1409 { 1410 InlineBox* leaf = 0; 1411 for (InlineBox* child = lastChild(); child && !leaf; child = child->prevOnLine()) 1412 leaf = child->isLeaf() ? child : toInlineFlowBox(child)->lastLeafChild(); 1413 return leaf; 1414 } 1415 1416 RenderObject::SelectionState InlineFlowBox::selectionState() 1417 { 1418 return RenderObject::SelectionNone; 1419 } 1420 1421 bool InlineFlowBox::canAccommodateEllipsis(bool ltr, int blockEdge, int ellipsisWidth) const 1422 { 1423 for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) { 1424 if (!box->canAccommodateEllipsis(ltr, blockEdge, ellipsisWidth)) 1425 return false; 1426 } 1427 return true; 1428 } 1429 1430 float InlineFlowBox::placeEllipsisBox(bool ltr, float blockLeftEdge, float blockRightEdge, float ellipsisWidth, float &truncatedWidth, bool& foundBox) 1431 { 1432 float result = -1; 1433 // We iterate over all children, the foundBox variable tells us when we've found the 1434 // box containing the ellipsis. All boxes after that one in the flow are hidden. 1435 // If our flow is ltr then iterate over the boxes from left to right, otherwise iterate 1436 // from right to left. Varying the order allows us to correctly hide the boxes following the ellipsis. 1437 InlineBox* box = ltr ? firstChild() : lastChild(); 1438 1439 // NOTE: these will cross after foundBox = true. 1440 int visibleLeftEdge = blockLeftEdge; 1441 int visibleRightEdge = blockRightEdge; 1442 1443 while (box) { 1444 int currResult = box->placeEllipsisBox(ltr, visibleLeftEdge, visibleRightEdge, ellipsisWidth, truncatedWidth, foundBox); 1445 if (currResult != -1 && result == -1) 1446 result = currResult; 1447 1448 if (ltr) { 1449 visibleLeftEdge += box->logicalWidth(); 1450 box = box->nextOnLine(); 1451 } 1452 else { 1453 visibleRightEdge -= box->logicalWidth(); 1454 box = box->prevOnLine(); 1455 } 1456 } 1457 return result; 1458 } 1459 1460 void InlineFlowBox::clearTruncation() 1461 { 1462 for (InlineBox *box = firstChild(); box; box = box->nextOnLine()) 1463 box->clearTruncation(); 1464 } 1465 1466 LayoutUnit InlineFlowBox::computeOverAnnotationAdjustment(LayoutUnit allowedPosition) const 1467 { 1468 LayoutUnit result = 0; 1469 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { 1470 if (curr->renderer()->isOutOfFlowPositioned()) 1471 continue; // Positioned placeholders don't affect calculations. 1472 1473 if (curr->isInlineFlowBox()) 1474 result = max(result, toInlineFlowBox(curr)->computeOverAnnotationAdjustment(allowedPosition)); 1475 1476 if (curr->renderer()->isReplaced() && curr->renderer()->isRubyRun() && curr->renderer()->style()->rubyPosition() == RubyPositionBefore) { 1477 RenderRubyRun* rubyRun = toRenderRubyRun(curr->renderer()); 1478 RenderRubyText* rubyText = rubyRun->rubyText(); 1479 if (!rubyText) 1480 continue; 1481 1482 if (!rubyRun->style()->isFlippedLinesWritingMode()) { 1483 LayoutUnit topOfFirstRubyTextLine = rubyText->logicalTop() + (rubyText->firstRootBox() ? rubyText->firstRootBox()->lineTop() : LayoutUnit()); 1484 if (topOfFirstRubyTextLine >= 0) 1485 continue; 1486 topOfFirstRubyTextLine += curr->logicalTop(); 1487 result = max(result, allowedPosition - topOfFirstRubyTextLine); 1488 } else { 1489 LayoutUnit bottomOfLastRubyTextLine = rubyText->logicalTop() + (rubyText->lastRootBox() ? rubyText->lastRootBox()->lineBottom() : rubyText->logicalHeight()); 1490 if (bottomOfLastRubyTextLine <= curr->logicalHeight()) 1491 continue; 1492 bottomOfLastRubyTextLine += curr->logicalTop(); 1493 result = max(result, bottomOfLastRubyTextLine - allowedPosition); 1494 } 1495 } 1496 1497 if (curr->isInlineTextBox()) { 1498 RenderStyle* style = curr->renderer()->style(isFirstLineStyle()); 1499 TextEmphasisPosition emphasisMarkPosition; 1500 if (style->textEmphasisMark() != TextEmphasisMarkNone && toInlineTextBox(curr)->getEmphasisMarkPosition(style, emphasisMarkPosition) && emphasisMarkPosition == TextEmphasisPositionOver) { 1501 if (!style->isFlippedLinesWritingMode()) { 1502 int topOfEmphasisMark = curr->logicalTop() - style->font().emphasisMarkHeight(style->textEmphasisMarkString()); 1503 result = max(result, allowedPosition - topOfEmphasisMark); 1504 } else { 1505 int bottomOfEmphasisMark = curr->logicalBottom() + style->font().emphasisMarkHeight(style->textEmphasisMarkString()); 1506 result = max(result, bottomOfEmphasisMark - allowedPosition); 1507 } 1508 } 1509 } 1510 } 1511 return result; 1512 } 1513 1514 LayoutUnit InlineFlowBox::computeUnderAnnotationAdjustment(LayoutUnit allowedPosition) const 1515 { 1516 LayoutUnit result = 0; 1517 for (InlineBox* curr = firstChild(); curr; curr = curr->nextOnLine()) { 1518 if (curr->renderer()->isOutOfFlowPositioned()) 1519 continue; // Positioned placeholders don't affect calculations. 1520 1521 if (curr->isInlineFlowBox()) 1522 result = max(result, toInlineFlowBox(curr)->computeUnderAnnotationAdjustment(allowedPosition)); 1523 1524 if (curr->renderer()->isReplaced() && curr->renderer()->isRubyRun() && curr->renderer()->style()->rubyPosition() == RubyPositionAfter) { 1525 RenderRubyRun* rubyRun = toRenderRubyRun(curr->renderer()); 1526 RenderRubyText* rubyText = rubyRun->rubyText(); 1527 if (!rubyText) 1528 continue; 1529 1530 if (rubyRun->style()->isFlippedLinesWritingMode()) { 1531 LayoutUnit topOfFirstRubyTextLine = rubyText->logicalTop() + (rubyText->firstRootBox() ? rubyText->firstRootBox()->lineTop() : LayoutUnit()); 1532 if (topOfFirstRubyTextLine >= 0) 1533 continue; 1534 topOfFirstRubyTextLine += curr->logicalTop(); 1535 result = max(result, allowedPosition - topOfFirstRubyTextLine); 1536 } else { 1537 LayoutUnit bottomOfLastRubyTextLine = rubyText->logicalTop() + (rubyText->lastRootBox() ? rubyText->lastRootBox()->lineBottom() : rubyText->logicalHeight()); 1538 if (bottomOfLastRubyTextLine <= curr->logicalHeight()) 1539 continue; 1540 bottomOfLastRubyTextLine += curr->logicalTop(); 1541 result = max(result, bottomOfLastRubyTextLine - allowedPosition); 1542 } 1543 } 1544 1545 if (curr->isInlineTextBox()) { 1546 RenderStyle* style = curr->renderer()->style(isFirstLineStyle()); 1547 if (style->textEmphasisMark() != TextEmphasisMarkNone && style->textEmphasisPosition() == TextEmphasisPositionUnder) { 1548 if (!style->isFlippedLinesWritingMode()) { 1549 LayoutUnit bottomOfEmphasisMark = curr->logicalBottom() + style->font().emphasisMarkHeight(style->textEmphasisMarkString()); 1550 result = max(result, bottomOfEmphasisMark - allowedPosition); 1551 } else { 1552 LayoutUnit topOfEmphasisMark = curr->logicalTop() - style->font().emphasisMarkHeight(style->textEmphasisMarkString()); 1553 result = max(result, allowedPosition - topOfEmphasisMark); 1554 } 1555 } 1556 } 1557 } 1558 return result; 1559 } 1560 1561 void InlineFlowBox::collectLeafBoxesInLogicalOrder(Vector<InlineBox*>& leafBoxesInLogicalOrder, CustomInlineBoxRangeReverse customReverseImplementation, void* userData) const 1562 { 1563 InlineBox* leaf = firstLeafChild(); 1564 1565 // FIXME: The reordering code is a copy of parts from BidiResolver::createBidiRunsForLine, operating directly on InlineBoxes, instead of BidiRuns. 1566 // Investigate on how this code could possibly be shared. 1567 unsigned char minLevel = 128; 1568 unsigned char maxLevel = 0; 1569 1570 // First find highest and lowest levels, and initialize leafBoxesInLogicalOrder with the leaf boxes in visual order. 1571 for (; leaf; leaf = leaf->nextLeafChild()) { 1572 minLevel = min(minLevel, leaf->bidiLevel()); 1573 maxLevel = max(maxLevel, leaf->bidiLevel()); 1574 leafBoxesInLogicalOrder.append(leaf); 1575 } 1576 1577 if (renderer()->style()->rtlOrdering() == VisualOrder) 1578 return; 1579 1580 // Reverse of reordering of the line (L2 according to Bidi spec): 1581 // L2. From the highest level found in the text to the lowest odd level on each line, 1582 // reverse any contiguous sequence of characters that are at that level or higher. 1583 1584 // Reversing the reordering of the line is only done up to the lowest odd level. 1585 if (!(minLevel % 2)) 1586 ++minLevel; 1587 1588 Vector<InlineBox*>::iterator end = leafBoxesInLogicalOrder.end(); 1589 while (minLevel <= maxLevel) { 1590 Vector<InlineBox*>::iterator it = leafBoxesInLogicalOrder.begin(); 1591 while (it != end) { 1592 while (it != end) { 1593 if ((*it)->bidiLevel() >= minLevel) 1594 break; 1595 ++it; 1596 } 1597 Vector<InlineBox*>::iterator first = it; 1598 while (it != end) { 1599 if ((*it)->bidiLevel() < minLevel) 1600 break; 1601 ++it; 1602 } 1603 Vector<InlineBox*>::iterator last = it; 1604 if (customReverseImplementation) { 1605 ASSERT(userData); 1606 (*customReverseImplementation)(userData, first, last); 1607 } else 1608 std::reverse(first, last); 1609 } 1610 ++minLevel; 1611 } 1612 } 1613 1614 #ifndef NDEBUG 1615 1616 const char* InlineFlowBox::boxName() const 1617 { 1618 return "InlineFlowBox"; 1619 } 1620 1621 void InlineFlowBox::showLineTreeAndMark(const InlineBox* markedBox1, const char* markedLabel1, const InlineBox* markedBox2, const char* markedLabel2, const RenderObject* obj, int depth) const 1622 { 1623 InlineBox::showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, obj, depth); 1624 for (const InlineBox* box = firstChild(); box; box = box->nextOnLine()) 1625 box->showLineTreeAndMark(markedBox1, markedLabel1, markedBox2, markedLabel2, obj, depth + 1); 1626 } 1627 1628 void InlineFlowBox::checkConsistency() const 1629 { 1630 #ifdef CHECK_CONSISTENCY 1631 ASSERT(!m_hasBadChildList); 1632 const InlineBox* prev = 0; 1633 for (const InlineBox* child = m_firstChild; child; child = child->nextOnLine()) { 1634 ASSERT(child->parent() == this); 1635 ASSERT(child->prevOnLine() == prev); 1636 prev = child; 1637 } 1638 ASSERT(prev == m_lastChild); 1639 #endif 1640 } 1641 1642 #endif 1643 1644 } // namespace WebCore 1645