Skip to content
Permalink
Browse files
Add handling of mix-width and max-width for flexitems
https://bugs.webkit.org/show_bug.cgi?id=66723

Reviewed by David Hyatt.

Source/WebCore:

If we flex past a min/max width value, we need to mark the flexitem as
a fixed width and re-start the flexing algorithm.  We use a HashMap to
keep track of fixed width items.

This patch also split out the size computation from the actual layout
to avoid unnecessary layouts caused by restarting the flexing algorithm.

Test: css3/flexbox/003.html

* rendering/RenderFlexibleBox.cpp:
(WebCore::RenderFlexibleBox::layoutHorizontalBlock):
(WebCore::RenderFlexibleBox::runFreeSpaceAllocationAlgorithm):
* rendering/RenderFlexibleBox.h:

LayoutTests:

* css3/flexbox/003-expected.txt: Added.
* css3/flexbox/003.html: Added.


Canonical link: https://commits.webkit.org/82623@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@93651 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
idealisms committed Aug 23, 2011
1 parent a6e47eb commit 8fb789feac96434f5bc57734b331a27e1e858f5b
Showing 6 changed files with 200 additions and 29 deletions.
@@ -1,3 +1,13 @@
2011-08-23 Tony Chang <tony@chromium.org>

Add handling of mix-width and max-width for flexitems
https://bugs.webkit.org/show_bug.cgi?id=66723

Reviewed by David Hyatt.

* css3/flexbox/003-expected.txt: Added.
* css3/flexbox/003.html: Added.

2011-08-23 Adam Barth <abarth@webkit.org>

Update expectations for pkasting. The rebaseline script is having
@@ -0,0 +1,8 @@
PASS
PASS
PASS
PASS
PASS
PASS
PASS

@@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<style>
body {
margin: 0;
}
.horizontal-box {
width: 600px;
display: -webkit-flexbox;
background-color: #aaa;
position: relative;
}
.horizontal-box div {
height: 20px;
border: 0;
}

.horizontal-box :nth-child(1) {
background-color: blue;
}
.horizontal-box :nth-child(2) {
background-color: green;
}
.horizontal-box :nth-child(3) {
background-color: red;
}
</style>
<script>
if (window.layoutTestController)
layoutTestController.dumpAsText();
</script>
<script src="resources/flexbox.js"></script>
<body onload="checkHorizontalBoxen()">
<div class="horizontal-box">
<div data-expected-width="100" style="width: -webkit-flex(1 0 0); max-width: 100px;"></div>
<div data-expected-width="250" style="width: -webkit-flex(1 0 0);"></div>
<div data-expected-width="250" style="width: -webkit-flex(1 0 0);"></div>
</div>

<!-- The first two flexitems should hit their max width and the third item fills the remaining space. -->
<div class="horizontal-box">
<div data-expected-width="50" style="width: -webkit-flex(1 0 0); max-width: 50px;"></div>
<div data-expected-width="300" style="width: -webkit-flex(4 0 0); max-width: 300px;"></div>
<div data-expected-width="250" style="width: -webkit-flex(1 0 0);"></div>
</div>

<div class="horizontal-box">
<div data-expected-width="100" style="width: -webkit-flex(1 0 0); max-width: 100px;"></div>
<div data-expected-width="300" style="width: -webkit-flex(1 0 200px); max-width: 300px;"></div>
<div data-expected-width="200" style="width: -webkit-flex(1 0 0);"></div>
</div>

<!-- Test min-width. -->
<div class="horizontal-box">
<div data-expected-width="350" style="width: -webkit-flex(1 1 400px); min-width: 350px;"></div>
<div data-expected-width="250" style="width: -webkit-flex(1 1 400px);"></div>
</div>

<!-- The flex items can overflow the flexbox. -->
<div class="horizontal-box">
<div data-expected-width="350" style="width: -webkit-flex(1 1 400px); min-width: 350px;"></div>
<div data-expected-width="300" style="width: -webkit-flex(2 0 300px); max-width: 300px;"></div>
<div data-expected-width="0" style="width: -webkit-flex(1 0 0);"></div>
</div>

<div class="horizontal-box">
<div data-expected-width="100" data-offset-x="100" style="width: -webkit-flex(1 0 0); margin: 0 auto; max-width: 100px;"></div>
<div data-expected-width="200" data-offset-x="300" style="width: -webkit-flex(2 0 0);"></div>
<div data-expected-width="100" data-offset-x="500" style="width: -webkit-flex(1 0 0);"></div>
</div>

<!-- min-width and max-width take priority over the preferred size. -->
<div class="horizontal-box">
<div data-expected-width="500" style="width: -webkit-flex(1 0 0); min-width: 300px"></div>
<div data-expected-width="100" style="width: -webkit-flex(1 0 50%); max-width: 100px"></div>
</div>

</body>
</html>
@@ -1,3 +1,24 @@
2011-08-23 Tony Chang <tony@chromium.org>

Add handling of mix-width and max-width for flexitems
https://bugs.webkit.org/show_bug.cgi?id=66723

Reviewed by David Hyatt.

