implement -webkit-flex-order
authortony@chromium.org <tony@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 23 Sep 2011 04:54:42 +0000 (04:54 +0000)
committertony@chromium.org <tony@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 23 Sep 2011 04:54:42 +0000 (04:54 +0000)
https://bugs.webkit.org/show_bug.cgi?id=67432

Reviewed by Ojan Vafai.

Source/WebCore:

flex-order can be an int (including negative), but we disallow the two
smallest values so we can put the numbers into a hash set.

Also, create two iterators: one that goes in render tree order (we use
this for the first pass and to collect the possible flex order values)
and one that goes in flex order.

Test: css3/flexbox/flex-order.html

* css/CSSParser.cpp:
(WebCore::CSSParser::parseValue):
* rendering/RenderFlexibleBox.cpp:
(WebCore::FlexOrderHashTraits::emptyValue):
(WebCore::FlexOrderHashTraits::constructDeletedValue):
(WebCore::FlexOrderHashTraits::isDeletedValue):
(WebCore::RenderFlexibleBox::TreeOrderIterator::TreeOrderIterator): A simple iterator
    that goes in render tree order.
(WebCore::RenderFlexibleBox::TreeOrderIterator::next):
(WebCore::RenderFlexibleBox::TreeOrderIterator::reset):
(WebCore::RenderFlexibleBox::TreeOrderIterator::flexOrderValues):
(WebCore::RenderFlexibleBox::FlexOrderIterator::FlexOrderIterator): An iterator that
    goes in flex-order order.  Creating this involves sorting, so only create it once
    and pass it around.
(WebCore::RenderFlexibleBox::FlexOrderIterator::first):
(WebCore::RenderFlexibleBox::FlexOrderIterator::next):
(WebCore::RenderFlexibleBox::FlexOrderIterator::reset):
(WebCore::RenderFlexibleBox::layoutHorizontalBlock):
(WebCore::RenderFlexibleBox::computePreferredLogicalWidth):
(WebCore::RenderFlexibleBox::runFreeSpaceAllocationAlgorithmInlineDirection):
(WebCore::RenderFlexibleBox::layoutAndPlaceChildrenInlineDirection):
* rendering/RenderFlexibleBox.h:

LayoutTests:

* css3/flexbox/flex-order-expected.png: Added.
* css3/flexbox/flex-order-expected.txt: Added.
* css3/flexbox/flex-order.html: Added.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@95783 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/css3/flexbox/flex-order-expected.png [new file with mode: 0644]
LayoutTests/css3/flexbox/flex-order-expected.txt [new file with mode: 0644]
LayoutTests/css3/flexbox/flex-order.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/css/CSSParser.cpp
Source/WebCore/rendering/RenderFlexibleBox.cpp
Source/WebCore/rendering/RenderFlexibleBox.h

index 88b1a13..6228235 100644 (file)
@@ -1,3 +1,14 @@
+2011-09-22  Tony Chang  <tony@chromium.org>
+
+        implement -webkit-flex-order
+        https://bugs.webkit.org/show_bug.cgi?id=67432
+
+        Reviewed by Ojan Vafai.
+
+        * css3/flexbox/flex-order-expected.png: Added.
+        * css3/flexbox/flex-order-expected.txt: Added.
+        * css3/flexbox/flex-order.html: Added.
+
 2011-09-22  Ben Wells  <benwells@chromium.org>
 
         Rebaseline for bug 65583 (path based border radius drawing on skia) part 6
