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