From 7321212d1b4fa4d86096b1cf95524f53d9bd99ca Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Fri, 6 Jul 2012 17:06:23 -0700 Subject: [PATCH] Bug 696253, patch 8: implement parsing/computation for CSS shorthand property 'flex'. r=dbaron --- dom/interfaces/css/nsIDOMCSS2Properties.idl | 3 + layout/style/Declaration.cpp | 14 + layout/style/nsCSSParser.cpp | 119 ++++++++ layout/style/nsCSSPropList.h | 15 + layout/style/nsCSSProps.cpp | 9 + layout/style/test/Makefile.in | 1 + layout/style/test/property_database.js | 40 +++ .../test/test_flexbox_flex_shorthand.html | 260 ++++++++++++++++++ 8 files changed, 461 insertions(+) create mode 100644 layout/style/test/test_flexbox_flex_shorthand.html diff --git a/dom/interfaces/css/nsIDOMCSS2Properties.idl b/dom/interfaces/css/nsIDOMCSS2Properties.idl index 8933d70e126d..d3e7eaa2ff33 100644 --- a/dom/interfaces/css/nsIDOMCSS2Properties.idl +++ b/dom/interfaces/css/nsIDOMCSS2Properties.idl @@ -768,6 +768,9 @@ interface nsIDOMCSS2Properties : nsISupports attribute DOMString MozAlignSelf; // raises(DOMException) on setting + attribute DOMString MozFlex; + // raises(DOMException) on setting + attribute DOMString MozFlexBasis; // raises(DOMException) on setting diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp index 34b81c933477..1440b8e309af 100644 --- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -800,6 +800,20 @@ Declaration::GetValue(nsCSSProperty aProperty, nsAString& aValue) const AppendValueToString(subprops[1], aValue); break; } +#ifdef MOZ_FLEXBOX + case eCSSProperty_flex: { + // flex-grow, flex-shrink, flex-basis, separated by single space + const nsCSSProperty* subprops = + nsCSSProps::SubpropertyEntryFor(aProperty); + + AppendValueToString(subprops[0], aValue); + aValue.Append(PRUnichar(' ')); + AppendValueToString(subprops[1], aValue); + aValue.Append(PRUnichar(' ')); + AppendValueToString(subprops[2], aValue); + break; + } +#endif // MOZ_FLEXBOX default: NS_ABORT_IF_FALSE(false, "no other shorthands"); break; diff --git a/layout/style/nsCSSParser.cpp b/layout/style/nsCSSParser.cpp index c0c82813cb9d..770048a00a85 100644 --- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -476,6 +476,11 @@ protected: bool ParseCalcTerm(nsCSSValue& aValue, PRInt32& aVariantMask); bool RequireWhitespace(); +#ifdef MOZ_FLEXBOX + // For "flex" shorthand property, defined in CSS3 Flexbox + bool ParseFlex(); +#endif + // for 'clip' and '-moz-image-region' bool ParseRect(nsCSSProperty aPropID); bool ParseColumns(); @@ -4831,6 +4836,116 @@ CSSParserImpl::ParseElement(nsCSSValue& aValue) return false; } +#ifdef MOZ_FLEXBOX +// flex: none | [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] +bool +CSSParserImpl::ParseFlex() +{ + // First check for inherit / initial + nsCSSValue tmpVal; + if (ParseVariant(tmpVal, VARIANT_INHERIT, nsnull)) { + AppendValue(eCSSProperty_flex_grow, tmpVal); + AppendValue(eCSSProperty_flex_shrink, tmpVal); + AppendValue(eCSSProperty_flex_basis, tmpVal); + return true; + } + + // Next, check for 'none' == '0 0 auto' + if (ParseVariant(tmpVal, VARIANT_NONE, nsnull)) { + AppendValue(eCSSProperty_flex_grow, nsCSSValue(0.0f, eCSSUnit_Number)); + AppendValue(eCSSProperty_flex_shrink, nsCSSValue(0.0f, eCSSUnit_Number)); + AppendValue(eCSSProperty_flex_basis, nsCSSValue(eCSSUnit_Auto)); + return true; + } + + // OK, try parsing our value as individual per-subproperty components: + // [ <'flex-grow'> <'flex-shrink'>? || <'flex-basis'> ] + + // Each subproperty has a default value that it takes when it's omitted in a + // "flex" shorthand value. These default values are *only* for the shorthand + // syntax -- they're distinct from the subproperties' own initial values. We + // start with each subproperty at its default, as if we had "flex: 1 1 0%". + nsCSSValue flexGrow(1.0f, eCSSUnit_Number); + nsCSSValue flexShrink(1.0f, eCSSUnit_Number); + nsCSSValue flexBasis(0.0f, eCSSUnit_Percent); + + // OVERVIEW OF PARSING STRATEGY: + // ============================= + // a) Parse the first component as either flex-basis or flex-grow. + // b) If it wasn't flex-grow, parse the _next_ component as flex-grow. + // c) Now we've just parsed flex-grow -- so try parsing the next thing as + // flex-shrink. + // d) Finally: If we didn't get flex-basis at the beginning, try to parse + // it now, at the end. + // + // More details in each section below. + + // (a) Parse first component. It's either 'flex-basis' or 'flex-grow', so + // we allow anything that would be legal to specify for the 'flex-basis' + // property (except for "inherit") and we also allow VARIANT_NUMBER so that + // we'll accept 'flex-grow' values (and importantly, so that we'll treat + // unitless 0 as a number instead of a length, since the flexbox spec + // disallows unitless 0 as a flex-basis value in the shorthand). + PRUint32 variantMask = VARIANT_NUMBER | + (nsCSSProps::ParserVariant(eCSSProperty_flex_basis) & ~(VARIANT_INHERIT)); + + if (!ParseNonNegativeVariant(tmpVal, variantMask, nsCSSProps::kWidthKTable)) { + // First component was not a valid flex-basis or flex-grow value. Fail. + return false; + } + + // Record what we just parsed as either flex-basis or flex-grow: + bool wasFirstComponentFlexBasis = (tmpVal.GetUnit() != eCSSUnit_Number); + (wasFirstComponentFlexBasis ? flexBasis : flexGrow) = tmpVal; + + // (b) If we didn't get flex-grow yet, parse _next_ component as flex-grow. + bool doneParsing = false; + if (wasFirstComponentFlexBasis) { + if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nsnull)) { + flexGrow = tmpVal; + } else { + // Failed to parse anything after our flex-basis -- that's fine. We can + // skip the remaining parsing. + doneParsing = true; + } + } + + if (!doneParsing) { + // (c) OK -- the last thing we parsed was flex-grow, so look for a + // flex-shrink in the next position. + if (ParseNonNegativeVariant(tmpVal, VARIANT_NUMBER, nsnull)) { + flexShrink = tmpVal; + } + + // d) Finally: If we didn't get flex-basis at the beginning, try to parse + // it now, at the end. + // + // NOTE: Even though we're looking for a (length-ish) flex-basis value, we + // *do* need to pass VARIANT_NUMBER as part of |variantMask| here. This + // ensures that we'll parse unitless '0' as a number, rather than as a + // length, so that we can reject it (along with any other number) below. + // (The flexbox spec disallows unitless '0' as a flex-basis value in the + // 'flex' shorthand.) + if (!wasFirstComponentFlexBasis && + ParseNonNegativeVariant(tmpVal, variantMask, + nsCSSProps::kWidthKTable)) { + if (tmpVal.GetUnit() == eCSSUnit_Number) { + // This is where we reject "0 0 0" (which the spec says is invalid, + // because we must reject unitless 0 as a flex-basis value). + return false; + } + flexBasis = tmpVal; + } + } + + AppendValue(eCSSProperty_flex_grow, flexGrow); + AppendValue(eCSSProperty_flex_shrink, flexShrink); + AppendValue(eCSSProperty_flex_basis, flexBasis); + + return true; +} +#endif + // : [ | ]? bool CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient) @@ -5488,6 +5603,10 @@ CSSParserImpl::ParsePropertyByFunction(nsCSSProperty aPropID) return ParseCounterData(aPropID); case eCSSProperty_cursor: return ParseCursor(); +#ifdef MOZ_FLEXBOX + case eCSSProperty_flex: + return ParseFlex(); +#endif // MOZ_FLEXBOX case eCSSProperty_font: return ParseFont(); case eCSSProperty_image_region: diff --git a/layout/style/nsCSSPropList.h b/layout/style/nsCSSPropList.h index 6ccf95910460..ee2a934497cd 100644 --- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -1532,6 +1532,12 @@ CSS_PROP_POSITION( kAlignSelfKTable, offsetof(nsStylePosition, mAlignSelf), eStyleAnimType_EnumU8) +CSS_PROP_SHORTHAND( + -moz-flex, + flex, + CSS_PROP_DOMPROP_PREFIXED(Flex), + CSS_PROPERTY_PARSE_FUNCTION, + "") CSS_PROP_POSITION( -moz-flex-basis, flex_basis, @@ -1540,6 +1546,9 @@ CSS_PROP_POSITION( CSS_PROPERTY_VALUE_NONNEGATIVE | CSS_PROPERTY_STORES_CALC, "", + // NOTE: The parsing implementation for the 'flex' shorthand property has + // its own code to parse each subproperty. It does not depend on the + // longhand parsing defined here. VARIANT_AHKLP | VARIANT_CALC, kWidthKTable, offsetof(nsStylePosition, mFlexBasis), @@ -1561,6 +1570,9 @@ CSS_PROP_POSITION( CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_VALUE_NONNEGATIVE, "", + // NOTE: The parsing implementation for the 'flex' shorthand property has + // its own code to parse each subproperty. It does not depend on the + // longhand parsing defined here. VARIANT_HN, nsnull, offsetof(nsStylePosition, mFlexGrow), @@ -1572,6 +1584,9 @@ CSS_PROP_POSITION( CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_VALUE_NONNEGATIVE, "", + // NOTE: The parsing implementation for the 'flex' shorthand property has + // its own code to parse each subproperty. It does not depend on the + // longhand parsing defined here. VARIANT_HN, nsnull, offsetof(nsStylePosition, mFlexShrink), diff --git a/layout/style/nsCSSProps.cpp b/layout/style/nsCSSProps.cpp index 552ed9824f85..984f25534b00 100644 --- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -2068,6 +2068,15 @@ static const nsCSSProperty gColumnRuleSubpropTable[] = { eCSSProperty_UNKNOWN }; +#ifdef MOZ_FLEXBOX +static const nsCSSProperty gFlexSubpropTable[] = { + eCSSProperty_flex_grow, + eCSSProperty_flex_shrink, + eCSSProperty_flex_basis, + eCSSProperty_UNKNOWN +}; +#endif // MOZ_FLEXBOX + static const nsCSSProperty gOverflowSubpropTable[] = { eCSSProperty_overflow_x, eCSSProperty_overflow_y, diff --git a/layout/style/test/Makefile.in b/layout/style/test/Makefile.in index 09194be0f616..912a7e1ab558 100644 --- a/layout/style/test/Makefile.in +++ b/layout/style/test/Makefile.in @@ -206,6 +206,7 @@ ifdef MOZ_FLEXBOX _TEST_FILES += \ test_flexbox_align_self_auto.html \ test_flexbox_flex_grow_and_shrink.html \ + test_flexbox_flex_shorthand.html \ $(NULL) endif diff --git a/layout/style/test/property_database.js b/layout/style/test/property_database.js index 6627babc7c1a..a1d11a79e08f 100644 --- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -755,6 +755,46 @@ var gCSSProperties = { other_values: [ "flex-start", "flex-end", "center", "baseline" ], invalid_values: [ "space-between", "abc", "30px" ] }, + "-moz-flex": { + domProp: "MozFlex", + inherited: false, + type: CSS_TYPE_TRUE_SHORTHAND, + subproperties: [ + "-moz-flex-grow", + "-moz-flex-shrink", + "-moz-flex-basis" + ], + initial_values: [ "0 1 auto", "auto 0 1", "0 auto", "auto 0" ], + other_values: [ + "none", + "1", + "0", + "0 1", + "0.5", + "1.2 3.4", + "0 0 0px", + "0px 0 0", + "5px 0 0", + "2 auto", + "auto 4", + "auto 5.6 7.8", + "-moz-max-content", + "1 -moz-max-content", + "1 2 -moz-max-content", + "-moz-max-content 1", + "-moz-max-content 1 2", + "-0" + ], + invalid_values: [ + "0 0 0", + "1 2px 3", + "1 auto 3", + "1px 2 3px", + "1px 2 3 4px", + "-1", + "1 -1" + ] + }, "-moz-flex-basis": { domProp: "MozFlexBasis", inherited: false, diff --git a/layout/style/test/test_flexbox_flex_shorthand.html b/layout/style/test/test_flexbox_flex_shorthand.html new file mode 100644 index 000000000000..d786df89db86 --- /dev/null +++ b/layout/style/test/test_flexbox_flex_shorthand.html @@ -0,0 +1,260 @@ + + + + + Test for Bug 696253 + + + + + +Mozilla Bug 696253 +

+
+
+
+
+
+ +