1 /* 2 * This file is part of the render object implementation for KHTML. 3 * 4 * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org) 5 * (C) 1999 Antti Koivisto (koivisto (at) kde.org) 6 * Copyright (C) 2003 Apple Computer, Inc. 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Library General Public 10 * License as published by the Free Software Foundation; either 11 * version 2 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Library General Public License for more details. 17 * 18 * You should have received a copy of the GNU Library General Public License 19 * along with this library; see the file COPYING.LIB. If not, write to 20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 21 * Boston, MA 02110-1301, USA. 22 * 23 */ 24 25 #include "config.h" 26 #include "core/rendering/RenderDeprecatedFlexibleBox.h" 27 28 #include "core/frame/UseCounter.h" 29 #include "core/rendering/LayoutRepainter.h" 30 #include "core/rendering/RenderLayer.h" 31 #include "core/rendering/RenderView.h" 32 #include "platform/fonts/Font.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 (static_cast<size_t>(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 (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 clearPreferredLogicalWidthsDirty(); 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 if (isHorizontal()) 285 layoutHorizontalBox(relayoutChildren); 286 else 287 layoutVerticalBox(relayoutChildren); 288 289 LayoutUnit oldClientAfterEdge = clientLogicalBottom(); 290 updateLogicalHeight(); 291 292 if (previousSize.height() != height()) 293 relayoutChildren = true; 294 295 layoutPositionedObjects(relayoutChildren || isRoot()); 296 297 computeRegionRangeForBlock(flowThread); 298 299 computeOverflow(oldClientAfterEdge); 300 301 statePusher.pop(); 302 303 updateLayerTransform(); 304 305 if (view()->layoutState()->pageLogicalHeight()) 306 setPageLogicalOffset(view()->layoutState()->pageLogicalOffset(this, logicalTop())); 307 308 // Update our scrollbars if we're overflow:auto/scroll/hidden now that we know if 309 // we overflow or not. 310 if (hasOverflowClip()) 311 layer()->scrollableArea()->updateAfterLayout(); 312 313 // Repaint with our new bounds if they are different from our old bounds. 314 repainter.repaintAfterLayout(); 315 316 clearNeedsLayout(); 317 } 318 319 // The first walk over our kids is to find out if we have any flexible children. 320 static void gatherFlexChildrenInfo(FlexBoxIterator& iterator, bool relayoutChildren, unsigned int& highestFlexGroup, unsigned int& lowestFlexGroup, bool& haveFlex) 321 { 322 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 323 // Check to see if this child flexes. 324 if (!childDoesNotAffectWidthOrFlexing(child) && child->style()->boxFlex() > 0.0f) { 325 // We always have to lay out flexible objects again, since the flex distribution 326 // may have changed, and we need to reallocate space. 327 child->clearOverrideSize(); 328 if (!relayoutChildren) 329 child->setChildNeedsLayout(MarkOnlyThis); 330 haveFlex = true; 331 unsigned int flexGroup = child->style()->boxFlexGroup(); 332 if (lowestFlexGroup == 0) 333 lowestFlexGroup = flexGroup; 334 if (flexGroup < lowestFlexGroup) 335 lowestFlexGroup = flexGroup; 336 if (flexGroup > highestFlexGroup) 337 highestFlexGroup = flexGroup; 338 } 339 } 340 } 341 342 void RenderDeprecatedFlexibleBox::layoutHorizontalBox(bool relayoutChildren) 343 { 344 LayoutUnit toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); 345 LayoutUnit yPos = borderTop() + paddingTop(); 346 LayoutUnit xPos = borderLeft() + paddingLeft(); 347 bool heightSpecified = false; 348 LayoutUnit oldHeight = 0; 349 350 LayoutUnit remainingSpace = 0; 351 352 353 FlexBoxIterator iterator(this); 354 unsigned int highestFlexGroup = 0; 355 unsigned int lowestFlexGroup = 0; 356 bool haveFlex = false, flexingChildren = false; 357 gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex); 358 359 RenderBlock::startDelayUpdateScrollInfo(); 360 361 // We do 2 passes. The first pass is simply to lay everyone out at 362 // their preferred widths. The second pass handles flexing the children. 363 do { 364 // Reset our height. 365 setHeight(yPos); 366 367 xPos = borderLeft() + paddingLeft(); 368 369 // Our first pass is done without flexing. We simply lay the children 370 // out within the box. We have to do a layout first in order to determine 371 // our box's intrinsic height. 372 LayoutUnit maxAscent = 0, maxDescent = 0; 373 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 374 if (child->isOutOfFlowPositioned()) 375 continue; 376 377 SubtreeLayoutScope layoutScope(child); 378 if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent()))) 379 layoutScope.setChildNeedsLayout(child); 380 381 // Compute the child's vertical margins. 382 child->computeAndSetBlockDirectionMargins(this); 383 384 if (!child->needsLayout()) 385 child->markForPaginationRelayoutIfNeeded(layoutScope); 386 387 // Now do the layout. 388 child->layoutIfNeeded(); 389 390 // Update our height and overflow height. 391 if (style()->boxAlign() == BBASELINE) { 392 LayoutUnit ascent = child->firstLineBoxBaseline(); 393 if (ascent == -1) 394 ascent = child->height() + child->marginBottom(); 395 ascent += child->marginTop(); 396 LayoutUnit descent = (child->height() + child->marginHeight()) - ascent; 397 398 // Update our maximum ascent. 399 maxAscent = max(maxAscent, ascent); 400 401 // Update our maximum descent. 402 maxDescent = max(maxDescent, descent); 403 404 // Now update our height. 405 setHeight(max(yPos + maxAscent + maxDescent, height())); 406 } 407 else 408 setHeight(max(height(), yPos + child->height() + child->marginHeight())); 409 } 410 411 if (!iterator.first() && hasLineIfEmpty()) 412 setHeight(height() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); 413 414 setHeight(height() + toAdd); 415 416 oldHeight = height(); 417 updateLogicalHeight(); 418 419 relayoutChildren = false; 420 if (oldHeight != height()) 421 heightSpecified = true; 422 423 // Now that our height is actually known, we can place our boxes. 424 m_stretchingChildren = (style()->boxAlign() == BSTRETCH); 425 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 426 if (child->isOutOfFlowPositioned()) { 427 child->containingBlock()->insertPositionedObject(child); 428 RenderLayer* childLayer = child->layer(); 429 childLayer->setStaticInlinePosition(xPos); // FIXME: Not right for regions. 430 if (childLayer->staticBlockPosition() != yPos) { 431 childLayer->setStaticBlockPosition(yPos); 432 if (child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode())) 433 child->setChildNeedsLayout(MarkOnlyThis); 434 } 435 continue; 436 } 437 438 if (child->style()->visibility() == COLLAPSE) { 439 // visibility: collapsed children do not participate in our positioning. 440 // But we need to lay them down. 441 child->layoutIfNeeded(); 442 continue; 443 } 444 445 SubtreeLayoutScope layoutScope(child); 446 447 // We need to see if this child's height has changed, since we make block elements 448 // fill the height of a containing box by default. 449 // Now do a layout. 450 LayoutUnit oldChildHeight = child->height(); 451 child->updateLogicalHeight(); 452 if (oldChildHeight != child->height()) 453 layoutScope.setChildNeedsLayout(child); 454 455 if (!child->needsLayout()) 456 child->markForPaginationRelayoutIfNeeded(layoutScope); 457 458 child->layoutIfNeeded(); 459 460 // We can place the child now, using our value of box-align. 461 xPos += child->marginLeft(); 462 LayoutUnit childY = yPos; 463 switch (style()->boxAlign()) { 464 case BCENTER: 465 childY += child->marginTop() + max<LayoutUnit>(0, (contentHeight() - (child->height() + child->marginHeight())) / 2); 466 break; 467 case BBASELINE: { 468 LayoutUnit ascent = child->firstLineBoxBaseline(); 469 if (ascent == -1) 470 ascent = child->height() + child->marginBottom(); 471 ascent += child->marginTop(); 472 childY += child->marginTop() + (maxAscent - ascent); 473 break; 474 } 475 case BEND: 476 childY += contentHeight() - child->marginBottom() - child->height(); 477 break; 478 default: // BSTART 479 childY += child->marginTop(); 480 break; 481 } 482 483 placeChild(child, LayoutPoint(xPos, childY)); 484 485 xPos += child->width() + child->marginRight(); 486 } 487 488 remainingSpace = borderLeft() + paddingLeft() + contentWidth() - xPos; 489 490 m_stretchingChildren = false; 491 if (flexingChildren) 492 haveFlex = false; // We're done. 493 else if (haveFlex) { 494 // We have some flexible objects. See if we need to grow/shrink them at all. 495 if (!remainingSpace) 496 break; 497 498 // Allocate the remaining space among the flexible objects. If we are trying to 499 // grow, then we go from the lowest flex group to the highest flex group. For shrinking, 500 // we go from the highest flex group to the lowest group. 501 bool expanding = remainingSpace > 0; 502 unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup; 503 unsigned int end = expanding? highestFlexGroup : lowestFlexGroup; 504 for (unsigned int i = start; i <= end && remainingSpace; i++) { 505 // Always start off by assuming the group can get all the remaining space. 506 LayoutUnit groupRemainingSpace = remainingSpace; 507 do { 508 // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width 509 // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and 510 // computing the allowed growth before an object hits its min/max width (and thus 511 // forces a totalFlex recomputation). 512 LayoutUnit groupRemainingSpaceAtBeginning = groupRemainingSpace; 513 float totalFlex = 0.0f; 514 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 515 if (allowedChildFlex(child, expanding, i)) 516 totalFlex += child->style()->boxFlex(); 517 } 518 LayoutUnit spaceAvailableThisPass = groupRemainingSpace; 519 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 520 LayoutUnit allowedFlex = allowedChildFlex(child, expanding, i); 521 if (allowedFlex) { 522 LayoutUnit projectedFlex = (allowedFlex == LayoutUnit::max()) ? allowedFlex : LayoutUnit(allowedFlex * (totalFlex / child->style()->boxFlex())); 523 spaceAvailableThisPass = expanding ? min(spaceAvailableThisPass, projectedFlex) : max(spaceAvailableThisPass, projectedFlex); 524 } 525 } 526 527 // The flex groups may not have any flexible objects this time around. 528 if (!spaceAvailableThisPass || totalFlex == 0.0f) { 529 // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group. 530 groupRemainingSpace = 0; 531 continue; 532 } 533 534 // Now distribute the space to objects. 535 for (RenderBox* child = iterator.first(); child && spaceAvailableThisPass && totalFlex; child = iterator.next()) { 536 if (child->style()->visibility() == COLLAPSE) 537 continue; 538 539 if (allowedChildFlex(child, expanding, i)) { 540 LayoutUnit spaceAdd = LayoutUnit(spaceAvailableThisPass * (child->style()->boxFlex() / totalFlex)); 541 if (spaceAdd) { 542 child->setOverrideLogicalContentWidth(contentWidthForChild(child) + spaceAdd); 543 flexingChildren = true; 544 relayoutChildren = true; 545 } 546 547 spaceAvailableThisPass -= spaceAdd; 548 remainingSpace -= spaceAdd; 549 groupRemainingSpace -= spaceAdd; 550 551 totalFlex -= child->style()->boxFlex(); 552 } 553 } 554 if (groupRemainingSpace == groupRemainingSpaceAtBeginning) { 555 // This is not advancing, avoid getting stuck by distributing the remaining pixels. 556 LayoutUnit spaceAdd = groupRemainingSpace > 0 ? 1 : -1; 557 for (RenderBox* child = iterator.first(); child && groupRemainingSpace; child = iterator.next()) { 558 if (allowedChildFlex(child, expanding, i)) { 559 child->setOverrideLogicalContentWidth(contentWidthForChild(child) + spaceAdd); 560 flexingChildren = true; 561 relayoutChildren = true; 562 remainingSpace -= spaceAdd; 563 groupRemainingSpace -= spaceAdd; 564 } 565 } 566 } 567 } while (absoluteValue(groupRemainingSpace) >= 1); 568 } 569 570 // We didn't find any children that could grow. 571 if (haveFlex && !flexingChildren) 572 haveFlex = false; 573 } 574 } while (haveFlex); 575 576 RenderBlock::finishDelayUpdateScrollInfo(); 577 578 if (remainingSpace > 0 && ((style()->isLeftToRightDirection() && style()->boxPack() != Start) 579 || (!style()->isLeftToRightDirection() && style()->boxPack() != End))) { 580 // Children must be repositioned. 581 LayoutUnit offset = 0; 582 if (style()->boxPack() == Justify) { 583 // Determine the total number of children. 584 int totalChildren = 0; 585 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 586 if (childDoesNotAffectWidthOrFlexing(child)) 587 continue; 588 ++totalChildren; 589 } 590 591 // Iterate over the children and space them out according to the 592 // justification level. 593 if (totalChildren > 1) { 594 --totalChildren; 595 bool firstChild = true; 596 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 597 if (childDoesNotAffectWidthOrFlexing(child)) 598 continue; 599 600 if (firstChild) { 601 firstChild = false; 602 continue; 603 } 604 605 offset += remainingSpace/totalChildren; 606 remainingSpace -= (remainingSpace/totalChildren); 607 --totalChildren; 608 609 placeChild(child, child->location() + LayoutSize(offset, 0)); 610 } 611 } 612 } else { 613 if (style()->boxPack() == Center) 614 offset += remainingSpace / 2; 615 else // END for LTR, START for RTL 616 offset += remainingSpace; 617 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 618 if (childDoesNotAffectWidthOrFlexing(child)) 619 continue; 620 621 placeChild(child, child->location() + LayoutSize(offset, 0)); 622 } 623 } 624 } 625 626 // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of 627 // a height change, we revert our height back to the intrinsic height before returning. 628 if (heightSpecified) 629 setHeight(oldHeight); 630 } 631 632 void RenderDeprecatedFlexibleBox::layoutVerticalBox(bool relayoutChildren) 633 { 634 LayoutUnit yPos = borderTop() + paddingTop(); 635 LayoutUnit toAdd = borderBottom() + paddingBottom() + horizontalScrollbarHeight(); 636 bool heightSpecified = false; 637 LayoutUnit oldHeight = 0; 638 639 LayoutUnit remainingSpace = 0; 640 641 FlexBoxIterator iterator(this); 642 unsigned int highestFlexGroup = 0; 643 unsigned int lowestFlexGroup = 0; 644 bool haveFlex = false, flexingChildren = false; 645 gatherFlexChildrenInfo(iterator, relayoutChildren, highestFlexGroup, lowestFlexGroup, haveFlex); 646 647 // We confine the line clamp ugliness to vertical flexible boxes (thus keeping it out of 648 // mainstream block layout); this is not really part of the XUL box model. 649 bool haveLineClamp = !style()->lineClamp().isNone(); 650 if (haveLineClamp) 651 applyLineClamp(iterator, relayoutChildren); 652 653 RenderBlock::startDelayUpdateScrollInfo(); 654 655 // We do 2 passes. The first pass is simply to lay everyone out at 656 // their preferred widths. The second pass handles flexing the children. 657 // Our first pass is done without flexing. We simply lay the children 658 // out within the box. 659 do { 660 setHeight(borderTop() + paddingTop()); 661 LayoutUnit minHeight = height() + toAdd; 662 663 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 664 if (child->isOutOfFlowPositioned()) { 665 child->containingBlock()->insertPositionedObject(child); 666 RenderLayer* childLayer = child->layer(); 667 childLayer->setStaticInlinePosition(borderStart() + paddingStart()); // FIXME: Not right for regions. 668 if (childLayer->staticBlockPosition() != height()) { 669 childLayer->setStaticBlockPosition(height()); 670 if (child->style()->hasStaticBlockPosition(style()->isHorizontalWritingMode())) 671 child->setChildNeedsLayout(MarkOnlyThis); 672 } 673 continue; 674 } 675 676 SubtreeLayoutScope layoutScope(child); 677 if (!haveLineClamp && (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())))) 678 layoutScope.setChildNeedsLayout(child); 679 680 if (child->style()->visibility() == COLLAPSE) { 681 // visibility: collapsed children do not participate in our positioning. 682 // But we need to lay them down. 683 child->layoutIfNeeded(); 684 continue; 685 } 686 687 // Compute the child's vertical margins. 688 child->computeAndSetBlockDirectionMargins(this); 689 690 // Add in the child's marginTop to our height. 691 setHeight(height() + child->marginTop()); 692 693 if (!child->needsLayout()) 694 child->markForPaginationRelayoutIfNeeded(layoutScope); 695 696 // Now do a layout. 697 child->layoutIfNeeded(); 698 699 // We can place the child now, using our value of box-align. 700 LayoutUnit childX = borderLeft() + paddingLeft(); 701 switch (style()->boxAlign()) { 702 case BCENTER: 703 case BBASELINE: // Baseline just maps to center for vertical boxes 704 childX += child->marginLeft() + max<LayoutUnit>(0, (contentWidth() - (child->width() + child->marginWidth())) / 2); 705 break; 706 case BEND: 707 if (!style()->isLeftToRightDirection()) 708 childX += child->marginLeft(); 709 else 710 childX += contentWidth() - child->marginRight() - child->width(); 711 break; 712 default: // BSTART/BSTRETCH 713 if (style()->isLeftToRightDirection()) 714 childX += child->marginLeft(); 715 else 716 childX += contentWidth() - child->marginRight() - child->width(); 717 break; 718 } 719 720 // Place the child. 721 placeChild(child, LayoutPoint(childX, height())); 722 setHeight(height() + child->height() + child->marginBottom()); 723 } 724 725 yPos = height(); 726 727 if (!iterator.first() && hasLineIfEmpty()) 728 setHeight(height() + lineHeight(true, style()->isHorizontalWritingMode() ? HorizontalLine : VerticalLine, PositionOfInteriorLineBoxes)); 729 730 setHeight(height() + toAdd); 731 732 // Negative margins can cause our height to shrink below our minimal height (border/padding). 733 // If this happens, ensure that the computed height is increased to the minimal height. 734 if (height() < minHeight) 735 setHeight(minHeight); 736 737 // Now we have to calc our height, so we know how much space we have remaining. 738 oldHeight = height(); 739 updateLogicalHeight(); 740 if (oldHeight != height()) 741 heightSpecified = true; 742 743 remainingSpace = borderTop() + paddingTop() + contentHeight() - yPos; 744 745 if (flexingChildren) 746 haveFlex = false; // We're done. 747 else if (haveFlex) { 748 // We have some flexible objects. See if we need to grow/shrink them at all. 749 if (!remainingSpace) 750 break; 751 752 // Allocate the remaining space among the flexible objects. If we are trying to 753 // grow, then we go from the lowest flex group to the highest flex group. For shrinking, 754 // we go from the highest flex group to the lowest group. 755 bool expanding = remainingSpace > 0; 756 unsigned int start = expanding ? lowestFlexGroup : highestFlexGroup; 757 unsigned int end = expanding? highestFlexGroup : lowestFlexGroup; 758 for (unsigned int i = start; i <= end && remainingSpace; i++) { 759 // Always start off by assuming the group can get all the remaining space. 760 LayoutUnit groupRemainingSpace = remainingSpace; 761 do { 762 // Flexing consists of multiple passes, since we have to change ratios every time an object hits its max/min-width 763 // For a given pass, we always start off by computing the totalFlex of all objects that can grow/shrink at all, and 764 // computing the allowed growth before an object hits its min/max width (and thus 765 // forces a totalFlex recomputation). 766 LayoutUnit groupRemainingSpaceAtBeginning = groupRemainingSpace; 767 float totalFlex = 0.0f; 768 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 769 if (allowedChildFlex(child, expanding, i)) 770 totalFlex += child->style()->boxFlex(); 771 } 772 LayoutUnit spaceAvailableThisPass = groupRemainingSpace; 773 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 774 LayoutUnit allowedFlex = allowedChildFlex(child, expanding, i); 775 if (allowedFlex) { 776 LayoutUnit projectedFlex = (allowedFlex == LayoutUnit::max()) ? allowedFlex : static_cast<LayoutUnit>(allowedFlex * (totalFlex / child->style()->boxFlex())); 777 spaceAvailableThisPass = expanding ? min(spaceAvailableThisPass, projectedFlex) : max(spaceAvailableThisPass, projectedFlex); 778 } 779 } 780 781 // The flex groups may not have any flexible objects this time around. 782 if (!spaceAvailableThisPass || totalFlex == 0.0f) { 783 // If we just couldn't grow/shrink any more, then it's time to transition to the next flex group. 784 groupRemainingSpace = 0; 785 continue; 786 } 787 788 // Now distribute the space to objects. 789 for (RenderBox* child = iterator.first(); child && spaceAvailableThisPass && totalFlex; child = iterator.next()) { 790 if (allowedChildFlex(child, expanding, i)) { 791 LayoutUnit spaceAdd = static_cast<LayoutUnit>(spaceAvailableThisPass * (child->style()->boxFlex() / totalFlex)); 792 if (spaceAdd) { 793 child->setOverrideLogicalContentHeight(contentHeightForChild(child) + spaceAdd); 794 flexingChildren = true; 795 relayoutChildren = true; 796 } 797 798 spaceAvailableThisPass -= spaceAdd; 799 remainingSpace -= spaceAdd; 800 groupRemainingSpace -= spaceAdd; 801 802 totalFlex -= child->style()->boxFlex(); 803 } 804 } 805 if (groupRemainingSpace == groupRemainingSpaceAtBeginning) { 806 // This is not advancing, avoid getting stuck by distributing the remaining pixels. 807 LayoutUnit spaceAdd = groupRemainingSpace > 0 ? 1 : -1; 808 for (RenderBox* child = iterator.first(); child && groupRemainingSpace; child = iterator.next()) { 809 if (allowedChildFlex(child, expanding, i)) { 810 child->setOverrideLogicalContentHeight(contentHeightForChild(child) + spaceAdd); 811 flexingChildren = true; 812 relayoutChildren = true; 813 remainingSpace -= spaceAdd; 814 groupRemainingSpace -= spaceAdd; 815 } 816 } 817 } 818 } while (absoluteValue(groupRemainingSpace) >= 1); 819 } 820 821 // We didn't find any children that could grow. 822 if (haveFlex && !flexingChildren) 823 haveFlex = false; 824 } 825 } while (haveFlex); 826 827 RenderBlock::finishDelayUpdateScrollInfo(); 828 829 if (style()->boxPack() != Start && remainingSpace > 0) { 830 // Children must be repositioned. 831 LayoutUnit offset = 0; 832 if (style()->boxPack() == Justify) { 833 // Determine the total number of children. 834 int totalChildren = 0; 835 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 836 if (childDoesNotAffectWidthOrFlexing(child)) 837 continue; 838 839 ++totalChildren; 840 } 841 842 // Iterate over the children and space them out according to the 843 // justification level. 844 if (totalChildren > 1) { 845 --totalChildren; 846 bool firstChild = true; 847 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 848 if (childDoesNotAffectWidthOrFlexing(child)) 849 continue; 850 851 if (firstChild) { 852 firstChild = false; 853 continue; 854 } 855 856 offset += remainingSpace/totalChildren; 857 remainingSpace -= (remainingSpace/totalChildren); 858 --totalChildren; 859 placeChild(child, child->location() + LayoutSize(0, offset)); 860 } 861 } 862 } else { 863 if (style()->boxPack() == Center) 864 offset += remainingSpace / 2; 865 else // END 866 offset += remainingSpace; 867 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 868 if (childDoesNotAffectWidthOrFlexing(child)) 869 continue; 870 placeChild(child, child->location() + LayoutSize(0, offset)); 871 } 872 } 873 } 874 875 // So that the computeLogicalHeight in layoutBlock() knows to relayout positioned objects because of 876 // a height change, we revert our height back to the intrinsic height before returning. 877 if (heightSpecified) 878 setHeight(oldHeight); 879 } 880 881 void RenderDeprecatedFlexibleBox::applyLineClamp(FlexBoxIterator& iterator, bool relayoutChildren) 882 { 883 UseCounter::count(document(), UseCounter::LineClamp); 884 885 int maxLineCount = 0; 886 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 887 if (childDoesNotAffectWidthOrFlexing(child)) 888 continue; 889 890 child->clearOverrideSize(); 891 if (relayoutChildren || (child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) 892 || (child->style()->height().isAuto() && child->isRenderBlock())) { 893 child->setChildNeedsLayout(MarkOnlyThis); 894 895 // Dirty all the positioned objects. 896 if (child->isRenderBlock()) { 897 toRenderBlock(child)->markPositionedObjectsForLayout(); 898 toRenderBlock(child)->clearTruncation(); 899 } 900 } 901 child->layoutIfNeeded(); 902 if (child->style()->height().isAuto() && child->isRenderBlock()) 903 maxLineCount = max(maxLineCount, toRenderBlock(child)->lineCount()); 904 } 905 906 // Get the number of lines and then alter all block flow children with auto height to use the 907 // specified height. We always try to leave room for at least one line. 908 LineClampValue lineClamp = style()->lineClamp(); 909 int numVisibleLines = lineClamp.isPercentage() ? max(1, (maxLineCount + 1) * lineClamp.value() / 100) : lineClamp.value(); 910 if (numVisibleLines >= maxLineCount) 911 return; 912 913 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 914 if (childDoesNotAffectWidthOrFlexing(child) || !child->style()->height().isAuto() || !child->isRenderBlock()) 915 continue; 916 917 RenderBlock* blockChild = toRenderBlock(child); 918 int lineCount = blockChild->lineCount(); 919 if (lineCount <= numVisibleLines) 920 continue; 921 922 LayoutUnit newHeight = blockChild->heightForLineCount(numVisibleLines); 923 if (newHeight == child->height()) 924 continue; 925 926 child->setOverrideLogicalContentHeight(newHeight - child->borderAndPaddingHeight()); 927 child->forceChildLayout(); 928 929 // FIXME: For now don't support RTL. 930 if (style()->direction() != LTR) 931 continue; 932 933 // Get the last line 934 RootInlineBox* lastLine = blockChild->lineAtIndex(lineCount - 1); 935 if (!lastLine) 936 continue; 937 938 RootInlineBox* lastVisibleLine = blockChild->lineAtIndex(numVisibleLines - 1); 939 if (!lastVisibleLine) 940 continue; 941 942 const UChar ellipsisAndSpace[2] = { horizontalEllipsis, ' ' }; 943 DEFINE_STATIC_LOCAL(AtomicString, ellipsisAndSpaceStr, (ellipsisAndSpace, 2)); 944 DEFINE_STATIC_LOCAL(AtomicString, ellipsisStr, (&horizontalEllipsis, 1)); 945 const Font& font = style(numVisibleLines == 1)->font(); 946 947 // Get ellipsis width, and if the last child is an anchor, it will go after the ellipsis, so add in a space and the anchor width too 948 LayoutUnit totalWidth; 949 InlineBox* anchorBox = lastLine->lastChild(); 950 if (anchorBox && anchorBox->renderer()->style()->isLink()) 951 totalWidth = anchorBox->logicalWidth() + font.width(RenderBlockFlow::constructTextRun(this, font, ellipsisAndSpace, 2, style())); 952 else { 953 anchorBox = 0; 954 totalWidth = font.width(RenderBlockFlow::constructTextRun(this, font, &horizontalEllipsis, 1, style())); 955 } 956 957 // See if this width can be accommodated on the last visible line 958 RenderBlock* destBlock = toRenderBlock(lastVisibleLine->renderer()); 959 RenderBlock* srcBlock = toRenderBlock(lastLine->renderer()); 960 961 // FIXME: Directions of src/destBlock could be different from our direction and from one another. 962 if (!srcBlock->style()->isLeftToRightDirection()) 963 continue; 964 965 bool leftToRight = destBlock->style()->isLeftToRightDirection(); 966 if (!leftToRight) 967 continue; 968 969 LayoutUnit blockRightEdge = destBlock->logicalRightOffsetForLine(lastVisibleLine->y(), false); 970 if (!lastVisibleLine->lineCanAccommodateEllipsis(leftToRight, blockRightEdge, lastVisibleLine->x() + lastVisibleLine->logicalWidth(), totalWidth)) 971 continue; 972 973 // Let the truncation code kick in. 974 // FIXME: the text alignment should be recomputed after the width changes due to truncation. 975 LayoutUnit blockLeftEdge = destBlock->logicalLeftOffsetForLine(lastVisibleLine->y(), false); 976 lastVisibleLine->placeEllipsis(anchorBox ? ellipsisAndSpaceStr : ellipsisStr, leftToRight, blockLeftEdge, blockRightEdge, totalWidth, anchorBox); 977 destBlock->setHasMarkupTruncation(true); 978 } 979 } 980 981 void RenderDeprecatedFlexibleBox::clearLineClamp() 982 { 983 FlexBoxIterator iterator(this); 984 for (RenderBox* child = iterator.first(); child; child = iterator.next()) { 985 if (childDoesNotAffectWidthOrFlexing(child)) 986 continue; 987 988 child->clearOverrideSize(); 989 if ((child->isReplaced() && (child->style()->width().isPercent() || child->style()->height().isPercent())) 990 || (child->style()->height().isAuto() && child->isRenderBlock())) { 991 child->setChildNeedsLayout(); 992 993 if (child->isRenderBlock()) { 994 toRenderBlock(child)->markPositionedObjectsForLayout(); 995 toRenderBlock(child)->clearTruncation(); 996 } 997 } 998 } 999 } 1000 1001 void RenderDeprecatedFlexibleBox::placeChild(RenderBox* child, const LayoutPoint& location) 1002 { 1003 LayoutRect oldRect = child->frameRect(); 1004 1005 // Place the child. 1006 child->setLocation(location); 1007 1008 // If the child moved, we have to repaint it as well as any floating/positioned 1009 // descendants. An exception is if we need a layout. In this case, we know we're going to 1010 // repaint ourselves (and the child) anyway. 1011 if (!selfNeedsLayout() && child->checkForRepaintDuringLayout()) 1012 child->repaintDuringLayoutIfMoved(oldRect); 1013 } 1014 1015 LayoutUnit RenderDeprecatedFlexibleBox::allowedChildFlex(RenderBox* child, bool expanding, unsigned int group) 1016 { 1017 if (childDoesNotAffectWidthOrFlexing(child) || child->style()->boxFlex() == 0.0f || child->style()->boxFlexGroup() != group) 1018 return 0; 1019 1020 if (expanding) { 1021 if (isHorizontal()) { 1022 // FIXME: For now just handle fixed values. 1023 LayoutUnit maxWidth = LayoutUnit::max(); 1024 LayoutUnit width = contentWidthForChild(child); 1025 if (!child->style()->maxWidth().isUndefined() && child->style()->maxWidth().isFixed()) 1026 maxWidth = child->style()->maxWidth().value(); 1027 else if (child->style()->maxWidth().type() == Intrinsic) 1028 maxWidth = child->maxPreferredLogicalWidth(); 1029 else if (child->style()->maxWidth().type() == MinIntrinsic) 1030 maxWidth = child->minPreferredLogicalWidth(); 1031 if (maxWidth == LayoutUnit::max()) 1032 return maxWidth; 1033 return max<LayoutUnit>(0, maxWidth - width); 1034 } else { 1035 // FIXME: For now just handle fixed values. 1036 LayoutUnit maxHeight = LayoutUnit::max(); 1037 LayoutUnit height = contentHeightForChild(child); 1038 if (!child->style()->maxHeight().isUndefined() && child->style()->maxHeight().isFixed()) 1039 maxHeight = child->style()->maxHeight().value(); 1040 if (maxHeight == LayoutUnit::max()) 1041 return maxHeight; 1042 return max<LayoutUnit>(0, maxHeight - height); 1043 } 1044 } 1045 1046 // FIXME: For now just handle fixed values. 1047 if (isHorizontal()) { 1048 LayoutUnit minWidth = child->minPreferredLogicalWidth(); 1049 LayoutUnit width = contentWidthForChild(child); 1050 if (child->style()->minWidth().isFixed()) 1051 minWidth = child->style()->minWidth().value(); 1052 else if (child->style()->minWidth().type() == Intrinsic) 1053 minWidth = child->maxPreferredLogicalWidth(); 1054 else if (child->style()->minWidth().type() == MinIntrinsic) 1055 minWidth = child->minPreferredLogicalWidth(); 1056 else if (child->style()->minWidth().type() == Auto) 1057 minWidth = 0; 1058 1059 LayoutUnit allowedShrinkage = min<LayoutUnit>(0, minWidth - width); 1060 return allowedShrinkage; 1061 } else { 1062 Length minHeight = child->style()->minHeight(); 1063 if (minHeight.isFixed() || minHeight.isAuto()) { 1064 LayoutUnit minHeight = child->style()->minHeight().value(); 1065 LayoutUnit height = contentHeightForChild(child); 1066 LayoutUnit allowedShrinkage = min<LayoutUnit>(0, minHeight - height); 1067 return allowedShrinkage; 1068 } 1069 } 1070 1071 return 0; 1072 } 1073 1074 const char* RenderDeprecatedFlexibleBox::renderName() const 1075 { 1076 if (isFloating()) 1077 return "RenderDeprecatedFlexibleBox (floating)"; 1078 if (isOutOfFlowPositioned()) 1079 return "RenderDeprecatedFlexibleBox (positioned)"; 1080 // FIXME: Temporary hack while the new generated content system is being implemented. 1081 if (isPseudoElement()) 1082 return "RenderDeprecatedFlexibleBox (generated)"; 1083 if (isAnonymous()) 1084 return "RenderDeprecatedFlexibleBox (generated)"; 1085 if (isRelPositioned()) 1086 return "RenderDeprecatedFlexibleBox (relative positioned)"; 1087 return "RenderDeprecatedFlexibleBox"; 1088 } 1089 1090 } // namespace WebCore 1091