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