author | Daniel Holbert <dholbert@cs.stanford.edu> |
Fri, 06 Jul 2012 17:06:23 -0700 | |
changeset 98590 | a112868224d10b3924a28e6752edf6336c6e62ba |
parent 98589 | 57126745d4629a4be7049756fb649f59bd4de2d5 |
child 98591 | b6488e90d566871ff1d86e19c90522ab35d20038 |
push id | 23064 |
push user | ryanvm@gmail.com |
push date | Sat, 07 Jul 2012 18:54:06 +0000 |
treeherder | mozilla-central@9533b40ff28b [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | dbaron |
bugs | 696253 |
milestone | 16.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/interfaces/css/nsIDOMCSS2Properties.idl +++ b/dom/interfaces/css/nsIDOMCSS2Properties.idl @@ -763,16 +763,19 @@ interface nsIDOMCSS2Properties : nsISupp block should be uncommented (and this interface's uuid should be revved). (This would be #ifdef MOZ_FLEXBOX, if that worked in IDL files.) attribute DOMString MozAlignItems; // raises(DOMException) on setting attribute DOMString MozAlignSelf; // raises(DOMException) on setting + attribute DOMString MozFlex; + // raises(DOMException) on setting + attribute DOMString MozFlexBasis; // raises(DOMException) on setting attribute DOMString MozFlexDirection; // raises(DOMException) on setting attribute DOMString MozFlexGrow; // raises(DOMException) on setting
--- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -795,16 +795,30 @@ Declaration::GetValue(nsCSSProperty aPro // Two values, column-count and column-width, separated by a space. const nsCSSProperty* subprops = nsCSSProps::SubpropertyEntryFor(aProperty); AppendValueToString(subprops[0], aValue); aValue.Append(PRUnichar(' ')); 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; } } bool Declaration::GetValueIsImportant(const nsAString& aProperty) const
--- a/layout/style/nsCSSParser.cpp +++ b/layout/style/nsCSSParser.cpp @@ -471,16 +471,21 @@ protected: bool ParseCalcAdditiveExpression(nsCSSValue& aValue, PRInt32& aVariantMask); bool ParseCalcMultiplicativeExpression(nsCSSValue& aValue, PRInt32& aVariantMask, bool *aHadFinalWS); 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(); bool ParseContent(); bool ParseCounterData(nsCSSProperty aPropID); bool ParseCursor(); bool ParseFont(); bool ParseFontWeight(nsCSSValue& aValue); @@ -4826,16 +4831,126 @@ CSSParserImpl::ParseElement(nsCSSValue& } // If we detect a syntax error, we must match the opening parenthesis of the // function with the closing parenthesis and skip all the tokens in between. SkipUntil(')'); 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 + // <color-stop> : <color> [ <percentage> | <length> ]? bool CSSParserImpl::ParseColorStop(nsCSSValueGradient* aGradient) { nsCSSValueGradientStop* stop = aGradient->mStops.AppendElement(); if (!ParseVariant(stop->mColor, VARIANT_COLOR, nsnull)) { return false; } @@ -5483,16 +5598,20 @@ CSSParserImpl::ParsePropertyByFunction(n return ParseBorderSide(kColumnRuleIDs, false); case eCSSProperty_content: return ParseContent(); case eCSSProperty_counter_increment: case eCSSProperty_counter_reset: 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: return ParseRect(eCSSProperty_image_region); case eCSSProperty_list_style: return ParseListStyle(); case eCSSProperty_margin: return ParseMargin();
--- a/layout/style/nsCSSPropList.h +++ b/layout/style/nsCSSPropList.h @@ -1527,24 +1527,33 @@ CSS_PROP_POSITION( align_self, CSS_PROP_DOMPROP_PREFIXED(AlignSelf), CSS_PROPERTY_PARSE_VALUE, "", VARIANT_HK, 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, CSS_PROP_DOMPROP_PREFIXED(FlexBasis), CSS_PROPERTY_PARSE_VALUE | 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), eStyleAnimType_Coord) CSS_PROP_POSITION( -moz-flex-direction, flex_direction, CSS_PROP_DOMPROP_PREFIXED(FlexDirection), @@ -1556,27 +1565,33 @@ CSS_PROP_POSITION( eStyleAnimType_EnumU8) CSS_PROP_POSITION( -moz-flex-grow, flex_grow, CSS_PROP_DOMPROP_PREFIXED(FlexGrow), 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), eStyleAnimType_float) // float, except animations to/from 0 shouldn't work CSS_PROP_POSITION( -moz-flex-shrink, flex_shrink, CSS_PROP_DOMPROP_PREFIXED(FlexShrink), 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), eStyleAnimType_float) // float, except animations to/from 0 shouldn't work CSS_PROP_POSITION( -moz-order, order, CSS_PROP_DOMPROP_PREFIXED(Order),
--- a/layout/style/nsCSSProps.cpp +++ b/layout/style/nsCSSProps.cpp @@ -2063,16 +2063,25 @@ static const nsCSSProperty gColumnRuleSu // nsCSSDeclaration.cpp outputs the subproperties in this order. // It also depends on the color being third. eCSSProperty__moz_column_rule_width, eCSSProperty__moz_column_rule_style, eCSSProperty__moz_column_rule_color, 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, eCSSProperty_UNKNOWN }; static const nsCSSProperty gPaddingSubpropTable[] = { // Code relies on these being in top-right-bottom-left order.
--- a/layout/style/test/Makefile.in +++ b/layout/style/test/Makefile.in @@ -201,16 +201,17 @@ GARBAGE += css_properties.js test_load_events_on_stylesheets.html \ test_bug721136.html \ $(NULL) 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 _VISITED_REFTEST_FILES = \ $(shell find $(topsrcdir)/layout/reftests/css-visited/ -name '*.html' -o -name '*.xhtml') \ $(topsrcdir)/layout/reftests/svg/pseudo-classes-02.svg \ $(topsrcdir)/layout/reftests/svg/pseudo-classes-02-ref.svg \ $(topsrcdir)/layout/reftests/svg/as-image/lime100x100.svg \
--- a/layout/style/test/property_database.js +++ b/layout/style/test/property_database.js @@ -750,16 +750,56 @@ var gCSSProperties = { domProp: "MozAlignSelf", inherited: false, type: CSS_TYPE_LONGHAND, // (Assuming defaults on the parent, 'auto' will compute to 'stretch'.) initial_values: [ "auto", "stretch" ], 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, type: CSS_TYPE_LONGHAND, initial_values: [ " auto" ], // NOTE: This is cribbed directly from the "width" chunk, since this // property takes the exact same values as width (albeit with // different semantics on 'auto').
new file mode 100644 --- /dev/null +++ b/layout/style/test/test_flexbox_flex_shorthand.html @@ -0,0 +1,260 @@ +<!DOCTYPE HTML> +<html> +<!-- +https://bugzilla.mozilla.org/show_bug.cgi?id=696253 +--> +<head> + <title>Test for Bug 696253</title> + <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="property_database.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/> +</head> +<body> +<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=696253">Mozilla Bug 696253</a> +<p id="display"></p> +<div id="content"> +</div> +<pre id="test"> +<script type="application/javascript;version=1.7"> + +/** Test for Bug 696253 **/ +/* (Testing the [-moz-]flex CSS shorthand property) */ + +const gFlexPropName = "-moz-flex"; +const gFlexPropInfo = gCSSProperties[gFlexPropName]; + +// Default values for shorthand subproperties, when they're not specified +// explicitly in a testcase. This lets the testcases be more concise. +// +// The values here are from the flexbox spec on the 'flex' shorthand: +// "When omitted, [flex-grow and flex-shrink] are set to '1'. +// "If omitted, the flex basis defaults to 0%" +let gFlexShorthandDefaults = { + "-moz-flex-grow": "1", + "-moz-flex-shrink": "1", + "-moz-flex-basis": "0%" +}; + +let gFlexShorthandTestcases = [ +/* + { + "-moz-flex": "SPECIFIED value for flex shorthand", + + // Expected Computed Values of Subproperties + // Semi-optional -- if unspecified, the expected value is taken + // from gFlexShorthandDefaults. + "-moz-flex-grow": "EXPECTED computed value for flex-grow property", + "-moz-flex-shrink": "EXPECTED computed value for flex-shrink property", + "-moz-flex-basis": "EXPECTED computed value for flex-basis property", + }, +*/ + + // Initial values of subproperties: + // -------------------------------- + // (checked by another test that uses property_database.js, too, but + // might as well check here, too, for thoroughness). + { + "-moz-flex": "", + "-moz-flex-grow": "0", + "-moz-flex-shrink": "1", + "-moz-flex-basis": "auto", + }, + { + "-moz-flex": "-moz-initial", + "-moz-flex-grow": "0", + "-moz-flex-shrink": "1", + "-moz-flex-basis": "auto", + }, + + // Special keyword "none" --> "0 0 auto" + // ------------------------------------- + { + "-moz-flex": "none", + "-moz-flex-grow": "0", + "-moz-flex-shrink": "0", + "-moz-flex-basis": "auto", + }, + + // One Value (numeric) --> sets flex-grow + // -------------------------------------- + { + "-moz-flex": "0", + "-moz-flex-grow": "0", + }, + { + "-moz-flex": "5", + "-moz-flex-grow": "5", + }, + { + "-moz-flex": "1000", + "-moz-flex-grow": "1000", + }, + { + "-moz-flex": "0.0000001", + "-moz-flex-grow": "1e-7" + }, + { + "-moz-flex": "20000000", + "-moz-flex-grow": "2e+7" + }, + + // One Value (length or other nonnumeric) --> sets flex-basis + // ---------------------------------------------------------- + { + "-moz-flex": "0px", + "-moz-flex-basis": "0px", + }, + { + "-moz-flex": "0%", + "-moz-flex-basis": "0%", + }, + { + "-moz-flex": "25px", + "-moz-flex-basis": "25px", + }, + { + "-moz-flex": "5%", + "-moz-flex-basis": "5%", + }, + { + "-moz-flex": "auto", + "-moz-flex-basis": "auto", + }, + { + "-moz-flex": "-moz-fit-content", + "-moz-flex-basis": "-moz-fit-content", + }, + { + "-moz-flex": "-moz-calc(5px + 6px)", + "-moz-flex-basis": "11px", + }, + { + "-moz-flex": "-moz-calc(15% + 30px)", + "-moz-flex-basis": "-moz-calc(30px + 15%)", + }, + + // Two Values (numeric) --> sets flex-grow, flex-shrink + // ---------------------------------------------------- + { + "-moz-flex": "0 0", + "-moz-flex-grow": "0", + "-moz-flex-shrink": "0", + }, + { + "-moz-flex": "0 2", + "-moz-flex-grow": "0", + "-moz-flex-shrink": "2", + }, + { + "-moz-flex": "3 0", + "-moz-flex-grow": "3", + "-moz-flex-shrink": "0", + }, + { + "-moz-flex": "0.5000 2.03", + "-moz-flex-grow": "0.5", + "-moz-flex-shrink": "2.03", + }, + { + "-moz-flex": "300.0 500.0", + "-moz-flex-grow": "300", + "-moz-flex-shrink": "500", + }, + + // Two Values (numeric & length-ish) --> sets flex-grow, flex-basis + // ---------------------------------------------------------------- + { + "-moz-flex": "0 0px", + "-moz-flex-grow": "0", + "-moz-flex-basis": "0px", + }, + { + "-moz-flex": "0 0%", + "-moz-flex-grow": "0", + "-moz-flex-basis": "0%", + }, + { + "-moz-flex": "10 30px", + "-moz-flex-grow": "10", + "-moz-flex-basis": "30px", + }, + { + "-moz-flex": "99px 2.3", + "-moz-flex-grow": "2.3", + "-moz-flex-basis": "99px", + }, + { + "-moz-flex": "99% 6", + "-moz-flex-grow": "6", + "-moz-flex-basis": "99%", + }, + { + "-moz-flex": "auto 5", + "-moz-flex-grow": "5", + "-moz-flex-basis": "auto", + }, + { + "-moz-flex": "5 -moz-fit-content", + "-moz-flex-grow": "5", + "-moz-flex-basis": "-moz-fit-content", + }, + { + "-moz-flex": "-moz-calc(5% + 10px) 3", + "-moz-flex-grow": "3", + "-moz-flex-basis": "-moz-calc(10px + 5%)", + }, + + // Three Values --> Sets all three subproperties + // --------------------------------------------- + { + "-moz-flex": "0.0 0.00 0px", + "-moz-flex-grow": "0", + "-moz-flex-shrink": "0", + "-moz-flex-basis": "0px", + }, + { + "-moz-flex": "0% 0 0", + "-moz-flex-grow": "0", + "-moz-flex-shrink": "0", + "-moz-flex-basis": "0%", + }, + { + "-moz-flex": "10px 3 2", + "-moz-flex-grow": "3", + "-moz-flex-shrink": "2", + "-moz-flex-basis": "10px", + }, +]; + +function runFlexShorthandTest(aFlexShorthandTestcase) +{ + let content = document.getElementById("content"); + + let elem = document.createElement("div"); + + elem.style[gFlexPropInfo.domProp] = aFlexShorthandTestcase[gFlexPropName]; + content.appendChild(elem); + + gFlexPropInfo.subproperties.forEach(function(aSubPropName) { + var expectedVal = aSubPropName in aFlexShorthandTestcase ? + aFlexShorthandTestcase[aSubPropName] : + gFlexShorthandDefaults[aSubPropName]; + + // Compare computed value against expected computed value (from testcase) + is(window.getComputedStyle(elem, null).getPropertyValue(aSubPropName), + expectedVal, + "Computed value of subproperty \"" + aSubPropName + "\" when we set \"" + + gFlexPropName + ": " + aFlexShorthandTestcase[gFlexPropName] + "\""); + }); + + // Clean up + content.removeChild(elem); +} + +// Run the tests! +gFlexShorthandTestcases.forEach(runFlexShorthandTest) + +</script> +</pre> +</body> +</html>