diff --git a/LayoutTests/css3/flexbox/flex-order-expected.png b/LayoutTests/css3/flexbox/flex-order-expected.png
new file mode 100644 (file)
index 0000000..fb4c7ae
Binary files /dev/null and b/LayoutTests/css3/flexbox/flex-order-expected.png differ
diff --git a/LayoutTests/css3/flexbox/flex-order-expected.txt b/LayoutTests/css3/flexbox/flex-order-expected.txt
new file mode 100644 (file)
index 0000000..8057557
--- /dev/null
@@ -0,0 +1 @@
+You should see identical green bars going from light green (left) to dark green (right).
diff --git a/LayoutTests/css3/flexbox/flex-order.html b/LayoutTests/css3/flexbox/flex-order.html
new file mode 100644 (file)
index 0000000..2c25515
--- /dev/null
@@ -0,0 +1,116 @@
+<!DOCTYPE html>
+<html>
+<style>
+body {
+    margin: 0;
+}
+.horizontal-box div {
+    height: 30px;
+    border: 0;
+    margin-bottom: 10px;
+}
+
+.first {
+    background-color: #0f0;
+}
+.second {
+    background-color: #0d0;
+}
+.third {
+    background-color: #090;
+}
+.fourth {
+    background-color: #060;
+}
+.fifth {
+    background-color: #030;
+}
+</style>
+<script>
+if (window.layoutTestController)
+    layoutTestController.dumpAsText(true);
+</script>
+<body>
+
+<div style="display: -webkit-flexbox" class="horizontal-box">
+  <div class="first"  style="width: -webkit-flex(1); -webkit-flex-order: 1"></div>
+  <div class="second" style="width: -webkit-flex(1);"></div>
+  <div class="third"  style="width: -webkit-flex(1); -webkit-flex-order: 3"></div>
+  <div class="fourth" style="width: -webkit-flex(1); -webkit-flex-order: 20"></div>
+</div>
+
+<div style="display: -webkit-flexbox" class="horizontal-box">
+  <div class="fourth" style="width: -webkit-flex(1); -webkit-flex-order: 4"></div>
+  <div class="third"  style="width: -webkit-flex(1); -webkit-flex-order: 3"></div>
+  <div class="second" style="width: -webkit-flex(1); -webkit-flex-order: 2"></div>
+  <div class="first"  style="width: -webkit-flex(1); -webkit-flex-order: 1"></div>
+</div>
+
+<!-- The example from the spec. -->
+<div style="display: -webkit-flexbox" class="horizontal-box">
+  <div class="third"  style="width: -webkit-flex(1); -webkit-flex-order: 2"></div>
+  <div class="first"  style="width: -webkit-flex(1)"></div>
+  <div class="fourth" style="width: -webkit-flex(1); -webkit-flex-order: 2"></div>
+  <div class="second" style="width: -webkit-flex(1); -webkit-flex-order: 1"></div>
+</div>
+
+<div style="display: -webkit-flexbox" class="horizontal-box">
+  <div class="fourth" style="width: -webkit-flex(1); -webkit-flex-order: 2"></div>
+  <div class="second" style="width: -webkit-flex(1)"></div>
+  <div class="third"  style="width: -webkit-flex(1)"></div>
+  <div class="first"  style="width: -webkit-flex(1); -webkit-flex-order: -10"></div>
+</div>
+
+<div style="display: -webkit-flexbox" class="horizontal-box">
+  <div class="fourth" style="width: -webkit-flex(1); -webkit-flex-order: 2000000000"></div>
+  <div class="second" style="width: -webkit-flex(1); -webkit-flex-order: 1000000000"></div>
+  <div class="third"  style="width: -webkit-flex(1); -webkit-flex-order: 1000000000"></div>
+  <div class="first"  style="width: -webkit-flex(1); -webkit-flex-order: -1000000000"></div>
+</div>
+
+<!-- Floating numbers are ignored and we use the default of 1 instead. -->
+<div style="display: -webkit-flexbox" class="horizontal-box">
+  <div class="second" style="width: -webkit-flex(1); -webkit-flex-order: 2.5"></div>
+  <div class="fourth" style="width: -webkit-flex(1); -webkit-flex-order: 2"></div>
+  <div class="first"  style="width: -webkit-flex(1); -webkit-flex-order: -1"></div>
+  <div class="third"  style="width: -webkit-flex(1)"></div>
+</div>
+
+<div style="display: -webkit-flexbox" class="horizontal-box">
+  <div class="second" style="width: -webkit-flex(1); -webkit-flex-order: 0"></div>
+  <div class="first" style="width: -webkit-flex(1); -webkit-flex-order: -1"></div>
+  <div class="third"  style="width: -webkit-flex(1); -webkit-flex-order: 1"></div>
+  <div class="fourth"  style="width: -webkit-flex(1)"></div>
+</div>
+
+<!-- Values greater than what can be stored in an int are clamped from
+     -2,147,483,646 (int min + 2) to 2,147,483,647. -->
+<div style="display: -webkit-flexbox" class="horizontal-box">
+  <div class="third"  style="width: -webkit-flex(1); -webkit-flex-order: 4000000000"></div>
+  <div class="fourth" style="width: -webkit-flex(1); -webkit-flex-order: 3000000000"></div>
+  <div class="first"  style="width: -webkit-flex(1);"></div>
+  <div class="second" style="width: -webkit-flex(1); -webkit-flex-order: 2147483646"></div>
+</div>
+
+<div style="display: -webkit-flexbox" class="horizontal-box">
+  <div class="second" style="width: -webkit-flex(1); -webkit-flex-order: 4000000000"></div>
+  <div class="third"  style="width: -webkit-flex(1); -webkit-flex-order: 3000000000"></div>
+  <div class="first"  style="width: -webkit-flex(1);"></div>
+  <div class="fourth" style="width: -webkit-flex(1); -webkit-flex-order: 2147483647"></div>
+</div>
+
+<div style="display: -webkit-flexbox" class="horizontal-box">
+  <div class="third"  style="width: -webkit-flex(1); -webkit-flex-order: -2147483645"></div>
+  <div class="first"  style="width: -webkit-flex(1); -webkit-flex-order: -2147483646"></div>
+  <div class="second" style="width: -webkit-flex(1); -webkit-flex-order: -2147483647"></div>
+  <div class="fourth" style="width: -webkit-flex(1);"></div>
+</div>
+
+<!-- This should not crash. -->
+<div style="display: -webkit-flexbox" class="horizontal-box"></div>
+
+<div style="position:absolute; left: -10000px;">You should see identical green bars going from light green
+(left) to dark green (right).</div>
+
+</body>
+</html>
index fffe9c0..84532b1 100644 (file)
@@ -1,3 +1,42 @@
+2011-09-22  Tony Chang  <tony@chromium.org>
+
+        implement -webkit-flex-order
+        https://bugs.webkit.org/show_bug.cgi?id=67432
+
+        Reviewed by Ojan Vafai.
+
+        flex-order can be an int (including negative), but we disallow the two
+        smallest values so we can put the numbers into a hash set.
+
+        Also, create two iterators: one that goes in render tree order (we use
+        this for the first pass and to collect the possible flex order values)
+        and one that goes in flex order.
+
+        Test: css3/flexbox/flex-order.html
+
+        * css/CSSParser.cpp:
+        (WebCore::CSSParser::parseValue):
+        * rendering/RenderFlexibleBox.cpp:
+        (WebCore::FlexOrderHashTraits::emptyValue):
+        (WebCore::FlexOrderHashTraits::constructDeletedValue):
+        (WebCore::FlexOrderHashTraits::isDeletedValue):
+        (WebCore::RenderFlexibleBox::TreeOrderIterator::TreeOrderIterator): A simple iterator
+            that goes in render tree order.
+        (WebCore::RenderFlexibleBox::TreeOrderIterator::next):
+        (WebCore::RenderFlexibleBox::TreeOrderIterator::reset):
+        (WebCore::RenderFlexibleBox::TreeOrderIterator::flexOrderValues):
+        (WebCore::RenderFlexibleBox::FlexOrderIterator::FlexOrderIterator): An iterator that
+            goes in flex-order order.  Creating this involves sorting, so only create it once
+            and pass it around.
+        (WebCore::RenderFlexibleBox::FlexOrderIterator::first):
+        (WebCore::RenderFlexibleBox::FlexOrderIterator::next):
+        (WebCore::RenderFlexibleBox::FlexOrderIterator::reset):
+        (WebCore::RenderFlexibleBox::layoutHorizontalBlock):
+        (WebCore::RenderFlexibleBox::computePreferredLogicalWidth):
+        (WebCore::RenderFlexibleBox::runFreeSpaceAllocationAlgorithmInlineDirection):
+        (WebCore::RenderFlexibleBox::layoutAndPlaceChildrenInlineDirection):
+        * rendering/RenderFlexibleBox.h:
+
 2011-09-22  Darin Adler  <darin@apple.com>
 
         Use AffineTransform scale functions in ShadowBlur::adjustBlurRadius
