1 /* 2 * This file is part of the render object implementation for KHTML. 3 * 4 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 5 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 6 * Copyright (C) 2003 Apple Computer, Inc. 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 * 23 */ 24 25 #include "config.h" 26 #include "core/rendering/RenderDeprecatedFlexibleBox.h" 27 28 #include "core/frame/UseCounter.h" 29 #include "core/rendering/RenderLayer.h" 30 #include "core/rendering/RenderView.h" 31 #include "core/rendering/TextAutosizer.h" 32 #include "core/rendering/TextRunConstructor.h" 33 #include "platform/fonts/Font.h" 34 #include "wtf/StdLibExtras.h" 35 #include "wtf/unicode/CharacterNames.h" 36 37 namespace blink { 38 39 class FlexBoxIterator { 40 public: 41 FlexBoxIterator(RenderDeprecatedFlexibleBox* parent) 42 : m_box(parent) 43 , m_largestOrdinal(1) 44 { 45 if (m_box->style()->boxOrient() == HORIZONTAL && !m_box->style()->isLeftToRightDirection()) 46 m_forward = m_box->style()->boxDirection() != BNORMAL; 47 else 48 m_forward = m_box->style()->boxDirection() == BNORMAL; 49 if (!m_forward) { 50 // No choice, since we're going backwards, we have to find out the highest ordinal up front. 51 RenderBox* child = m_box->firstChildBox(); 52 while (child) { 53 if (child->style()->boxOrdinalGroup() > m_largestOrdinal) 54 m_largestOrdinal = child->style()->boxOrdinalGroup(); 55 child = child->nextSiblingBox(); 56 } 57 } 58 59 reset(); 60 } 61 62 void reset() 63 { 64 m_currentChild = 0; 65 m_ordinalIteration = -1; 66 } 67 68 RenderBox* first() 69 { 70 reset(); 71 return next(); 72 } 73 74 RenderBox* next() 75 { 76 do { 77 if (!m_currentChild) { 78 ++m_ordinalIteration; 79 80 if (!m_ordinalIteration) 81 m_currentOrdinal = m_forward ? 1 : m_largestOrdinal; 82 else { 83 if (static_cast<size_t>(m_ordinalIteration) >= m_ordinalValues.size() + 1) 84 return 0; 85 86 // Only copy+sort the values once per layout even if the iterator is reset. 87 if (m_ordinalValues.size() != m_sortedOrdinalValues.size()) { 88 copyToVector(m_ordinalValues, m_sortedOrdinalValues); 89 std::sort(m_sortedOrdinalValues.begin(), m_sortedOrdinalValues.end()); 90 } 91 m_currentOrdinal = m_forward ? m_sortedOrdinalValues[m_ordinalIteration - 1] : m_sortedOrdinalValues[m_sortedOrdinalValues.size() - m_ordinalIteration]; 92 } 93 94 m_currentChild = m_forward ? m_box->firstChildBox() : m_box->lastChildBox(); 95 } else 96 m_currentChild = m_forward ? m_currentChild->nextSiblingBox() : m_currentChild->previousSiblingBox(); 97 98 if (m_currentChild && notFirstOrdinalValue()) 99 m_ordinalValues.add(m_currentChild->style()->boxOrdinalGroup()); 100 } while (!m_currentChild || (!m_currentChild->isAnonymous() 101 && m_currentChild->style()->boxOrdinalGroup() != m_currentOrdinal)); 102 return m_currentChild; 103 } 104 105 private: 106 bool notFirstOrdinalValue() 107 { 108 unsigned int firstOrdinalValue = m_forward ? 1 : m_largestOrdinal; 109 return m_currentOrdinal == firstOrdinalValue && m_currentChild->style()->boxOrdinalGroup() != firstOrdinalValue; 110 } 111 112 RenderDeprecatedFlexibleBox* m_box; 113 RenderBox* m_currentChild; 114 bool m_forward; 115 unsigned int m_currentOrdinal; 116 unsigned int m_largestOrdinal; 117 HashSet<unsigned int> m_ordinalValues; 118 Vector<unsigned int> m_sortedOrdinalValues; 119 int m_ordinalIteration; 120 }; 121 122 RenderDeprecatedFlexibleBox::RenderDeprecatedFlexibleBox(Element& element) 123 : RenderBlock(&element) 124 { 125 ASSERT(!childrenInline()); 126 m_stretchingChildren = false; 127 if (!isAnonymous()) { 128 const KURL& url = document().url(); 129 if (url.protocolIs("chrome")) 130 UseCounter::count(document(), UseCounter::DeprecatedFlexboxChrome); 131 else if (url.protocolIs("chrome-extension")) 132 UseCounter::count(document(), UseCounter::DeprecatedFlexboxChromeExtension); 133 else 134 UseCounter::count(document(), UseCounter::DeprecatedFlexboxWebContent); 135 } 136 } 137 138 RenderDeprecatedFlexibleBox::~RenderDeprecatedFlexibleBox() 139 { 140 } 141 142 static LayoutUnit marginWidthForChild(RenderBox* child) 143 { 144 // A margin basically has three types: fixed, percentage, and auto (variable). 145 // Auto and percentage margins simply become 0 when computing min/max width. 146 // Fixed margins can be added in as is. 147 Length marginLeft = child->style()->marginLeft(); 148 Length marginRight = child->style()->marginRight(); 149 LayoutUnit margin = 0; 150 if (marginLeft.isFixed()) 151 margin += marginLeft.value(); 152 if (marginRight.isFixed()) 153 margin += marginRight.value(); 154 return margin; 155 } 156 157 static bool childDoesNotAffectWidthOrFlexing(RenderObject* child) 158 { 159 // Positioned children and collapsed children don't affect the min/max width. 160 return child->isOutOfFlowPositioned() || child->style()->visibility() == COLLAPSE; 161 } 162 163 static LayoutUnit contentWidthForChild(RenderBox* child) 164 { 165 if (child->hasOverrideWidth()) 166 return child->overrideLogicalContentWidth(); 167 return child->logicalWidth() - child->borderAndPaddingLogicalWidth(); 168 } 169 170 static LayoutUnit contentHeightForChild(RenderBox* child) 171 { 172 if (child->hasOverrideHeight()) 173 return child->overrideLogicalContentHeight(); 174 return child->logicalHeight() - child->borderAndPaddingLogicalHeight(); 175 } 176 177 void RenderDeprecatedFlexibleBox::styleWillChange(StyleDifference diff, const RenderStyle& newStyle) 178 { 179 RenderStyle* oldStyle = style(); 180 if (oldStyle && !oldStyle->lineClamp().isNone() && newStyle.lineClamp().isNone()) 181 clearLineClamp(); 182 183 RenderBlock::styleWillChange(diff, newStyle); 184 } 185 186 void RenderDeprecatedFlexibleBox::computeIntrinsicLogicalWidths(LayoutUnit& minLogicalWidth, LayoutUnit& maxLogicalWidth) const 187 { 188 if (hasMultipleLines() || isVertical()) { 189 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { 190 if (childDoesNotAffectWidthOrFlexing(child)) 191 continue; 192 193 LayoutUnit margin = marginWidthForChild(child); 194 LayoutUnit width = child->minPreferredLogicalWidth() + margin; 195 minLogicalWidth = std::max(width, minLogicalWidth); 196 197 width = child->maxPreferredLogicalWidth() + margin; 198 maxLogicalWidth = std::max(width, maxLogicalWidth); 199 } 200 } else { 201 for (RenderBox* child = firstChildBox(); child; child = child->nextSiblingBox()) { 202 if (childDoesNotAffectWidthOrFlexing(child)) 203 continue; 204 205 LayoutUnit margin = marginWidthForChild(child); 206 minLogicalWidth += child->minPreferredLogicalWidth() + margin; 207 maxLogicalWidth += child->maxPreferredLogicalWidth() + margin; 208 } 209 } 210 211 maxLogicalWidth = std::max(minLogicalWidth, maxLogicalWidth); 212 213 LayoutUnit scrollbarWidth = instrinsicScrollbarLogicalWidth(); 214 maxLogicalWidth += scrollbarWidth; 215 minLogicalWidth += scrollbarWidth; 216 } 217 218 void RenderDeprecatedFlexibleBox::computePreferredLogicalWidths() 219 { 220 ASSERT(preferredLogicalWidthsDirty()); 221 222 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = 0; 223 RenderStyle* styleToUse = style(); 224 225 if (styleToUse->width().isFixed() && styleToUse->width().value() > 0) 226 m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = adjustContentBoxLogicalWidthForBoxSizing(styleToUse->width().value()); 227 else 228 computeIntrinsicLogicalWidths(m_minPreferredLogicalWidth, m_maxPreferredLogicalWidth); 229 230 if (styleToUse->minWidth().isFixed() && styleToUse->minWidth().value() > 0) { 231 m_maxPreferredLogicalWidth = std::max(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->minWidth().value())); 232 m_minPreferredLogicalWidth = std::max(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->minWidth().value())); 233 } 234 235 if (styleToUse->maxWidth().isFixed()) { 236 m_maxPreferredLogicalWidth = std::min(m_maxPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->maxWidth().value())); 237 m_minPreferredLogicalWidth = std::min(m_minPreferredLogicalWidth, adjustContentBoxLogicalWidthForBoxSizing(styleToUse->maxWidth().value())); 238 } 239 240 LayoutUnit borderAndPadding = borderAndPaddingLogicalWidth(); 241 m_minPreferredLogicalWidth += borderAndPadding; 242 m_maxPreferredLogicalWidth += borderAndPadding; 243 244 clearPreferredLogicalWidthsDirty(); 245 } 246 247 void RenderDeprecatedFlexibleBox::layoutBlock(bool relayoutChildren) 248 { 249 ASSERT(needsLayout()); 250 251 if (!relayoutChildren && simplifiedLayout()) 252 return; 253 254 { 255 // LayoutState needs this deliberate scope to pop before paint invalidation. 256 LayoutState state(*this, locationOffset()); 257 258 LayoutSize previousSize = size(); 259 260 updateLogicalWidth(); 261 updateLogicalHeight(); 262 263 TextAutosizer::LayoutScope textAutosizerLayoutScope(this); 264 265 if (previousSize != size() 266 || (parent()->isDeprecatedFlexibleBox() && parent()->style()->boxOrient() == HORIZONTAL 267 && parent()->style()->boxAlign() == BSTRETCH)) 268 relayoutChildren = true; 269 270 setHeight(0); 271 272 m_stretchingChildren = false; 273 274 if (isHorizontal()) 275 layoutHorizontalBox(relayoutChildren); 276 else 277 layoutVerticalBox(relayoutChildren); 278 279 LayoutUnit oldClientAfterEdge = clientLogicalBottom(); 280 updateLogicalHeight(); 281 282 if (previousSize.height() != height()) 283 relayoutChildren = true; 284 285 layoutPositionedObjects(relayoutChildren || isDocumentElement()); 286 287 computeOverflow(oldClientAfterEdge); 288 } 289 290 updateLayerTransformAfterLayout(); 291 292 if (view()->layoutState()->pageLogicalHeight()) 293 setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(*this, logicalTop())); 294 295 // Update our scrollbars if we're overflow:auto/scroll/hidden now that we know if 296 // we overflow or not. 297 if (hasOverflowClip()) 298 layer()->scrollableArea()->updateAfterLayout(); 299 300 clearNeedsLayout(); 301 } 302 303 // The first walk over our kids is to find out if we have any flexible children. 304 static void gatherFlexChildrenInfo(FlexBoxIterator& iterator, bool relayoutChildren, unsigned int& highestFlexGroup, unsigned int& lowestFlexGroup, bool& haveFlex) 305 { 306 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 307 // Check to see if this child flexes. 308 if (!childDoesNotAffectWidthOrFlexing(child) && child->style()->boxFlex() > 0.0f) { 309 // We always have to lay out flexible objects again, since the flex distribution 310 // may have changed, and we need to reallocate space. 311 child->clearOverrideSize(); 312 if (!relayoutChildren) 313 child->setChildNeedsLayout(MarkOnlyThis); 314 haveFlex = true; 315 unsigned int flexGroup = child->style()->boxFlexGroup(); 316 if (lowestFlexGroup == 0) 317 lowestFlexGroup = flexGroup; 318 if (flexGroup < lowestFlexGroup) 319 lowestFlexGroup = flexGroup; 320 if (flexGroup > highestFlexGroup) 321 highestFlexGroup = flexGroup; 322 } 323 } 324 } 325 326 void RenderDeprecatedFlexibleBox::layoutHorizontalBox(bool relayoutChildren) 327 { 328 LayoutUnit toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); 329 LayoutUnit yPos = borderTop() + paddingTop(); 330 LayoutUnit xPos = borderLeft() + paddingLeft(); 331 bool heightSpecified = false; 332 LayoutUnit oldHeight = 0; 333 334 LayoutUnit remainingSpace = 0; 335 336 337 FlexBoxIterator iterator(this); 338 unsigned int highestFlexGroup = 0; 339 unsigned int lowestFlexGroup = 0; 340 bool haveFlex = false, flexingChildren = false; 341 gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex); 342 343 RenderBlock::startDelayUpdateScrollInfo(); 344 345 // We do 2 passes. The first pass is simply to lay everyone out at 346 // their preferred widths. The second pass handles flexing the children. 347 do { 348 // Reset our height. 349 setHeight(yPos); 350 351 xPos = borderLeft() + paddingLeft(); 352 353 // Our first pass is done without flexing. We simply lay the children 354 // out within the box. We have to do a layout first in order to determine 355 // our box's intrinsic height. 356 LayoutUnit maxAscent = 0, maxDescent = 0; 357 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 358 if (child->isOutOfFlowPositioned()) 359 continue; 360 361 SubtreeLayoutScope layoutScope(*child); 362 if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent()))) 363 layoutScope.setChildNeedsLayout(child); 364 365 // Compute the child's vertical margins. 366 child->computeAndSetBlockDirectionMargins(this); 367 368 if (!child->needsLayout()) 369 child->markForPaginationRelayoutIfNeeded(layoutScope); 370 371 // Now do the layout. 372 child->layoutIfNeeded(); 373 374 // Update our height and overflow height. 375 if (style()->boxAlign() == BBASELINE) { 376 LayoutUnit ascent = child->firstLineBoxBaseline(); 377 if (ascent == -1) 378 ascent = child->height() + child->marginBottom(); 379 ascent += child->marginTop(); 380 LayoutUnit descent = (child->height() + child->marginHeight()) - ascent; 381 382 // Update our maximum ascent. 383 maxAscent = std::max(maxAscent, ascent); 384 385 // Update our maximum descent. 386 maxDescent = std::max(maxDescent, descent); 387 388 // Now update our height. 389 setHeight(std::max(yPos + maxAscent + maxDescent, height())); 390 } else { 391 setHeight(std::max(height(), yPos + child->height() + child->marginHeight())); 392 } 393 } 394 395 if (!iterator.first() && hasLineIfEmpty()) 396 setHeight(height() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); 397 398 setHeight(height() + toAdd); 399 400 oldHeight = height(); 401 updateLogicalHeight(); 402 403 relayoutChildren = false; 404 if (oldHeight != height()) 405 heightSpecified = true; 406 407 // Now that our height is actually known, we can place our boxes. 408 m_stretchingChildren = (style()->boxAlign() == BSTRETCH); 409 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 410 if (child->isOutOfFlowPositioned()) { 411 child->containingBlock()->insertPositionedObject(child); 412 RenderLayer* childLayer = child->layer(); 413 childLayer->setStaticInlinePosition(xPos); 414 if (childLayer->staticBlockPosition() != yPos) { 415 childLayer->setStaticBlockPosition(yPos); 416 if (child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode())) 417 child->setChildNeedsLayout(MarkOnlyThis); 418 } 419 continue; 420 } 421 422 if (child->style()->visibility() == COLLAPSE) { 423 // visibility: collapsed children do not participate in our positioning. 424 // But we need to lay them down. 425 child->layoutIfNeeded(); 426 continue; 427 } 428 429 SubtreeLayoutScope layoutScope(*child); 430 431 // We need to see if this child's height has changed, since we make block elements 432 // fill the height of a containing box by default. 433 // Now do a layout. 434 LayoutUnit oldChildHeight = child->height(); 435 child->updateLogicalHeight(); 436 if (oldChildHeight != child->height()) 437 layoutScope.setChildNeedsLayout(child); 438 439 if (!child->needsLayout()) 440 child->markForPaginationRelayoutIfNeeded(layoutScope); 441 442 child->layoutIfNeeded(); 443 444 // We can place the child now, using our value of box-align. 445 xPos += child->marginLeft(); 446 LayoutUnit childY = yPos; 447 switch (style()->boxAlign()) { 448 case BCENTER: 449 childY += child->marginTop() + std::max<LayoutUnit>(0, (contentHeight() - (child->height() + child->marginHeight())) / 2); 450 break; 451 case BBASELINE: { 452 LayoutUnit ascent = child->firstLineBoxBaseline(); 453 if (ascent == -1) 454 ascent = child->height() + child->marginBottom(); 455 ascent += child->marginTop(); 456 childY += child->marginTop() + (maxAscent - ascent); 457 break; 458 } 459 case BEND: 460 childY += contentHeight() - child->marginBottom() - child->height(); 461 break; 462 default: // BSTART 463 childY += child->marginTop(); 464 break; 465 } 466 467 placeChild(child, LayoutPoint(xPos, childY)); 468 469 xPos += child->width() + child->marginRight(); 470 } 471 472 remainingSpace = borderLeft() + paddingLeft() + contentWidth() - xPos; 473 474 m_stretchingChildren = false; 475 if (flexingChildren) 476 haveFlex = false; // We're done. 477 else if (haveFlex) { 478 // We have some flexible objects. See if we need to grow/shrink them at all. 479 if (!remainingSpace) 480 break; 481 482 // Allocate the remaining space among the flexible objects. If we are trying to 483 // grow, then we go from the lowest flex group to the highest flex group. For shrinking, 484 // we go from the highest flex group to the lowest group. 485 bool expanding = remainingSpace > 0; 486 unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup; 487 unsigned int end = expanding? highestFlexGroup : lowestFlexGroup; 488 for (unsigned i = start; i <= end && remainingSpace; i++) { 489 // Always start off by assuming the group can get all the remaining space. 490 LayoutUnit groupRemainingSpace = remainingSpace; 491 do { 492 // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width 493 // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and 494 // computing the allowed growth before an object hits its min/max width (and thus 495 // forces a totalFlex recomputation). 496 LayoutUnit groupRemainingSpaceAtBeginning = groupRemainingSpace; 497 float totalFlex = 0.0f; 498 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 499 if (allowedChildFlex(child, expanding, i)) 500 totalFlex += child->style()->boxFlex(); 501 } 502 LayoutUnit spaceAvailableThisPass = groupRemainingSpace; 503 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 504 LayoutUnit allowedFlex = allowedChildFlex(child, expanding, i); 505 if (allowedFlex) { 506 LayoutUnit projectedFlex = (allowedFlex == LayoutUnit::max()) ? allowedFlex : LayoutUnit(allowedFlex * (totalFlex / child->style()->boxFlex())); 507 spaceAvailableThisPass = expanding ? std::min(spaceAvailableThisPass, projectedFlex) : std::max(spaceAvailableThisPass, projectedFlex); 508 } 509 } 510 511 // The flex groups may not have any flexible objects this time around. 512 if (!spaceAvailableThisPass || totalFlex == 0.0f) { 513 // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group. 514 groupRemainingSpace = 0; 515 continue; 516 } 517 518 // Now distribute the space to objects. 519 for (RenderBox* child = iterator.first(); child && spaceAvailableThisPass && totalFlex; child = iterator.next()) { 520 if (child->style()->visibility() == COLLAPSE) 521 continue; 522 523 if (allowedChildFlex(child, expanding, i)) { 524 LayoutUnit spaceAdd = LayoutUnit(spaceAvailableThisPass * (child->style()->boxFlex() / totalFlex)); 525 if (spaceAdd) { 526 child->setOverrideLogicalContentWidth(contentWidthForChild(child) + spaceAdd); 527 flexingChildren = true; 528 relayoutChildren = true; 529 } 530 531 spaceAvailableThisPass -= spaceAdd; 532 remainingSpace -= spaceAdd; 533 groupRemainingSpace -= spaceAdd; 534 535 totalFlex -= child->style()->boxFlex(); 536 } 537 } 538 if (groupRemainingSpace == groupRemainingSpaceAtBeginning) { 539 // This is not advancing, avoid getting stuck by distributing the remaining pixels. 540 LayoutUnit spaceAdd = groupRemainingSpace > 0 ? 1 : -1; 541 for (RenderBox* child = iterator.first(); child && groupRemainingSpace; child = iterator.next()) { 542 if (allowedChildFlex(child, expanding, i)) { 543 child->setOverrideLogicalContentWidth(contentWidthForChild(child) + spaceAdd); 544 flexingChildren = true; 545 relayoutChildren = true; 546 remainingSpace -= spaceAdd; 547 groupRemainingSpace -= spaceAdd; 548 } 549 } 550 } 551 } while (absoluteValue(groupRemainingSpace) >= 1); 552 } 553 554 // We didn't find any children that could grow. 555 if (haveFlex && !flexingChildren) 556 haveFlex = false; 557 } 558 } while (haveFlex); 559 560 RenderBlock::finishDelayUpdateScrollInfo(); 561 562 if (remainingSpace > 0 && ((style()->isLeftToRightDirection() && style()->boxPack() != Start) 563 || (!style()->isLeftToRightDirection() && style()->boxPack() != End))) { 564 // Children must be repositioned. 565 LayoutUnit offset = 0; 566 if (style()->boxPack() == Justify) { 567 // Determine the total number of children. 568 int totalChildren = 0; 569 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 570 if (childDoesNotAffectWidthOrFlexing(child)) 571 continue; 572 ++totalChildren; 573 } 574 575 // Iterate over the children and space them out according to the 576 // justification level. 577 if (totalChildren > 1) { 578 --totalChildren; 579 bool firstChild = true; 580 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 581 if (childDoesNotAffectWidthOrFlexing(child)) 582 continue; 583 584 if (firstChild) { 585 firstChild = false; 586 continue; 587 } 588 589 offset += remainingSpace/totalChildren; 590 remainingSpace -= (remainingSpace/totalChildren); 591 --totalChildren; 592 593 placeChild(child, child->location() + LayoutSize(offset, 0)); 594 } 595 } 596 } else { 597 if (style()->boxPack() == Center) 598 offset += remainingSpace / 2; 599 else // END for LTR, START for RTL 600 offset += remainingSpace; 601 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 602 if (childDoesNotAffectWidthOrFlexing(child)) 603 continue; 604 605 placeChild(child, child->location() + LayoutSize(offset, 0)); 606 } 607 } 608 } 609 610 // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of 611 // a height change, we revert our height back to the intrinsic height before returning. 612 if (heightSpecified) 613 setHeight(oldHeight); 614 } 615 616 void RenderDeprecatedFlexibleBox::layoutVerticalBox(bool relayoutChildren) 617 { 618 LayoutUnit yPos = borderTop() + paddingTop(); 619 LayoutUnit toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); 620 bool heightSpecified = false; 621 LayoutUnit oldHeight = 0; 622 623 LayoutUnit remainingSpace = 0; 624 625 FlexBoxIterator iterator(this); 626 unsigned int highestFlexGroup = 0; 627 unsigned int lowestFlexGroup = 0; 628 bool haveFlex = false, flexingChildren = false; 629 gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex); 630 631 // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of 632 // mainstream block layout); this is not really part of the XUL box model. 633 bool haveLineClamp = !style()->lineClamp().isNone(); 634 if (haveLineClamp) 635 applyLineClamp(iterator, relayoutChildren); 636 637 RenderBlock::startDelayUpdateScrollInfo(); 638 639 // We do 2 passes. The first pass is simply to lay everyone out at 640 // their preferred widths. The second pass handles flexing the children. 641 // Our first pass is done without flexing. We simply lay the children 642 // out within the box. 643 do { 644 setHeight(borderTop() + paddingTop()); 645 LayoutUnit minHeight = height() + toAdd; 646 647 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 648 if (child->isOutOfFlowPositioned()) { 649 child->containingBlock()->insertPositionedObject(child); 650 RenderLayer* childLayer = child->layer(); 651 childLayer->setStaticInlinePosition(borderStart() + paddingStart()); 652 if (childLayer->staticBlockPosition() != height()) { 653 childLayer->setStaticBlockPosition(height()); 654 if (child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode())) 655 child->setChildNeedsLayout(MarkOnlyThis); 656 } 657 continue; 658 } 659 660 SubtreeLayoutScope layoutScope(*child); 661 if (!haveLineClamp && (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())))) 662 layoutScope.setChildNeedsLayout(child); 663 664 if (child->style()->visibility() == COLLAPSE) { 665 // visibility: collapsed children do not participate in our positioning. 666 // But we need to lay them down. 667 child->layoutIfNeeded(); 668 continue; 669 } 670 671 // Compute the child's vertical margins. 672 child->computeAndSetBlockDirectionMargins(this); 673 674 // Add in the child's marginTop to our height. 675 setHeight(height() + child->marginTop()); 676 677 if (!child->needsLayout()) 678 child->markForPaginationRelayoutIfNeeded(layoutScope); 679 680 // Now do a layout. 681 child->layoutIfNeeded(); 682 683 // We can place the child now, using our value of box-align. 684 LayoutUnit childX = borderLeft() + paddingLeft(); 685 switch (style()->boxAlign()) { 686 case BCENTER: 687 case BBASELINE: // Baseline just maps to center for vertical boxes 688 childX += child->marginLeft() + std::max<LayoutUnit>(0, (contentWidth() - (child->width() + child->marginWidth())) / 2); 689 break; 690 case BEND: 691 if (!style()->isLeftToRightDirection()) 692 childX += child->marginLeft(); 693 else 694 childX += contentWidth() - child->marginRight() - child->width(); 695 break; 696 default: // BSTART/BSTRETCH 697 if (style()->isLeftToRightDirection()) 698 childX += child->marginLeft(); 699 else 700 childX += contentWidth() - child->marginRight() - child->width(); 701 break; 702 } 703 704 // Place the child. 705 placeChild(child, LayoutPoint(childX, height())); 706 setHeight(height() + child->height() + child->marginBottom()); 707 } 708 709 yPos = height(); 710 711 if (!iterator.first() && hasLineIfEmpty()) 712 setHeight(height() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); 713 714 setHeight(height() + toAdd); 715 716 // Negative margins can cause our height to shrink below our minimal height (border/padding). 717 // If this happens, ensure that the computed height is increased to the minimal height. 718 if (height() < minHeight) 719 setHeight(minHeight); 720 721 // Now we have to calc our height, so we know how much space we have remaining. 722 oldHeight = height(); 723 updateLogicalHeight(); 724 if (oldHeight != height()) 725 heightSpecified = true; 726 727 remainingSpace = borderTop() + paddingTop() + contentHeight() - yPos; 728 729 if (flexingChildren) 730 haveFlex = false; // We're done. 731 else if (haveFlex) { 732 // We have some flexible objects. See if we need to grow/shrink them at all. 733 if (!remainingSpace) 734 break; 735 736 // Allocate the remaining space among the flexible objects. If we are trying to 737 // grow, then we go from the lowest flex group to the highest flex group. For shrinking, 738 // we go from the highest flex group to the lowest group. 739 bool expanding = remainingSpace > 0; 740 unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup; 741 unsigned int end = expanding? highestFlexGroup : lowestFlexGroup; 742 for (unsigned i = start; i <= end && remainingSpace; i++) { 743 // Always start off by assuming the group can get all the remaining space. 744 LayoutUnit groupRemainingSpace = remainingSpace; 745 do { 746 // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width 747 // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and 748 // computing the allowed growth before an object hits its min/max width (and thus 749 // forces a totalFlex recomputation). 750 LayoutUnit groupRemainingSpaceAtBeginning = groupRemainingSpace; 751 float totalFlex = 0.0f; 752 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 753 if (allowedChildFlex(child, expanding, i)) 754 totalFlex += child->style()->boxFlex(); 755 } 756 LayoutUnit spaceAvailableThisPass = groupRemainingSpace; 757 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 758 LayoutUnit allowedFlex = allowedChildFlex(child, expanding, i); 759 if (allowedFlex) { 760 LayoutUnit projectedFlex = (allowedFlex == LayoutUnit::max()) ? allowedFlex : static_cast<LayoutUnit>(allowedFlex * (totalFlex / child->style()->boxFlex())); 761 spaceAvailableThisPass = expanding ? std::min(spaceAvailableThisPass, projectedFlex) : std::max(spaceAvailableThisPass, projectedFlex); 762 } 763 } 764 765 // The flex groups may not have any flexible objects this time around. 766 if (!spaceAvailableThisPass || totalFlex == 0.0f) { 767 // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group. 768 groupRemainingSpace = 0; 769 continue; 770 } 771 772 // Now distribute the space to objects. 773 for (RenderBox* child = iterator.first(); child && spaceAvailableThisPass && totalFlex; child = iterator.next()) { 774 if (allowedChildFlex(child, expanding, i)) { 775 LayoutUnit spaceAdd = static_cast<LayoutUnit>(spaceAvailableThisPass * (child->style()->boxFlex() / totalFlex)); 776 if (spaceAdd) { 777 child->setOverrideLogicalContentHeight(contentHeightForChild(child) + spaceAdd); 778 flexingChildren = true; 779 relayoutChildren = true; 780 } 781 782 spaceAvailableThisPass -= spaceAdd; 783 remainingSpace -= spaceAdd; 784 groupRemainingSpace -= spaceAdd; 785 786 totalFlex -= child->style()->boxFlex(); 787 } 788 } 789 if (groupRemainingSpace == groupRemainingSpaceAtBeginning) { 790 // This is not advancing, avoid getting stuck by distributing the remaining pixels. 791 LayoutUnit spaceAdd = groupRemainingSpace > 0 ? 1 : -1; 792 for (RenderBox* child = iterator.first(); child && groupRemainingSpace; child = iterator.next()) { 793 if (allowedChildFlex(child, expanding, i)) { 794 child->setOverrideLogicalContentHeight(contentHeightForChild(child) + spaceAdd); 795 flexingChildren = true; 796 relayoutChildren = true; 797 remainingSpace -= spaceAdd; 798 groupRemainingSpace -= spaceAdd; 799 } 800 } 801 } 802 } while (absoluteValue(groupRemainingSpace) >= 1); 803 } 804 805 // We didn't find any children that could grow. 806 if (haveFlex && !flexingChildren) 807 haveFlex = false; 808 } 809 } while (haveFlex); 810 811 RenderBlock::finishDelayUpdateScrollInfo(); 812 813 if (style()->boxPack() != Start && remainingSpace > 0) { 814 // Children must be repositioned. 815 LayoutUnit offset = 0; 816 if (style()->boxPack() == Justify) { 817 // Determine the total number of children. 818 int totalChildren = 0; 819 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 820 if (childDoesNotAffectWidthOrFlexing(child)) 821 continue; 822 823 ++totalChildren; 824 } 825 826 // Iterate over the children and space them out according to the 827 // justification level. 828 if (totalChildren > 1) { 829 --totalChildren; 830 bool firstChild = true; 831 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 832 if (childDoesNotAffectWidthOrFlexing(child)) 833 continue; 834 835 if (firstChild) { 836 firstChild = false; 837 continue; 838 } 839 840 offset += remainingSpace/totalChildren; 841 remainingSpace -= (remainingSpace/totalChildren); 842 --totalChildren; 843 placeChild(child, child->location() + LayoutSize(0, offset)); 844 } 845 } 846 } else { 847 if (style()->boxPack() == Center) 848 offset += remainingSpace / 2; 849 else // END 850 offset += remainingSpace; 851 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 852 if (childDoesNotAffectWidthOrFlexing(child)) 853 continue; 854 placeChild(child, child->location() + LayoutSize(0, offset)); 855 } 856 } 857 } 858 859 // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of 860 // a height change, we revert our height back to the intrinsic height before returning. 861 if (heightSpecified) 862 setHeight(oldHeight); 863 } 864 865 void RenderDeprecatedFlexibleBox::applyLineClamp(FlexBoxIterator& iterator, bool relayoutChildren) 866 { 867 UseCounter::count(document(), UseCounter::LineClamp); 868 869 int maxLineCount = 0; 870 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 871 if (childDoesNotAffectWidthOrFlexing(child)) 872 continue; 873 874 child->clearOverrideSize(); 875 if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) 876 || (child->style()->height().isAuto() && child->isRenderBlock())) { 877 child->setChildNeedsLayout(MarkOnlyThis); 878 879 // Dirty all the positioned objects. 880 if (child->isRenderBlock()) { 881 toRenderBlock(child)->markPositionedObjectsForLayout(); 882 toRenderBlock(child)->clearTruncation(); 883 } 884 } 885 child->layoutIfNeeded(); 886 if (child->style()->height().isAuto() && child->isRenderBlock()) 887 maxLineCount = std::max(maxLineCount, toRenderBlock(child)->lineCount()); 888 } 889 890 // Get the number of lines and then alter all block flow children with auto height to use the 891 // specified height. We always try to leave room for at least one line. 892 LineClampValue lineClamp = style()->lineClamp(); 893 int numVisibleLines = lineClamp.isPercentage() ? std::max(1, (maxLineCount + 1) * lineClamp.value() / 100) : lineClamp.value(); 894 if (numVisibleLines >= maxLineCount) 895 return; 896 897 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 898 if (childDoesNotAffectWidthOrFlexing(child) || !child->style()->height().isAuto() || !child->isRenderBlock()) 899 continue; 900 901 RenderBlock* blockChild = toRenderBlock(child); 902 int lineCount = blockChild->lineCount(); 903 if (lineCount <= numVisibleLines) 904 continue; 905 906 LayoutUnit newHeight = blockChild->heightForLineCount(numVisibleLines); 907 if (newHeight == child->height()) 908 continue; 909 910 child->setOverrideLogicalContentHeight(newHeight - child->borderAndPaddingHeight()); 911 child->forceChildLayout(); 912 913 // FIXME: For now don't support RTL. 914 if (style()->direction() != LTR) 915 continue; 916 917 // Get the last line 918 RootInlineBox* lastLine = blockChild->lineAtIndex(lineCount - 1); 919 if (!lastLine) 920 continue; 921 922 RootInlineBox* lastVisibleLine = blockChild->lineAtIndex(numVisibleLines - 1); 923 if (!lastVisibleLine) 924 continue; 925 926 const UChar ellipsisAndSpace[2] = { horizontalEllipsis, ' ' }; 927 DEFINE_STATIC_LOCAL(AtomicString, ellipsisAndSpaceStr, (ellipsisAndSpace, 2)); 928 DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1)); 929 const Font& font = style(numVisibleLines == 1)->font(); 930 931 // Get ellipsis width, and if the last child is an anchor, it will go after the ellipsis, so add in a space and the anchor width too 932 float totalWidth; 933 InlineBox* anchorBox = lastLine->lastChild(); 934 if (anchorBox && anchorBox->renderer().style()->isLink()) 935 totalWidth = anchorBox->logicalWidth() + font.width(constructTextRun(this, font, ellipsisAndSpace, 2, style(), style()->direction())); 936 else { 937 anchorBox = 0; 938 totalWidth = font.width(constructTextRun(this, font, &horizontalEllipsis, 1, style(), style()->direction())); 939 } 940 941 // See if this width can be accommodated on the last visible line 942 RenderBlockFlow& destBlock = lastVisibleLine->block(); 943 RenderBlockFlow& srcBlock = lastLine->block(); 944 945 // FIXME: Directions of src/destBlock could be different from our direction and from one another. 946 if (!srcBlock.style()->isLeftToRightDirection()) 947 continue; 948 949 bool leftToRight = destBlock.style()->isLeftToRightDirection(); 950 if (!leftToRight) 951 continue; 952 953 LayoutUnit blockRightEdge = destBlock.logicalRightOffsetForLine(lastVisibleLine->y(), false); 954 if (!lastVisibleLine->lineCanAccommodateEllipsis(leftToRight, blockRightEdge, lastVisibleLine->x() + lastVisibleLine->logicalWidth(), totalWidth)) 955 continue; 956 957 // Let the truncation code kick in. 958 // FIXME: the text alignment should be recomputed after the width changes due to truncation. 959 LayoutUnit blockLeftEdge = destBlock.logicalLeftOffsetForLine(lastVisibleLine->y(), false); 960 lastVisibleLine->placeEllipsis(anchorBox ? ellipsisAndSpaceStr : ellipsisStr, leftToRight, blockLeftEdge.toFloat(), blockRightEdge.toFloat(), totalWidth, anchorBox); 961 destBlock.setHasMarkupTruncation(true); 962 } 963 } 964 965 void RenderDeprecatedFlexibleBox::clearLineClamp() 966 { 967 FlexBoxIterator iterator(this); 968 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 969 if (childDoesNotAffectWidthOrFlexing(child)) 970 continue; 971 972 child->clearOverrideSize(); 973 if ((child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) 974 || (child->style()->height().isAuto() && child->isRenderBlock())) { 975 child->setChildNeedsLayout(); 976 977 if (child->isRenderBlock()) { 978 toRenderBlock(child)->markPositionedObjectsForLayout(); 979 toRenderBlock(child)->clearTruncation(); 980 } 981 } 982 } 983 } 984 985 void RenderDeprecatedFlexibleBox::placeChild(RenderBox* child, const LayoutPoint& location) 986 { 987 // FIXME Investigate if this can be removed based on other flags. crbug.com/370010 988 child->setMayNeedPaintInvalidation(true); 989 990 // Place the child. 991 child->setLocation(location); 992 } 993 994 LayoutUnit RenderDeprecatedFlexibleBox::allowedChildFlex(RenderBox* child, bool expanding, unsigned int group) 995 { 996 if (childDoesNotAffectWidthOrFlexing(child) || child->style()->boxFlex() == 0.0f || child->style()->boxFlexGroup() != group) 997 return 0; 998 999 if (expanding) { 1000 if (isHorizontal()) { 1001 // FIXME: For now just handle fixed values. 1002 LayoutUnit maxWidth = LayoutUnit::max(); 1003 LayoutUnit width = contentWidthForChild(child); 1004 if (child->style()->maxWidth().isFixed()) 1005 maxWidth = child->style()->maxWidth().value(); 1006 else if (child->style()->maxWidth().type() == Intrinsic) 1007 maxWidth = child->maxPreferredLogicalWidth(); 1008 else if (child->style()->maxWidth().type() == MinIntrinsic) 1009 maxWidth = child->minPreferredLogicalWidth(); 1010 if (maxWidth == LayoutUnit::max()) 1011 return maxWidth; 1012 return std::max<LayoutUnit>(0, maxWidth - width); 1013 } else { 1014 // FIXME: For now just handle fixed values. 1015 LayoutUnit maxHeight = LayoutUnit::max(); 1016 LayoutUnit height = contentHeightForChild(child); 1017 if (child->style()->maxHeight().isFixed()) 1018 maxHeight = child->style()->maxHeight().value(); 1019 if (maxHeight == LayoutUnit::max()) 1020 return maxHeight; 1021 return std::max<LayoutUnit>(0, maxHeight - height); 1022 } 1023 } 1024 1025 // FIXME: For now just handle fixed values. 1026 if (isHorizontal()) { 1027 LayoutUnit minWidth = child->minPreferredLogicalWidth(); 1028 LayoutUnit width = contentWidthForChild(child); 1029 if (child->style()->minWidth().isFixed()) 1030 minWidth = child->style()->minWidth().value(); 1031 else if (child->style()->minWidth().type() == Intrinsic) 1032 minWidth = child->maxPreferredLogicalWidth(); 1033 else if (child->style()->minWidth().type() == MinIntrinsic) 1034 minWidth = child->minPreferredLogicalWidth(); 1035 else if (child->style()->minWidth().type() == Auto) 1036 minWidth = 0; 1037 1038 LayoutUnit allowedShrinkage = std::min<LayoutUnit>(0, minWidth - width); 1039 return allowedShrinkage; 1040 } else { 1041 Length minHeight = child->style()->minHeight(); 1042 if (minHeight.isFixed() || minHeight.isAuto()) { 1043 LayoutUnit minHeight = child->style()->minHeight().value(); 1044 LayoutUnit height = contentHeightForChild(child); 1045 LayoutUnit allowedShrinkage = std::min<LayoutUnit>(0, minHeight - height); 1046 return allowedShrinkage; 1047 } 1048 } 1049 1050 return 0; 1051 } 1052 1053 const char* RenderDeprecatedFlexibleBox::renderName() const 1054 { 1055 if (isFloating()) 1056 return "RenderDeprecatedFlexibleBox (floating)"; 1057 if (isOutOfFlowPositioned()) 1058 return "RenderDeprecatedFlexibleBox (positioned)"; 1059 // FIXME: Cleanup isPseudoElement duplication with other renderName methods. 1060 // crbug.com/415653 1061 if (isPseudoElement()) { 1062 if (style()->styleType() == BEFORE) 1063 return "RenderDeprecatedFlexibleBox (pseudo:before)"; 1064 if (style()->styleType() == AFTER) 1065 return "RenderDeprecatedFlexibleBox (pseudo:after)"; 1066 if (style()->styleType() == BACKDROP) 1067 return "RenderDeprecatedFlexibleBox (pseudo:backdrop)"; 1068 ASSERT_NOT_REACHED(); 1069 } 1070 if (isAnonymous()) 1071 return "RenderDeprecatedFlexibleBox (generated)"; 1072 if (isRelPositioned()) 1073 return "RenderDeprecatedFlexibleBox (relative positioned)"; 1074 return "RenderDeprecatedFlexibleBox"; 1075 } 1076 1077 } // namespace blink 1078