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