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