If we flex past a min/max width value, we need to mark the flexitem as
a fixed width and re-start the flexing algorithm. We use a HashMap to
keep track of fixed width items.

This patch also split out the size computation from the actual layout
to avoid unnecessary layouts caused by restarting the flexing algorithm.

Test: css3/flexbox/003.html

* rendering/RenderFlexibleBox.cpp:
(WebCore::RenderFlexibleBox::layoutHorizontalBlock):
(WebCore::RenderFlexibleBox::runFreeSpaceAllocationAlgorithm):
* rendering/RenderFlexibleBox.h:

2011-08-23 Pratik Solanki <psolanki@apple.com>

ResourceRequest::setStorageSession should update NSURLRequest as well
@@ -99,7 +99,7 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren, int, BlockLayoutPass)

m_overflow.clear();

// FIXME: Assume horizontal layout until flex-direction is added.
// FIXME: Assume horizontal layout until flex-flow is added.
layoutHorizontalBlock(relayoutChildren);

computeLogicalHeight();
@@ -116,6 +116,7 @@ void RenderFlexibleBox::layoutBlock(bool relayoutChildren, int, BlockLayoutPass)

static LayoutUnit preferredFlexItemContentWidth(RenderBox* child)
{
// FIXME: Handle vertical writing modes with horizontal flexing.
if (child->style()->width().isAuto())
return child->maxPreferredLogicalWidth() - child->borderLeft() - child->borderRight() - child->verticalScrollbarWidth() - child->paddingLeft() - child->paddingRight();
return child->contentWidth();
@@ -128,34 +129,13 @@ void RenderFlexibleBox::layoutHorizontalBlock(bool relayoutChildren)
float totalNegativeFlexibility;
FlexibleBoxIterator iterator(this);

computePreferredSize(relayoutChildren, iterator, preferredSize, totalPositiveFlexibility, totalNegativeFlexibility);
computePreferredSizeHorizontal(relayoutChildren, iterator, preferredSize, totalPositiveFlexibility, totalNegativeFlexibility);
LayoutUnit availableFreeSpace = contentWidth() - preferredSize;

LayoutUnit xOffset = borderLeft() + paddingLeft();
LayoutUnit yOffset = borderTop() + paddingTop();
for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
LayoutUnit childPreferredSize = preferredFlexItemContentWidth(child);
// FIXME: Handle max-width and min-width (we should clamp to the max/min value and restart the algorithm).
if (availableFreeSpace > 0 && totalPositiveFlexibility > 0)
childPreferredSize += lroundf(availableFreeSpace * child->style()->flexboxWidthPositiveFlex() / totalPositiveFlexibility);
else if (availableFreeSpace < 0 && totalNegativeFlexibility > 0)
childPreferredSize += lroundf(availableFreeSpace * child->style()->flexboxWidthNegativeFlex() / totalNegativeFlexibility);

childPreferredSize += child->borderLeft() + child->borderRight() + child->paddingLeft() + child->paddingRight();
child->setOverrideSize(LayoutSize(childPreferredSize, 0));
child->setChildNeedsLayout(true);
child->layoutIfNeeded();

setHeight(std::max(height(), borderTop() + paddingTop() + child->marginTop() + child->height() + child->marginBottom() + paddingBottom() + borderBottom() + horizontalScrollbarHeight()));

if (child->style()->marginLeft().isAuto())
child->setMarginLeft(availableFreeSpace > 0 ? lroundf(availableFreeSpace / totalPositiveFlexibility) : 0);
if (child->style()->marginRight().isAuto())
child->setMarginRight(availableFreeSpace > 0 ? lroundf(availableFreeSpace / totalPositiveFlexibility) : 0);

xOffset += child->marginLeft();
child->setLocation(IntPoint(xOffset, yOffset));
xOffset += child->width() + child->marginRight();
InflexibleFlexItemSize inflexibleItems;
while (!runFreeSpaceAllocationAlgorithmHorizontal(availableFreeSpace, totalPositiveFlexibility, totalNegativeFlexibility, inflexibleItems)) {
ASSERT(totalPositiveFlexibility >= 0 && totalNegativeFlexibility >= 0);
ASSERT(inflexibleItems.size() > 0);
}

// FIXME: Distribute leftover space to the packing space (second distribution round).
@@ -167,11 +147,12 @@ static LayoutUnit preferredSizeForMarginsAndPadding(Length length, LayoutUnit co
return length.calcMinValue(containerLength);
}

void RenderFlexibleBox::computePreferredSize(bool relayoutChildren, FlexibleBoxIterator& iterator, LayoutUnit& preferredSize, float& totalPositiveFlexibility, float& totalNegativeFlexibility)
void RenderFlexibleBox::computePreferredSizeHorizontal(bool relayoutChildren, FlexibleBoxIterator& iterator, LayoutUnit& preferredSize, float& totalPositiveFlexibility, float& totalNegativeFlexibility)
{
preferredSize = 0;
totalPositiveFlexibility = totalNegativeFlexibility = 0;

// FIXME: Handle vertical writing modes with horizontal flexing.
LayoutUnit flexboxAvailableLogicalWidth = availableLogicalWidth();
for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
// We always have to lay out flexible objects again, since the flex distribution
@@ -200,6 +181,76 @@ void RenderFlexibleBox::computePreferredSize(bool relayoutChildren, FlexibleBoxI
}
}

// Returns true if we successfully ran the algorithm and sized the flex items.
bool RenderFlexibleBox::runFreeSpaceAllocationAlgorithmHorizontal(LayoutUnit& availableFreeSpace, float& totalPositiveFlexibility, float& totalNegativeFlexibility, InflexibleFlexItemSize& inflexibleItems)
{
FlexibleBoxIterator iterator(this);

// FIXME: Handle vertical writing modes with horizontal flexing.
LayoutUnit flexboxAvailableLogicalWidth = availableLogicalWidth();
WTF::Vector<LayoutUnit> childSizes;
for (RenderBox* child = iterator.first(); child; child = iterator.next()) {
LayoutUnit childPreferredSize;
if (inflexibleItems.contains(child))
childPreferredSize = inflexibleItems.get(child);
else {
childPreferredSize = preferredFlexItemContentWidth(child);
if (availableFreeSpace > 0 && totalPositiveFlexibility > 0) {
childPreferredSize += lroundf(availableFreeSpace * child->style()->flexboxWidthPositiveFlex() / totalPositiveFlexibility);

Length childMaxWidth = child->style()->maxWidth();
if (!childMaxWidth.isUndefined() && childMaxWidth.isSpecified() && childPreferredSize > childMaxWidth.calcValue(flexboxAvailableLogicalWidth)) {
childPreferredSize = childMaxWidth.calcValue(flexboxAvailableLogicalWidth);
availableFreeSpace -= childPreferredSize - preferredFlexItemContentWidth(child);
totalPositiveFlexibility -= child->style()->flexboxWidthPositiveFlex();

inflexibleItems.set(child, childPreferredSize);
return false;
}
} else if (availableFreeSpace < 0 && totalNegativeFlexibility > 0) {
childPreferredSize += lroundf(availableFreeSpace * child->style()->flexboxWidthNegativeFlex() / totalNegativeFlexibility);

Length childMinWidth = child->style()->minWidth();
if (!childMinWidth.isUndefined() && childMinWidth.isSpecified() && childPreferredSize < childMinWidth.calcValue(flexboxAvailableLogicalWidth)) {
childPreferredSize = childMinWidth.calcValue(flexboxAvailableLogicalWidth);
availableFreeSpace += preferredFlexItemContentWidth(child) - childPreferredSize;
totalNegativeFlexibility -= child->style()->flexboxWidthNegativeFlex();

inflexibleItems.set(child, childPreferredSize);
return false;
}
}
}
childSizes.append(childPreferredSize);
}

// Now that we know the sizes, layout and position the flex items.
LayoutUnit xOffset = borderLeft() + paddingLeft();
LayoutUnit yOffset = borderTop() + paddingTop();
setHeight(0);
size_t i = 0;
for (RenderBox* child = iterator.first(); child; child = iterator.next(), ++i) {
LayoutUnit childPreferredSize = childSizes[i];
childPreferredSize += child->borderLeft() + child->borderRight() + child->paddingLeft() + child->paddingRight();
// FIXME: Handle vertical writing modes with horizontal flexing.
child->setOverrideSize(LayoutSize(childPreferredSize, 0));
child->setChildNeedsLayout(true);
child->layoutIfNeeded();

setHeight(std::max(height(), borderTop() + paddingTop() + child->marginTop() + child->height() + child->marginBottom() + paddingBottom() + borderBottom() + horizontalScrollbarHeight()));

if (child->style()->marginLeft().isAuto())
child->setMarginLeft(availableFreeSpace > 0 ? lroundf(availableFreeSpace / totalPositiveFlexibility) : 0);
if (child->style()->marginRight().isAuto())
child->setMarginRight(availableFreeSpace > 0 ? lroundf(availableFreeSpace / totalPositiveFlexibility) : 0);

xOffset += child->marginLeft();
child->setLocation(IntPoint(xOffset, yOffset));
xOffset += child->width() + child->marginRight();
}
return true;
}

}

#endif // ENABLE(CSS3_FLEXBOX)
@@ -50,10 +50,12 @@ class RenderFlexibleBox : public RenderBlock {

private:
class FlexibleBoxIterator;
typedef WTF::HashMap<const RenderBox*, LayoutUnit> InflexibleFlexItemSize;

void layoutHorizontalBlock(bool relayoutChildren);

void computePreferredSize(bool relayoutChildren, FlexibleBoxIterator&, LayoutUnit&, float& totalPositiveFlexibility, float& totalNegativeFlexibility);
void computePreferredSizeHorizontal(bool relayoutChildren, FlexibleBoxIterator&, LayoutUnit&, float& totalPositiveFlexibility, float& totalNegativeFlexibility);
bool runFreeSpaceAllocationAlgorithmHorizontal(LayoutUnit& availableFreeSpace, float& totalPositiveFlexibility, float& totalNegativeFlexibility, InflexibleFlexItemSize&);
};

} // namespace WebCore

0 comments on commit 8fb789f

Please sign in to comment.