index 76f1dc3..446e2f6 100644 (file)
@@ -1574,7 +1574,12 @@ bool CSSParser::parseValue(int propId, bool important)
         break;
 #if ENABLE(CSS3_FLEXBOX)
     case CSSPropertyWebkitFlexOrder:
-        validPrimitive = validUnit(value, FInteger, true);
+        if (validUnit(value, FInteger, true)) {
+            // We restrict the smallest value to int min + 2 because we use int min and int min + 1 as special values in a hash set.
+            parsedValue = primitiveValueCache()->createValue(max(static_cast<double>(std::numeric_limits<int>::min() + 2), value->fValue),
+                                                             static_cast<CSSPrimitiveValue::UnitTypes>(value->unit));
+            m_valueList->next();
+        }
         break;
     case CSSPropertyWebkitFlexPack:
         validPrimitive = id == CSSValueStart || id == CSSValueEnd || id == CSSValueCenter || id == CSSValueJustify;
index b5c86ad..c995a23 100644 (file)
 
 namespace WebCore {
 
-class RenderFlexibleBox::FlexibleBoxIterator {
+// Normally, -1 and 0 are not valid in a HashSet, but these are relatively likely flex-order values. Instead,
+// we make the two smallest int values invalid flex-order values (in the css parser code we clamp them to
+// int min + 2).
+struct FlexOrderHashTraits : WTF::GenericHashTraits<int> {
+    static const bool emptyValueIsZero = false;
+    static int emptyValue() { return std::numeric_limits<int>::min(); }
+    static void constructDeletedValue(int& slot) { slot = std::numeric_limits<int>::min() + 1; }
+    static bool isDeletedValue(int value) { return value == std::numeric_limits<int>::min() + 1; }
+};
+
+typedef HashSet<int, DefaultHash<int>::Hash, FlexOrderHashTraits> FlexOrderHashSet;
+
+class RenderFlexibleBox::TreeOrderIterator {
 public:
-    explicit FlexibleBoxIterator(RenderFlexibleBox* flexibleBox)
+    explicit TreeOrderIterator(RenderFlexibleBox* flexibleBox)
         : m_flexibleBox(flexibleBox)
         , m_currentChild(0)
     {
@@ -56,6 +68,65 @@ public:
         while (child && !child->isBox())
             child = child->nextSibling();
 
+        if (child)
+            m_flexOrderValues.add(child->style()->flexOrder());
+
+        m_currentChild = toRenderBox(child);
+        return m_currentChild;
+    }
+
+    void reset()
+    {
+        m_currentChild = 0;
+    }
+
+    const FlexOrderHashSet& flexOrderValues()
+    {
+        return m_flexOrderValues;
+    }
+
+private:
+    RenderFlexibleBox* m_flexibleBox;
+    RenderBox* m_currentChild;
+    FlexOrderHashSet m_flexOrderValues;
+};
+
+class RenderFlexibleBox::FlexOrderIterator {
+public:
+    FlexOrderIterator(RenderFlexibleBox* flexibleBox, const FlexOrderHashSet& flexOrderValues)
+        : m_flexibleBox(flexibleBox)
+        , m_currentChild(0)
+        , m_orderValuesIterator(0)
+    {
+        copyToVector(flexOrderValues, m_orderValues);
+        std::sort(m_orderValues.begin(), m_orderValues.end());
+    }
+
+    RenderBox* first()
+    {
+        reset();
+        return next();
+    }
+
+    RenderBox* next()
+    {
+        RenderObject* child = m_currentChild;
+        do {
+            if (!child) {
+                if (m_orderValuesIterator == m_orderValues.end())
+                    return 0;
+                if (m_orderValuesIterator) {
+                    ++m_orderValuesIterator;
+                    if (m_orderValuesIterator == m_orderValues.end())
+                        return 0;
+                } else
+                    m_orderValuesIterator = m_orderValues.begin();
+
+                child = m_flexibleBox->firstChild();
+            } else
+                child = child->nextSibling();
+        } while (!child || !child->isBox() || child->style()->flexOrder() != *m_orderValuesIterator);
+
         m_currentChild = toRenderBox(child);
         return m_currentChild;
     }
@@ -63,11 +134,14 @@ public:
     void reset()
     {
         m_currentChild = 0;
+        m_orderValuesIterator = 0;
     }
 
 private:
     RenderFlexibleBox* m_flexibleBox;
     RenderBox* m_currentChild;
+    Vector<int> m_orderValues;
+    Vector<int>::const_iterator m_orderValuesIterator;
 };
 
 
@@ -152,19 +226,20 @@ void RenderFlexibleBox::layoutInlineDirection(bool relayoutChildren)
     LayoutUnit preferredLogicalWidth;
     float totalPositiveFlexibility;
     float totalNegativeFlexibility;
-    FlexibleBoxIterator iterator(this);
+    TreeOrderIterator treeIterator(this);
 
-    computePreferredLogicalWidth(relayoutChildren, iterator, preferredLogicalWidth, totalPositiveFlexibility, totalNegativeFlexibility);
+    computePreferredLogicalWidth(relayoutChildren, treeIterator, preferredLogicalWidth, totalPositiveFlexibility, totalNegativeFlexibility);
     LayoutUnit availableFreeSpace = contentLogicalWidth() - preferredLogicalWidth;
 
+    FlexOrderIterator flexIterator(this, treeIterator.flexOrderValues());
     InflexibleFlexItemSize inflexibleItems;
     WTF::Vector<LayoutUnit> childSizes;
-    while (!runFreeSpaceAllocationAlgorithmInlineDirection(availableFreeSpace, totalPositiveFlexibility, totalNegativeFlexibility, inflexibleItems, childSizes)) {
+    while (!runFreeSpaceAllocationAlgorithmInlineDirection(flexIterator, availableFreeSpace, totalPositiveFlexibility, totalNegativeFlexibility, inflexibleItems, childSizes)) {
         ASSERT(totalPositiveFlexibility >= 0 && totalNegativeFlexibility >= 0);
         ASSERT(inflexibleItems.size() > 0);
     }
 
-    layoutAndPlaceChildrenInlineDirection(childSizes, availableFreeSpace, totalPositiveFlexibility);
+    layoutAndPlaceChildrenInlineDirection(flexIterator, childSizes, availableFreeSpace, totalPositiveFlexibility);
 
     // FIXME: Handle distribution of cross-axis space (third distribution round).
 }
@@ -179,7 +254,7 @@ float RenderFlexibleBox::logicalNegativeFlexForChild(RenderBox* child)
     return isHorizontalWritingMode() ? child->style()->flexboxWidthNegativeFlex() : child->style()->flexboxHeightNegativeFlex();
 }
 
-void RenderFlexibleBox::computePreferredLogicalWidth(bool relayoutChildren, FlexibleBoxIterator& iterator, LayoutUnit& preferredLogicalWidth, float& totalPositiveFlexibility, float& totalNegativeFlexibility)
+void RenderFlexibleBox::computePreferredLogicalWidth(bool relayoutChildren, TreeOrderIterator& iterator, LayoutUnit& preferredLogicalWidth, float& totalPositiveFlexibility, float& totalNegativeFlexibility)
 {
     preferredLogicalWidth = 0;
     totalPositiveFlexibility = totalNegativeFlexibility = 0;
@@ -216,9 +291,8 @@ void RenderFlexibleBox::computePreferredLogicalWidth(bool relayoutChildren, Flex
 }
 
 // Returns true if we successfully ran the algorithm and sized the flex items.
-bool RenderFlexibleBox::runFreeSpaceAllocationAlgorithmInlineDirection(LayoutUnit& availableFreeSpace, float& totalPositiveFlexibility, float& totalNegativeFlexibility, InflexibleFlexItemSize& inflexibleItems, WTF::Vector<LayoutUnit>& childSizes)
+bool RenderFlexibleBox::runFreeSpaceAllocationAlgorithmInlineDirection(FlexOrderIterator& iterator, LayoutUnit& availableFreeSpace, float& totalPositiveFlexibility, float& totalNegativeFlexibility, InflexibleFlexItemSize& inflexibleItems, WTF::Vector<LayoutUnit>& childSizes)
 {
-    FlexibleBoxIterator iterator(this);
     childSizes.clear();
 
     LayoutUnit flexboxAvailableLogicalWidth = availableLogicalWidth();
@@ -273,9 +347,8 @@ void RenderFlexibleBox::setLogicalOverrideSize(RenderBox* child, LayoutUnit chil
         child->isHorizontalWritingMode() ? child->setOverrideHeight(childPreferredSize) : child->setOverrideWidth(childPreferredSize);
 }
 
-void RenderFlexibleBox::layoutAndPlaceChildrenInlineDirection(const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, float totalPositiveFlexibility)
+void RenderFlexibleBox::layoutAndPlaceChildrenInlineDirection(FlexOrderIterator& iterator, const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, float totalPositiveFlexibility)
 {
-    FlexibleBoxIterator iterator(this);
     LayoutUnit startEdge = borderStart() + paddingStart();
 
     if (hasPackingSpace(availableFreeSpace, totalPositiveFlexibility)) {
index 13e1221..8b7e6b4 100644 (file)
@@ -49,7 +49,8 @@ public:
     virtual void layoutBlock(bool relayoutChildren, int pageLogicalHeight = 0, BlockLayoutPass = NormalLayoutPass);
 
 private:
-    class FlexibleBoxIterator;
+    class TreeOrderIterator;
+    class FlexOrderIterator;
     typedef WTF::HashMap<const RenderBox*, LayoutUnit> InflexibleFlexItemSize;
 
     LayoutUnit logicalBorderAndPaddingWidthForChild(RenderBox* child);
@@ -63,10 +64,10 @@ private:
     float logicalPositiveFlexForChild(RenderBox* child);
     float logicalNegativeFlexForChild(RenderBox* child);
 
-    void computePreferredLogicalWidth(bool relayoutChildren, FlexibleBoxIterator&, LayoutUnit&, float& totalPositiveFlexibility, float& totalNegativeFlexibility);
-    bool runFreeSpaceAllocationAlgorithmInlineDirection(LayoutUnit& availableFreeSpace, float& totalPositiveFlexibility, float& totalNegativeFlexibility, InflexibleFlexItemSize&, WTF::Vector<LayoutUnit>& childSizes);
+    void computePreferredLogicalWidth(bool relayoutChildren, TreeOrderIterator&, LayoutUnit&, float& totalPositiveFlexibility, float& totalNegativeFlexibility);
+    bool runFreeSpaceAllocationAlgorithmInlineDirection(FlexOrderIterator&, LayoutUnit& availableFreeSpace, float& totalPositiveFlexibility, float& totalNegativeFlexibility, InflexibleFlexItemSize&, WTF::Vector<LayoutUnit>& childSizes);
     void setLogicalOverrideSize(RenderBox* child, LayoutUnit childPreferredSize);
-    void layoutAndPlaceChildrenInlineDirection(const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, float totalPositiveFlexibility);
+    void layoutAndPlaceChildrenInlineDirection(FlexOrderIterator&, const WTF::Vector<LayoutUnit>& childSizes, LayoutUnit availableFreeSpace, float totalPositiveFlexibility);
 };
 
 } // namespace WebCore