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 "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:
FlexBoxIterator(RenderFlexibleBox * parent)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
reset()65 void reset()
66 {
67 current = 0;
68 currentOrdinal = forward ? 0 : lastOrdinal+1;
69 }
70
first()71 RenderBox* first()
72 {
73 reset();
74 return next();
75 }
76
next()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
RenderFlexibleBox(Node * node)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
~RenderFlexibleBox()117 RenderFlexibleBox::~RenderFlexibleBox()
118 {
119 }
120
calcHorizontalPrefWidths()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
calcVerticalPrefWidths()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
calcPrefWidths()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
layoutBlock(bool relayoutChildren)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.
gatherFlexChildrenInfo(FlexBoxIterator & iterator,bool relayoutChildren,unsigned int & highestFlexGroup,unsigned int & lowestFlexGroup,bool & haveFlex)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
layoutHorizontalBox(bool relayoutChildren)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
layoutVerticalBox(bool relayoutChildren)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
placeChild(RenderBox * child,int x,int y)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
allowedChildFlex(RenderBox * child,bool expanding,unsigned int group)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
renderName() const1087 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