Bug 696253, patch 5: implement parsing/computation for CSS property 'align-self'. r=dbaron

This commit is contained in:
Daniel Holbert 2012-07-06 17:06:21 -07:00
Родитель 85e8349a15
Коммит 0dd40b90f7
13 изменённых файлов: 348 добавлений и 1 удалений

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

@ -765,6 +765,9 @@ interface nsIDOMCSS2Properties : nsISupports
attribute DOMString MozAlignItems;
// raises(DOMException) on setting
attribute DOMString MozAlignSelf;
// raises(DOMException) on setting
attribute DOMString MozFlexDirection;
// raises(DOMException) on setting

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

@ -408,6 +408,12 @@ static inline mozilla::css::Side operator++(mozilla::css::Side& side, int) {
// For convenience/clarity (since we use this default value in multiple places)
#define NS_STYLE_ALIGN_ITEMS_INITIAL_VALUE NS_STYLE_ALIGN_ITEMS_STRETCH
// The "align-self" property accepts all of the normal "align-items" values
// (above) plus a special 'auto' value that computes to the parent's
// "align-items" value. Our computed style code internally represents 'auto'
// with this enum until we actually evaluate it:
#define NS_STYLE_ALIGN_SELF_AUTO 5
// See nsStylePosition
#define NS_STYLE_FLEX_DIRECTION_ROW 0
#define NS_STYLE_FLEX_DIRECTION_ROW_REVERSE 1

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

@ -1522,6 +1522,16 @@ CSS_PROP_POSITION(
kAlignItemsKTable,
offsetof(nsStylePosition, mAlignItems),
eStyleAnimType_EnumU8)
CSS_PROP_POSITION(
-moz-align-self,
align_self,
CSS_PROP_DOMPROP_PREFIXED(AlignSelf),
CSS_PROPERTY_PARSE_VALUE,
"",
VARIANT_HK,
kAlignSelfKTable,
offsetof(nsStylePosition, mAlignSelf),
eStyleAnimType_EnumU8)
CSS_PROP_POSITION(
-moz-flex-direction,
flex_direction,

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

@ -912,6 +912,17 @@ const PRInt32 nsCSSProps::kAlignItemsKTable[] = {
eCSSKeyword_UNKNOWN,-1
};
// Note: 'align-self' takes the same keywords as 'align-items', plus 'auto'.
const PRInt32 nsCSSProps::kAlignSelfKTable[] = {
eCSSKeyword_flex_start, NS_STYLE_ALIGN_ITEMS_FLEX_START,
eCSSKeyword_flex_end, NS_STYLE_ALIGN_ITEMS_FLEX_END,
eCSSKeyword_center, NS_STYLE_ALIGN_ITEMS_CENTER,
eCSSKeyword_baseline, NS_STYLE_ALIGN_ITEMS_BASELINE,
eCSSKeyword_stretch, NS_STYLE_ALIGN_ITEMS_STRETCH,
eCSSKeyword_auto, NS_STYLE_ALIGN_SELF_AUTO,
eCSSKeyword_UNKNOWN,-1
};
const PRInt32 nsCSSProps::kFlexDirectionKTable[] = {
eCSSKeyword_row, NS_STYLE_FLEX_DIRECTION_ROW,
eCSSKeyword_row_reverse, NS_STYLE_FLEX_DIRECTION_ROW_REVERSE,

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

@ -352,6 +352,7 @@ public:
static const PRInt32 kEmptyCellsKTable[];
#ifdef MOZ_FLEXBOX
static const PRInt32 kAlignItemsKTable[];
static const PRInt32 kAlignSelfKTable[];
static const PRInt32 kFlexDirectionKTable[];
static const PRInt32 kJustifyContentKTable[];
#endif // MOZ_FLEXBOX

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

@ -2935,6 +2935,32 @@ nsComputedDOMStyle::DoGetAlignItems()
return val;
}
nsIDOMCSSValue*
nsComputedDOMStyle::DoGetAlignSelf()
{
nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
PRUint8 computedAlignSelf = GetStylePosition()->mAlignSelf;
if (computedAlignSelf == NS_STYLE_ALIGN_SELF_AUTO) {
// "align-self: auto" needs to compute to parent's align-items value.
nsStyleContext* parentStyleContext = mStyleContextHolder->GetParent();
if (parentStyleContext) {
computedAlignSelf =
parentStyleContext->GetStylePosition()->mAlignItems;
} else {
// No parent --> use default.
computedAlignSelf = NS_STYLE_ALIGN_ITEMS_INITIAL_VALUE;
}
}
NS_ABORT_IF_FALSE(computedAlignSelf != NS_STYLE_ALIGN_SELF_AUTO,
"Should have swapped out 'auto' for something non-auto");
val->SetIdent(
nsCSSProps::ValueToKeywordEnum(computedAlignSelf,
nsCSSProps::kAlignSelfKTable));
return val;
}
nsIDOMCSSValue*
nsComputedDOMStyle::DoGetFlexDirection()
{
@ -4651,6 +4677,7 @@ nsComputedDOMStyle::GetQueryablePropertyMap(PRUint32* aLength)
#ifdef MOZ_FLEXBOX
COMPUTED_STYLE_MAP_ENTRY(align_items, AlignItems),
COMPUTED_STYLE_MAP_ENTRY(align_self, AlignSelf),
#endif // MOZ_FLEXBOX
COMPUTED_STYLE_MAP_ENTRY(animation_delay, AnimationDelay),
COMPUTED_STYLE_MAP_ENTRY(animation_direction, AnimationDirection),

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

@ -360,6 +360,7 @@ private:
#ifdef MOZ_FLEXBOX
/* CSS Flexbox properties */
nsIDOMCSSValue* DoGetAlignItems();
nsIDOMCSSValue* DoGetAlignSelf();
nsIDOMCSSValue* DoGetFlexDirection();
nsIDOMCSSValue* DoGetOrder();
nsIDOMCSSValue* DoGetJustifyContent();

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

@ -6431,6 +6431,60 @@ nsRuleNode::ComputePositionData(void* aStartStruct,
SETDSC_ENUMERATED, parentPos->mAlignItems,
NS_STYLE_ALIGN_ITEMS_INITIAL_VALUE, 0, 0, 0, 0);
// align-self: enum, inherit, initial
// NOTE: align-self's initial value is the special keyword "auto", which is
// supposed to compute to our parent's computed value of "align-items". So
// technically, "auto" itself is never a valid computed value for align-self,
// since it always computes to something else. Despite that, we do actually
// store "auto" in nsStylePosition::mAlignSelf, as NS_STYLE_ALIGN_SELF_AUTO
// (and then resolve it as-necessary). We do this because "auto" is the
// initial value for this property, so if we were to actually resolve it in
// nsStylePosition, we'd never be able to share any nsStylePosition structs
// in the rule tree, since their mAlignSelf values would depend on the parent
// style, by default.
if (aRuleData->ValueForAlignSelf()->GetUnit() == eCSSUnit_Inherit) {
// Special handling for "align-self: inherit", in case we're inheriting
// "align-self: auto", in which case we need to resolve the parent's "auto"
// and inherit that resolved value.
PRUint8 inheritedAlignSelf = parentPos->mAlignSelf;
if (inheritedAlignSelf == NS_STYLE_ALIGN_SELF_AUTO) {
if (parentPos == pos) {
// We're the root node. (If we weren't, COMPUTE_START_RESET would've
// given us a distinct parentPos, since we've got an 'inherit' value.)
// Nothing to inherit from --> just use default value.
inheritedAlignSelf = NS_STYLE_ALIGN_ITEMS_INITIAL_VALUE;
} else {
// Our parent's "auto" value should resolve to our grandparent's value
// for "align-items". So, that's what we're supposed to inherit.
NS_ABORT_IF_FALSE(aContext->GetParent(),
"we've got a distinct parent style-struct already, "
"so we should have a parent style-context");
nsStyleContext* grandparentContext = aContext->GetParent()->GetParent();
if (!grandparentContext) {
// No grandparent --> our parent is the root node, so its
// "align-self: auto" computes to the default "align-items" value:
inheritedAlignSelf = NS_STYLE_ALIGN_ITEMS_INITIAL_VALUE;
} else {
// Normal case -- we have a grandparent.
// Its "align-items" value is what we should end up inheriting.
const nsStylePosition* grandparentPos =
grandparentContext->GetStylePosition();
inheritedAlignSelf = grandparentPos->mAlignItems;
}
}
}
pos->mAlignSelf = inheritedAlignSelf;
canStoreInRuleTree = false;
} else {
SetDiscrete(*aRuleData->ValueForAlignSelf(),
pos->mAlignSelf, canStoreInRuleTree,
SETDSC_ENUMERATED,
parentPos->mAlignSelf, // (unused -- we handled inherit above)
NS_STYLE_ALIGN_SELF_AUTO, // initial == auto
0, 0, 0, 0);
}
// flex-direction: enum, inherit, initial
SetDiscrete(*aRuleData->ValueForFlexDirection(),
pos->mFlexDirection, canStoreInRuleTree,

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

@ -1127,6 +1127,7 @@ nsStylePosition::nsStylePosition(void)
mBoxSizing = NS_STYLE_BOX_SIZING_CONTENT;
#ifdef MOZ_FLEXBOX
mAlignItems = NS_STYLE_ALIGN_ITEMS_INITIAL_VALUE;
mAlignSelf = NS_STYLE_ALIGN_SELF_AUTO;
mFlexDirection = NS_STYLE_FLEX_DIRECTION_ROW;
mJustifyContent = NS_STYLE_JUSTIFY_CONTENT_FLEX_START;
mOrder = NS_STYLE_ORDER_INITIAL;
@ -1160,7 +1161,8 @@ nsChangeHint nsStylePosition::CalcDifference(const nsStylePosition& aOther) cons
// NOTE: Changes to "order" on a flex item may trigger some repositioning.
// If we're in a multi-line flex container, it also may affect our size
// (and that of our container & siblings) by shuffling items between lines.
if (mOrder != aOther.mOrder) {
if (mAlignSelf != aOther.mAlignSelf ||
mOrder != aOther.mOrder) {
return NS_CombineHint(hint, nsChangeHint_ReflowFrame);
}

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

@ -1095,6 +1095,7 @@ struct nsStylePosition {
PRUint8 mBoxSizing; // [reset] see nsStyleConsts.h
#ifdef MOZ_FLEXBOX
PRUint8 mAlignItems; // [reset] see nsStyleConsts.h
PRUint8 mAlignSelf; // [reset] see nsStyleConsts.h
PRUint8 mFlexDirection; // [reset] see nsStyleConsts.h
PRUint8 mJustifyContent; // [reset] see nsStyleConsts.h
PRInt32 mOrder; // [reset] integer

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

@ -202,6 +202,12 @@ _TEST_FILES = test_acid3_test46.html \
test_bug721136.html \
$(NULL)
ifdef MOZ_FLEXBOX
_TEST_FILES += \
test_flexbox_align_self_auto.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 \

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

@ -746,6 +746,15 @@ var gCSSProperties = {
other_values: [ "flex-start", "flex-end", "center", "baseline" ],
invalid_values: [ "space-between", "abc", "30px" ]
},
"-moz-align-self": {
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-direction": {
domProp: "MozFlexDirection",
inherited: false,

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

@ -0,0 +1,216 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=696253
-->
<head>
<meta charset="utf-8">
<title>Test behavior of 'align-self:auto' (Bug 696253)</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style type="text/css">
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=696253">Mozilla Bug 696253</a>
<div id="display">
<div id="myDiv"></div>
</div>
<pre id="test">
<script type="application/javascript">
"use strict";
/**
* Test behavior of 'align-self:auto' (Bug 696253)
* ===============================================
*
* The value "align-self: auto" is special. It's the initial value for
* "align-self", and it's supposed to compute to the parent's "align-items" value.
*
* However, to allow its style-struct to be shared by default, we internally
* make it compute to a special "auto" enumerated value, and then we resolve that
* to the correct value by examining the parent's style struct whenever we actually
* need to use it.
*
* This test makes sure that optimization isn't detectable to content.
*
* One special case of this is inheritance -- e.g.:
*
* <html style="align-items: baseline">
* <body style="align-self: auto; align-items: center">
* <div style="align-self: inherit">
*
* In that example, the child div's "inherit" should get the _computed_ value
* of "align-self" on the body. That, in turn, is "auto", so it should compute to
* its parent's "align-items" value, which is "baseline". So we need to end up
* with a computed "align-self" value of "baseline" on the child.
*
* (NOTE: if we instead allowed the child div to directly inherit the value "auto"
* from its parent, then we'd get different & incorrect behavior. The div would
* resolve that inherited "auto" value to its own parent's "align-items" value,
* which is "center" -- not "baseline".)
*
* This mochitest tests that situation and a few other similar tricky situations.
*/
/*
* Utility function for getting computed style of "align-self":
*/
function getComputedAlignSelf(elem) {
return window.getComputedStyle(elem, "").MozAlignSelf;
}
/*
* Tests that are useful regardless of whether we have a parent node:
*/
function testGeneralNode(elem) {
// Test initial computed style
// (Initial value should be 'auto', which should compute to 'stretch')
is(getComputedAlignSelf(elem), "stretch",
"initial computed value of 'align-self' should be 'stretch', " +
"if we haven't explicitly set any style on the parent");
// Test value after setting align-self explicitly to "auto"
elem.style.MozAlignSelf = "auto";
is(getComputedAlignSelf(elem), "stretch",
"computed value of 'align-self: auto' should be 'stretch', " +
"if we haven't explicitly set any style on the parent");
elem.style.MozAlignSelf = ""; // clean up
// Test value after setting align-self explicitly to "inherit"
elem.style.MozAlignSelf = "inherit";
is(getComputedAlignSelf(elem), "stretch",
"computed value of 'align-self: inherit' should be 'stretch', " +
"if we haven't explicitly set any style on the parent");
elem.style.MozAlignSelf = ""; // clean up
}
/*
* Tests that depend on us having a parent node:
*/
function testNodeThatHasParent(elem) {
// Sanity-check that we actually do have a styleable parent:
ok(elem.parentNode && elem.parentNode.style,
"bug in test -- expecting caller to pass us a node with a parent");
// Test initial computed style when "align-items" has been set on our parent.
// (elem's initial "align-self" value should be "auto", which should compute
// to its parent's "align-items" value, which in this case is "center".)
elem.parentNode.style.MozAlignItems = "center";
is(getComputedAlignSelf(elem), "center",
"initial computed value of 'align-self' should match parent's " +
"specified 'align-items' value");
// ...and now test computed style after setting "align-self" explicitly to
// "auto" (with parent "align-items" still at "center")
elem.style.MozAlignSelf = "auto";
is(getComputedAlignSelf(elem), "center",
"computed value of 'align-self: auto' should match parent's " +
"specified 'align-items' value");
elem.style.MozAlignSelf = ""; // clean up
elem.parentNode.style.MozAlignItems = ""; // clean up
// Finally: test computed style after setting "align-self" to "inherit"
// and leaving parent at its initial value (which should be "auto", which
// should compute to "stretch")
elem.style.MozAlignSelf = "inherit";
is(getComputedAlignSelf(elem), "stretch",
"computed value of 'align-self: inherit' should take parent's " +
"computed 'align-self' value (which should be 'stretch', " +
"if we haven't explicitly set any other style");
elem.style.MozAlignSelf = ""; // clean up
}
/*
* Tests that depend on us having a grandparent node:
*/
function testNodeThatHasGrandparent(elem) {
// Sanity-check that we actually do have a styleable grandparent:
ok(elem.parentNode && elem.parentNode.parentNode &&
elem.parentNode.parentNode.style,
"bug in test -- should be getting a node with a grandparent");
// Test computed "align-self" after we set "align-self" to "inherit" on our elem
// and to "auto" on its parent, and "align-items" to "baseline" on its
// grandparent. The parent's "auto" value should resolve to "baseline", and
// that's what our elem should inherit.
elem.style.MozAlignSelf = "inherit";
elem.parentNode.style.MozAlignSelf = "auto";
elem.parentNode.parentNode.style.MozAlignItems = "baseline";
is(getComputedAlignSelf(elem), "baseline",
"computed value of 'align-self:inherit' on node when parent has " +
"'align-self:auto' and grandparent has 'align-items:baseline'")
// clean up:
elem.style.MozAlignSelf = "";
elem.parentNode.style.MozAlignSelf = "";
elem.parentNode.parentNode.style.MozAlignItems = "";
// Test computed "align-self" after we set it to "auto" on our node, set
// "align-items" to "inherit" on its parent, and "align-items" to "baseline"
// on its grandparent. The parent's "inherit" should compute to "baseline",
// and our elem's "auto" value should resolve to that.
elem.style.MozAlignSelf = "auto";
elem.parentNode.style.MozAlignItems = "inherit";
elem.parentNode.parentNode.style.MozAlignItems = "baseline";
is(getComputedAlignSelf(elem), "baseline",
"computed value of 'align-self:auto on node when parent has " +
"'align-items:inherit' and grandparent has 'align-items:baseline'")
// clean up:
elem.style.MozAlignSelf = "";
elem.parentNode.style.MozAlignItems = "";
elem.parentNode.parentNode.style.MozAlignItems = "";
}
/*
* Main test function
*/
function main() {
// Test the root node
// ==================
// (It's special because it has no parent style context.)
var rootNode = document.documentElement;
// Sanity-check that we actually have the root node, as far as CSS is concerned.
// (Note: rootNode.parentNode is a HTMLDocument object -- not an element that
// we inherit style from.)
ok(!rootNode.parentNode.style,
"expecting root node to have no node to inherit style from");
testGeneralNode(rootNode);
// Test the body node
// ==================
// (It's special because it has no grandparent style context.)
var body = document.getElementsByTagName("body")[0];
is(body.parentNode, document.documentElement,
"expecting body element's parent to be the root node");
testGeneralNode(body);
testNodeThatHasParent(body);
// Test the <div id="display"> node
// ================================
// (It has both a parent and a grandparent style context.)
var displayNode = document.getElementById("display");
is(displayNode.parentNode.parentNode, document.documentElement,
"expecting 'display' node's grandparent to be the root node");
testGeneralNode(displayNode);
testNodeThatHasParent(displayNode);
testNodeThatHasGrandparent(displayNode);
}
main();
</script>
</pre>
</body>
</html>