Bug 696253, patch 8: implement parsing/computation for CSS shorthand property 'flex'. r=dbaron

This commit is contained in:
Daniel Holbert 2012-07-06 17:06:23 -07:00
Родитель 131f2b5d0d
Коммит 7321212d1b
8 изменённых файлов: 461 добавлений и 0 удалений

Просмотреть файл

@ -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

Просмотреть файл

@ -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;

Просмотреть файл

@ -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
// <color-stop> : <color> [ <percentage> | <length> ]?
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:

Просмотреть файл

@ -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),

Просмотреть файл

@ -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,

Просмотреть файл

@ -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

Просмотреть файл

@ -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,

Просмотреть файл

@ -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>