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