Bug 696253, patch 5: implement parsing/computation for CSS property 'align-self'. r=dbaron
authorDaniel Holbert <dholbert@cs.stanford.edu>
Fri, 06 Jul 2012 17:06:21 -0700
changeset 98587 08dc24b4a21cf7142fa980043a2899668b355a20
parent 98586 7f84743f59a17f3c2a30d142c492723291d76c1a
child 98588 10d1162cd4e1c2fbd9344dd42e874dd2886dcff6
push id23064
push userryanvm@gmail.com
push dateSat, 07 Jul 2012 18:54:06 +0000
treeherdermozilla-central@9533b40ff28b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs696253
milestone16.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
Bug 696253, patch 5: implement parsing/computation for CSS property 'align-self'. r=dbaron
dom/interfaces/css/nsIDOMCSS2Properties.idl
layout/base/nsStyleConsts.h
layout/style/nsCSSPropList.h
layout/style/nsCSSProps.cpp
layout/style/nsCSSProps.h
layout/style/nsComputedDOMStyle.cpp
layout/style/nsComputedDOMStyle.h
layout/style/nsRuleNode.cpp
layout/style/nsStyleStruct.cpp
layout/style/nsStyleStruct.h
layout/style/test/Makefile.in
layout/style/test/property_database.js
layout/style/test/test_flexbox_align_self_auto.html
--- a/dom/interfaces/css/nsIDOMCSS2Properties.idl
+++ b/dom/interfaces/css/nsIDOMCSS2Properties.idl
@@ -760,16 +760,19 @@ interface nsIDOMCSS2Properties : nsISupp
 
 /* XXXdholbert NOTE: Flexbox properties are commented out here, until our
    layout engine responds to them.  In builds with MOZ_FLEXBOX enabled, this
    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        MozFlexDirection;
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozOrder;
                                         // raises(DOMException) on setting
 
            attribute DOMString        MozJustifyContent;
                                         // raises(DOMException) on setting
--- a/layout/base/nsStyleConsts.h
+++ b/layout/base/nsStyleConsts.h
@@ -403,16 +403,22 @@ static inline mozilla::css::Side operato
 #define NS_STYLE_ALIGN_ITEMS_FLEX_END           1
 #define NS_STYLE_ALIGN_ITEMS_CENTER             2
 #define NS_STYLE_ALIGN_ITEMS_BASELINE           3
 #define NS_STYLE_ALIGN_ITEMS_STRETCH            4
 
 // 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
 #define NS_STYLE_FLEX_DIRECTION_COLUMN          2
 #define NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE  3
 
 // See nsStylePosition
 // NOTE: This is the initial value of the integer-valued 'order' property
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -1518,16 +1518,26 @@ CSS_PROP_POSITION(
     CSS_PROP_DOMPROP_PREFIXED(AlignItems),
     CSS_PROPERTY_PARSE_VALUE,
     "",
     VARIANT_HK,
     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,
     CSS_PROP_DOMPROP_PREFIXED(FlexDirection),
     CSS_PROPERTY_PARSE_VALUE,
     "",
     VARIANT_HK,
     kFlexDirectionKTable,
     offsetof(nsStylePosition, mFlexDirection),
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -907,16 +907,27 @@ const PRInt32 nsCSSProps::kAlignItemsKTa
   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_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,
   eCSSKeyword_column,         NS_STYLE_FLEX_DIRECTION_COLUMN,
   eCSSKeyword_column_reverse, NS_STYLE_FLEX_DIRECTION_COLUMN_REVERSE,
   eCSSKeyword_UNKNOWN,-1
 };
 
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -347,16 +347,17 @@ public:
   static const PRInt32 kContentKTable[];
   static const PRInt32 kCursorKTable[];
   static const PRInt32 kDirectionKTable[];
   static const PRInt32 kDisplayKTable[];
   static const PRInt32 kElevationKTable[];
   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
   static const PRInt32 kFloatKTable[];
   static const PRInt32 kFloatEdgeKTable[];
   static const PRInt32 kFontKTable[];
   static const PRInt32 kFontSizeKTable[];
   static const PRInt32 kFontStretchKTable[];
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2931,16 +2931,42 @@ nsComputedDOMStyle::DoGetAlignItems()
   nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
   val->SetIdent(
     nsCSSProps::ValueToKeywordEnum(GetStylePosition()->mAlignItems,
                                    nsCSSProps::kAlignItemsKTable));
   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()
 {
   nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
   val->SetIdent(
     nsCSSProps::ValueToKeywordEnum(GetStylePosition()->mFlexDirection,
                                    nsCSSProps::kFlexDirectionKTable));
   return val;
 }
@@ -4646,16 +4672,17 @@ nsComputedDOMStyle::GetQueryableProperty
     COMPUTED_STYLE_MAP_ENTRY(z_index,                       ZIndex),
 
     /* ******************************* *\
      * Implementations of -moz- styles *
     \* ******************************* */
 
 #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),
     COMPUTED_STYLE_MAP_ENTRY(animation_duration,            AnimationDuration),
     COMPUTED_STYLE_MAP_ENTRY(animation_fill_mode,           AnimationFillMode),
     COMPUTED_STYLE_MAP_ENTRY(animation_iteration_count,     AnimationIterationCount),
     COMPUTED_STYLE_MAP_ENTRY(animation_name,                AnimationName),
     COMPUTED_STYLE_MAP_ENTRY(animation_play_state,          AnimationPlayState),
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -355,16 +355,17 @@ private:
   nsIDOMCSSValue* DoGetAnimationDirection();
   nsIDOMCSSValue* DoGetAnimationFillMode();
   nsIDOMCSSValue* DoGetAnimationIterationCount();
   nsIDOMCSSValue* DoGetAnimationPlayState();
 
 #ifdef MOZ_FLEXBOX
   /* CSS Flexbox properties */
   nsIDOMCSSValue* DoGetAlignItems();
