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