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