+  nsIDOMCSSValue* DoGetAlignSelf();
   nsIDOMCSSValue* DoGetFlexDirection();
   nsIDOMCSSValue* DoGetOrder();
   nsIDOMCSSValue* DoGetJustifyContent();
 #endif // MOZ_FLEXBOX
 
   /* SVG properties */
   nsIDOMCSSValue* DoGetFill();
   nsIDOMCSSValue* DoGetStroke();
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -6426,16 +6426,70 @@ nsRuleNode::ComputePositionData(void* aS
 
 #ifdef MOZ_FLEXBOX
   // align-items: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForAlignItems(),
               pos->mAlignItems, canStoreInRuleTree,
               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,
               SETDSC_ENUMERATED, parentPos->mFlexDirection,
               NS_STYLE_FLEX_DIRECTION_ROW, 0, 0, 0, 0);
 
   // order: integer, inherit, initial
   SetDiscrete(*aRuleData->ValueForOrder(),
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -1122,16 +1122,17 @@ nsStylePosition::nsStylePosition(void)
   mMinWidth.SetCoordValue(0);
   mMaxWidth.SetNoneValue();
   mHeight.SetAutoValue();
   mMinHeight.SetCoordValue(0);
   mMaxHeight.SetNoneValue();
   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;
 #endif // MOZ_FLEXBOX
   mZIndex.SetAutoValue();
 }
 
 nsStylePosition::~nsStylePosition(void) 
@@ -1155,17 +1156,18 @@ nsChangeHint nsStylePosition::CalcDiffer
     return NS_CombineHint(hint, nsChangeHint_ReflowFrame);
   }
 
 #ifdef MOZ_FLEXBOX
   // Properties that apply to flex items:
   // 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);
   }
 
   // Properties that apply to flexbox containers:
 
   // flex-direction can swap a flexbox between vertical & horizontal.
   // align-items can change the sizing of a flexbox & the positioning
   // of its children.
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1090,16 +1090,17 @@ struct nsStylePosition {
   nsStyleCoord  mMinWidth;              // [reset] coord, percent, enum, calc
   nsStyleCoord  mMaxWidth;              // [reset] coord, percent, enum, calc, none
   nsStyleCoord  mHeight;                // [reset] coord, percent, calc, auto
   nsStyleCoord  mMinHeight;             // [reset] coord, percent, calc
   nsStyleCoord  mMaxHeight;             // [reset] coord, percent, calc, none
   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
 #endif // MOZ_FLEXBOX
   nsStyleCoord  mZIndex;                // [reset] integer, auto
 
   bool WidthDependsOnContainer() const
     { return WidthCoordDependsOnContainer(mWidth); }
--- a/layout/style/test/Makefile.in
+++ b/layout/style/test/Makefile.in
@@ -197,16 +197,22 @@ GARBAGE += css_properties.js
 		visited-lying-inner.html \
 		visited_image_loading.sjs \
 		visited_image_loading_frame.html \
 		visited_image_loading_frame_empty.html \
 		test_load_events_on_stylesheets.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 \
 		$(topsrcdir)/layout/reftests/svg/pseudo-classes-02-ref.svg \
 		$(topsrcdir)/layout/reftests/svg/as-image/lime100x100.svg \
 		$(topsrcdir)/layout/reftests/svg/as-image/svg-image-visited-1-helper.svg \
 		$(topsrcdir)/layout/reftests/svg/as-image/svg-image-visited-2-helper.svg \
 		$(NULL)
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -741,16 +741,25 @@ var gCSSProperties = {
 	"-moz-align-items": {
 		domProp: "MozAlignItems",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "stretch" ],
 		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,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "row" ],
 		other_values: [ "row-reverse", "column", "column-reverse" ],
 		invalid_values: [ "10px", "30%", "justify", "column wrap" ]
 	},
new file mode 100644
--- /dev/null
+++ b/layout/style/test/test_flexbox_align_self_auto.html
@@ -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>