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