Implement text-align-last. Bug 536557, r=dbaron
☠☠ backed out by a0c02bb5fbb4 ☠ ☠
authorSimon Montagu <smontagu@smontagu.org>
Thu, 12 Jan 2012 16:29:20 +0200
changeset 84344 6605cc311ec5ef7f9d63671c2392d7c307b30a4f
parent 84343 9217303c2e5c983cb032315066bba0cabd0c85aa
child 84345 16d8c02193abb8d8636b70385ed657c45fc65831
push id21842
push usermak77@bonardo.net
push dateFri, 13 Jan 2012 08:56:37 +0000
treeherdermozilla-central@8d4638feec54 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs536557
milestone12.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
Implement text-align-last. Bug 536557, r=dbaron
dom/interfaces/css/nsIDOMCSS2Properties.idl
layout/base/nsStyleConsts.h
layout/generic/nsBlockFrame.cpp
layout/generic/nsBlockFrame.h
layout/generic/nsLineLayout.cpp
layout/generic/nsLineLayout.h
layout/generic/nsTextFrameThebes.cpp
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/property_database.js
--- a/dom/interfaces/css/nsIDOMCSS2Properties.idl
+++ b/dom/interfaces/css/nsIDOMCSS2Properties.idl
@@ -615,16 +615,19 @@ interface nsIDOMCSS2Properties : nsISupp
 
            /* CSS3 properties */
            attribute DOMString        opacity;
                                         // raises(DOMException) on setting
 
            attribute DOMString        outlineOffset;
                                         // raises(DOMException) on setting
 
+           attribute DOMString        MozTextAlignLast;
+                                        // raises(DOMException) on setting
+
            /* Mozilla extensions */
            attribute DOMString        overflowX;
                                         // raises(DOMException) on setting
 
            attribute DOMString        overflowY;
                                         // raises(DOMException) on setting
 
            attribute DOMString        imeMode;
--- a/layout/base/nsStyleConsts.h
+++ b/layout/base/nsStyleConsts.h
@@ -620,22 +620,23 @@ static inline mozilla::css::Side operato
 // See nsStyleText
 #define NS_STYLE_TEXT_ALIGN_DEFAULT               0
 #define NS_STYLE_TEXT_ALIGN_LEFT                  1
 #define NS_STYLE_TEXT_ALIGN_RIGHT                 2
 #define NS_STYLE_TEXT_ALIGN_CENTER                3
 #define NS_STYLE_TEXT_ALIGN_JUSTIFY               4
 #define NS_STYLE_TEXT_ALIGN_CHAR                  5   //align based on a certain character, for table cell
 #define NS_STYLE_TEXT_ALIGN_END                   6
-#define NS_STYLE_TEXT_ALIGN_MOZ_CENTER            7
-#define NS_STYLE_TEXT_ALIGN_MOZ_RIGHT             8
-#define NS_STYLE_TEXT_ALIGN_MOZ_LEFT              9
+#define NS_STYLE_TEXT_ALIGN_AUTO                  7
+#define NS_STYLE_TEXT_ALIGN_MOZ_CENTER            8
+#define NS_STYLE_TEXT_ALIGN_MOZ_RIGHT             9
+#define NS_STYLE_TEXT_ALIGN_MOZ_LEFT             10
 // NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT is only used in data structs; it
 // is never present in stylesheets or computed data.
-#define NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT 10
+#define NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT 11
 // Note: make sure that the largest NS_STYLE_TEXT_ALIGN_* value is smaller than
 // the smallest NS_STYLE_VERTICAL_ALIGN_* value below!
 
 // See nsStyleText
 #define NS_STYLE_TEXT_BLINK_NONE                0
 #define NS_STYLE_TEXT_BLINK_BLINK               1
 
 // See nsStyleText, nsStyleFont
@@ -677,25 +678,25 @@ static inline mozilla::css::Side operato
 #define NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE_IN_OUT  4
 #define NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_START   5
 #define NS_STYLE_TRANSITION_TIMING_FUNCTION_STEP_END     6
 
 // See nsStyleText
 // Note: these values pickup after the text-align values because there
 // are a few html cases where an object can have both types of
 // alignment applied with a single attribute
-#define NS_STYLE_VERTICAL_ALIGN_BASELINE             11
-#define NS_STYLE_VERTICAL_ALIGN_SUB                  12
-#define NS_STYLE_VERTICAL_ALIGN_SUPER                13
-#define NS_STYLE_VERTICAL_ALIGN_TOP                  14
-#define NS_STYLE_VERTICAL_ALIGN_TEXT_TOP             15
-#define NS_STYLE_VERTICAL_ALIGN_MIDDLE               16
-#define NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM          17
-#define NS_STYLE_VERTICAL_ALIGN_BOTTOM               18
-#define NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE 19
+#define NS_STYLE_VERTICAL_ALIGN_BASELINE             12
+#define NS_STYLE_VERTICAL_ALIGN_SUB                  13
+#define NS_STYLE_VERTICAL_ALIGN_SUPER                14
+#define NS_STYLE_VERTICAL_ALIGN_TOP                  15
+#define NS_STYLE_VERTICAL_ALIGN_TEXT_TOP             16
+#define NS_STYLE_VERTICAL_ALIGN_MIDDLE               17
+#define NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM          18
+#define NS_STYLE_VERTICAL_ALIGN_BOTTOM               19
+#define NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE 20
 
 // See nsStyleVisibility
 #define NS_STYLE_VISIBILITY_HIDDEN              0
 #define NS_STYLE_VISIBILITY_VISIBLE             1
 #define NS_STYLE_VISIBILITY_COLLAPSE            2
 
 // See nsStyleText
 #define NS_STYLE_TABSIZE_INITIAL                8
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -4102,47 +4102,46 @@ nsBlockFrame::SplitLine(nsBlockReflowSta
 #ifdef DEBUG
     VerifyLines(true);
 #endif
   }
   return NS_OK;
 }
 
 bool
-nsBlockFrame::ShouldJustifyLine(nsBlockReflowState& aState,
-                                line_iterator aLine)
+nsBlockFrame::IsLastLine(nsBlockReflowState& aState,
+                         line_iterator aLine)
 {
   while (++aLine != end_lines()) {
     // There is another line
     if (0 != aLine->GetChildCount()) {
-      // If the next line is a block line then we must not justify
-      // this line because it means that this line is the last in a
+      // If the next line is a block line then this line is the last in a
       // group of inline lines.
-      return !aLine->IsBlock();
+      return aLine->IsBlock();
     }
     // The next line is empty, try the next one
   }
 
   // XXX Not sure about this part
   // Try our next-in-flows lines to answer the question
   nsBlockFrame* nextInFlow = (nsBlockFrame*) GetNextInFlow();
   while (nsnull != nextInFlow) {
     for (line_iterator line = nextInFlow->begin_lines(),
                    line_end = nextInFlow->end_lines();
          line != line_end;
          ++line)
     {
       if (0 != line->GetChildCount())
-        return !line->IsBlock();
+        return line->IsBlock();
     }
     nextInFlow = (nsBlockFrame*) nextInFlow->GetNextInFlow();
   }
 
   // This is the last line - so don't allow justification
-  return false;
+  return true;
 }
 
 bool
 nsBlockFrame::PlaceLine(nsBlockReflowState& aState,
                         nsLineLayout&       aLineLayout,
                         line_iterator       aLine,
                         nsFloatManager::SavedState *aFloatStateBeforeLine,
                         nsRect&             aFloatAvailableSpace,
@@ -4217,20 +4216,29 @@ nsBlockFrame::PlaceLine(nsBlockReflowSta
     }
   }
 #endif
 
   // Only block frames horizontally align their children because
   // inline frames "shrink-wrap" around their children (therefore
   // there is no extra horizontal space).
   const nsStyleText* styleText = GetStyleText();
-  bool allowJustify = NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign &&
-                        !aLineLayout.GetLineEndsInBR() &&
-                        ShouldJustifyLine(aState, aLine);
-  aLineLayout.HorizontalAlignFrames(aLine->mBounds, allowJustify);
+
+  /**
+   * text-align-last defaults to the same value as text-align when
+   * text-align-last is set to auto (unless when text-align is set to justify),
+   * so in that case we don't need to set isLastLine.
+   *
+   * In other words, isLastLine really means isLastLineAndWeCare.
+   */
+  bool isLastLine = ((NS_STYLE_TEXT_ALIGN_AUTO != styleText->mTextAlignLast ||
+                            NS_STYLE_TEXT_ALIGN_JUSTIFY == styleText->mTextAlign) &&
+                       (aLineLayout.GetLineEndsInBR() ||
+                        IsLastLine(aState, aLine)));
+  aLineLayout.HorizontalAlignFrames(aLine->mBounds, isLastLine);
   // XXX: not only bidi: right alignment can be broken after
   // RelativePositionFrames!!!
   // XXXldb Is something here considering relatively positioned frames at
   // other than their original positions?
 #ifdef IBMBIDI
   // XXXldb Why don't we do this earlier?
   if (aState.mPresContext->BidiEnabled()) {
     if (!aState.mPresContext->IsVisualMode() ||
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -525,18 +525,18 @@ protected:
    * @param aLine the line to mark dirty
    * @param aLineList the line list containing that line, null means the line
    *        is in 'mLines' of this frame.
    */
   nsresult MarkLineDirty(line_iterator aLine,
                          const nsLineList* aLineList = nsnull);
 
   // XXX where to go
-  bool ShouldJustifyLine(nsBlockReflowState& aState,
-                           line_iterator aLine);
+  bool IsLastLine(nsBlockReflowState& aState,
+                  line_iterator aLine);
 
   void DeleteLine(nsBlockReflowState& aState,
                   nsLineList::iterator aLine,
                   nsLineList::iterator aLineEnd);
 
   //----------------------------------------
   // Methods for individual frame reflow
 
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -108,17 +108,16 @@ nsLineLayout::nsLineLayout(nsPresContext
 {
   NS_ASSERTION(aFloatManager || aOuterReflowState->frame->GetType() ==
                                   nsGkAtoms::letterFrame,
                "float manager should be present");
   MOZ_COUNT_CTOR(nsLineLayout);
 
   // Stash away some style data that we need
   mStyleText = aOuterReflowState->frame->GetStyleText();
-  mTextAlign = mStyleText->mTextAlign;
   mLineNumber = 0;
   mFlags = 0; // default all flags to false except those that follow here...
   mTotalPlacedFrames = 0;
   mTopEdge = 0;
   mTrimmableWidth = 0;
 
   mInflationMinFontSize =
     nsLayoutUtils::InflationMinFontSizeFor(*aOuterReflowState);
@@ -2488,56 +2487,75 @@ nsLineLayout::ApplyFrameJustification(Pe
       pfd->mFrame->SetRect(pfd->mBounds);
     }
   }
   return deltaX;
 }
 
 void
 nsLineLayout::HorizontalAlignFrames(nsRect& aLineBounds,
-                                    bool aAllowJustify)
+                                    bool aIsLastLine)
 {
+  /**
+   * NOTE: aIsLastLine ain't necessarily so: it is correctly set by caller
+   * only in cases where the last line needs special handling.
+   */
   PerSpanData* psd = mRootSpan;
   NS_WARN_IF_FALSE(psd->mRightEdge != NS_UNCONSTRAINEDSIZE,
                    "have unconstrained width; this should only result from "
                    "very large sizes, not attempts at intrinsic width "
                    "calculation");
   nscoord availWidth = psd->mRightEdge - psd->mLeftEdge;
   nscoord remainingWidth = availWidth - aLineBounds.width;
 #ifdef NOISY_HORIZONTAL_ALIGN
     nsFrame::ListTag(stdout, mBlockReflowState->frame);
     printf(": availWidth=%d lineWidth=%d delta=%d\n",
            availWidth, aLineBounds.width, remainingWidth);
 #endif
   nscoord dx = 0;
 
   if (remainingWidth > 0) {
-    switch (mTextAlign) {
-      case NS_STYLE_TEXT_ALIGN_JUSTIFY:
-        // If this is not the last line then go ahead and justify the
-        // frames in the line.
-        if (aAllowJustify) {
-          PRInt32 numSpaces;
-          PRInt32 numLetters;
-            
-          ComputeJustificationWeights(psd, &numSpaces, &numLetters);
+    PRUint8 textAlign = mStyleText->mTextAlign;
+
+    /* 
+     * 'text-align-last: auto' is equivalent to the value of the 'text-align'
+     * property except when 'text-align' is set to 'justify', in which case it
+     * is 'justify' when 'text-justify' is 'distribute' and 'start' otherwise.
+     *
+     * XXX: the code below will have to change when we implement text-justify
+     */
+    if (aIsLastLine) {
+      if (mStyleText->mTextAlignLast == NS_STYLE_TEXT_ALIGN_AUTO) {
+        if (textAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY) {
+          textAlign = NS_STYLE_TEXT_ALIGN_DEFAULT;
+        }
+      } else {
+        textAlign = mStyleText->mTextAlignLast;
+      }
+    }
 
-          if (numSpaces > 0) {
-            FrameJustificationState state =
-              { numSpaces, numLetters, remainingWidth, 0, 0, 0, 0, 0 };
+    switch (textAlign) {
+      case NS_STYLE_TEXT_ALIGN_JUSTIFY:
+        PRInt32 numSpaces;
+        PRInt32 numLetters;
+            
+        ComputeJustificationWeights(psd, &numSpaces, &numLetters);
 
-            // Apply the justification, and make sure to update our linebox
-            // width to account for it.
-            aLineBounds.width += ApplyFrameJustification(psd, &state);
-            remainingWidth = availWidth - aLineBounds.width;
-            break;
-          }
+        if (numSpaces > 0) {
+          FrameJustificationState state =
+            { numSpaces, numLetters, remainingWidth, 0, 0, 0, 0, 0 };
+
+          // Apply the justification, and make sure to update our linebox
+          // width to account for it.
+          aLineBounds.width += ApplyFrameJustification(psd, &state);
+          remainingWidth = availWidth - aLineBounds.width;
+          break;
         }
-        // Fall through to the default case if we were told not to
-        // justify anything or could not justify to fill the space.
+        // Fall through to the default case if we could not justify to fill
+        // the space.
 
       case NS_STYLE_TEXT_ALIGN_DEFAULT:
         if (NS_STYLE_DIRECTION_LTR == psd->mDirection) {
           // default alignment for left-to-right is left so do nothing
           break;
         }
         // Fall through to align right case for default alignment
         // used when the direction is right-to-left.
--- a/layout/generic/nsLineLayout.h
+++ b/layout/generic/nsLineLayout.h
@@ -129,17 +129,17 @@ public:
   void RemoveBulletFrame(nsIFrame* aFrame) {
     PushFrame(aFrame);
   }
 
   void VerticalAlignLine();
 
   bool TrimTrailingWhiteSpace();
 
-  void HorizontalAlignFrames(nsRect& aLineBounds, bool aAllowJustify);
+  void HorizontalAlignFrames(nsRect& aLineBounds, bool aIsLastLine);
 
   /**
    * Handle all the relative positioning in the line, compute the
    * combined area (== overflow area) for the line, and handle view
    * sizing/positioning and the setting of the overflow rect.
    */
   void RelativePositionFrames(nsOverflowAreas& aOverflowAreas);
 
@@ -563,18 +563,16 @@ protected:
 #ifdef DEBUG
   PRInt32 mSpansAllocated, mSpansFreed;
   PRInt32 mFramesAllocated, mFramesFreed;
 #endif
   PLArenaPool mArena; // Per span and per frame data, 4 byte aligned
 
   PRUint32 mFlags;
 
-  PRUint8 mTextAlign;
-
   nsresult NewPerFrameData(PerFrameData** aResult);
 
   nsresult NewPerSpanData(PerSpanData** aResult);
 
   void FreeSpan(PerSpanData* psd);
 
   bool InBlockContext() const {
     return mSpanDepth == 0;
--- a/layout/generic/nsTextFrameThebes.cpp
+++ b/layout/generic/nsTextFrameThebes.cpp
@@ -1760,17 +1760,18 @@ BuildTextRunsScanner::BuildTextRunForFra
   userData->mMappedFlowCount = mMappedFlows.Length();
   userData->mLastFlowIndex = 0;
 
   PRUint32 currentTransformedTextOffset = 0;
 
   PRUint32 nextBreakIndex = 0;
   nsTextFrame* nextBreakBeforeFrame = GetNextBreakBeforeFrame(&nextBreakIndex);
   bool enabledJustification = mLineContainer &&
-    mLineContainer->GetStyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY;
+    (mLineContainer->GetStyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
+     mLineContainer->GetStyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY);
 
   PRUint32 i;
   const nsStyleText* textStyle = nsnull;
   const nsStyleFont* fontStyle = nsnull;
   nsStyleContext* lastStyleContext = nsnull;
   for (i = 0; i < mMappedFlows.Length(); ++i) {
     MappedFlow* mappedFlow = &mMappedFlows[i];
     nsTextFrame* f = mappedFlow->mStartFrame;
@@ -7669,17 +7670,18 @@ nsTextFrame::ReflowText(nsLineLayout& aL
       cachedNewlineOffset->mNewlineOffset = contentNewLineOffset;
     }
   } else if (cachedNewlineOffset) {
     mContent->DeleteProperty(nsGkAtoms::newline);
   }
 
   // Compute space and letter counts for justification, if required
   if (!textStyle->WhiteSpaceIsSignificant() &&
-      lineContainer->GetStyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY) {
+      (lineContainer->GetStyleText()->mTextAlign == NS_STYLE_TEXT_ALIGN_JUSTIFY ||
+       lineContainer->GetStyleText()->mTextAlignLast == NS_STYLE_TEXT_ALIGN_JUSTIFY)) {
     AddStateBits(TEXT_JUSTIFICATION_ENABLED);    // This will include a space for trailing whitespace, if any is present.
     // This is corrected for in nsLineLayout::TrimWhiteSpaceIn.
     PRInt32 numJustifiableCharacters =
       provider.ComputeJustifiableCharacters(offset, charsFit);
 
     NS_ASSERTION(numJustifiableCharacters <= charsFit,
                  "Bad justifiable character count");
     aLineLayout.SetTextJustificationWeights(numJustifiableCharacters,
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -2203,16 +2203,25 @@ CSS_PROP_TEXT(
     TextAlign,
     CSS_PROPERTY_PARSE_VALUE,
     // When we support aligning on a string, we can parse text-align
     // as a string....
     VARIANT_HK /* | VARIANT_STRING */,
     kTextAlignKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
+CSS_PROP_TEXT(
+    -moz-text-align-last,
+    text_align_last,
+    CSS_PROP_DOMPROP_PREFIXED(TextAlignLast),
+    CSS_PROPERTY_PARSE_VALUE,
+    VARIANT_HK,
+    kTextAlignLastKTable,
+    offsetof(nsStyleText, mTextAlignLast),
+    eStyleAnimType_None)
 CSS_PROP_SHORTHAND(
     text-decoration,
     text_decoration,
     TextDecoration,
     CSS_PROPERTY_PARSE_FUNCTION)
 CSS_PROP_TEXTRESET(
     -moz-text-blink,
     text_blink,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1195,16 +1195,27 @@ const PRInt32 nsCSSProps::kTextAlignKTab
   eCSSKeyword__moz_center, NS_STYLE_TEXT_ALIGN_MOZ_CENTER,
   eCSSKeyword__moz_right, NS_STYLE_TEXT_ALIGN_MOZ_RIGHT,
   eCSSKeyword__moz_left, NS_STYLE_TEXT_ALIGN_MOZ_LEFT,
   eCSSKeyword_start, NS_STYLE_TEXT_ALIGN_DEFAULT,
   eCSSKeyword_end, NS_STYLE_TEXT_ALIGN_END,
   eCSSKeyword_UNKNOWN,-1
 };
 
+const PRInt32 nsCSSProps::kTextAlignLastKTable[] = {
+  eCSSKeyword_auto, NS_STYLE_TEXT_ALIGN_AUTO,
+  eCSSKeyword_left, NS_STYLE_TEXT_ALIGN_LEFT,
+  eCSSKeyword_right, NS_STYLE_TEXT_ALIGN_RIGHT,
+  eCSSKeyword_center, NS_STYLE_TEXT_ALIGN_CENTER,
+  eCSSKeyword_justify, NS_STYLE_TEXT_ALIGN_JUSTIFY,
+  eCSSKeyword_start, NS_STYLE_TEXT_ALIGN_DEFAULT,
+  eCSSKeyword_end, NS_STYLE_TEXT_ALIGN_END,
+  eCSSKeyword_UNKNOWN,-1
+};
+
 const PRInt32 nsCSSProps::kTextBlinkKTable[] = {
   eCSSKeyword_none, NS_STYLE_TEXT_BLINK_NONE,
   eCSSKeyword_blink, NS_STYLE_TEXT_BLINK_BLINK,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const PRInt32 nsCSSProps::kTextDecorationLineKTable[] = {
   eCSSKeyword_none, NS_STYLE_TEXT_DECORATION_LINE_NONE,
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -403,16 +403,17 @@ public:
   static const PRInt32 kSpeakKTable[];
   static const PRInt32 kSpeakHeaderKTable[];
   static const PRInt32 kSpeakNumeralKTable[];
   static const PRInt32 kSpeakPunctuationKTable[];
   static const PRInt32 kSpeechRateKTable[];
   static const PRInt32 kStackSizingKTable[];
   static const PRInt32 kTableLayoutKTable[];
   static const PRInt32 kTextAlignKTable[];
+  static const PRInt32 kTextAlignLastKTable[];
   static const PRInt32 kTextBlinkKTable[];
   static const PRInt32 kTextDecorationLineKTable[];
   static const PRInt32 kTextDecorationStyleKTable[];
   static const PRInt32 kTextOverflowKTable[];
   static const PRInt32 kTextTransformKTable[];
   static const PRInt32 kTransitionTimingFunctionKTable[];
   static const PRInt32 kUnicodeBidiKTable[];
   static const PRInt32 kUserFocusKTable[];
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2397,16 +2397,26 @@ nsComputedDOMStyle::DoGetTextAlign()
   nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
   val->SetIdent(
     nsCSSProps::ValueToKeywordEnum(GetStyleText()->mTextAlign,
                                    nsCSSProps::kTextAlignKTable));
   return val;
 }
 
 nsIDOMCSSValue*
+nsComputedDOMStyle::DoGetTextAlignLast()
+{
+  nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
+  val->SetIdent(
+    nsCSSProps::ValueToKeywordEnum(GetStyleText()->mTextAlignLast,
+                                   nsCSSProps::kTextAlignLastKTable));
+  return val;
+}
+
+nsIDOMCSSValue*
 nsComputedDOMStyle::DoGetMozTextBlink()
 {
   nsROCSSPrimitiveValue* val = GetROCSSPrimitiveValue();
 
   val->SetIdent(
     nsCSSProps::ValueToKeywordEnum(GetStyleTextReset()->mTextBlink,
                                    nsCSSProps::kTextBlinkKTable));
 
@@ -4570,16 +4580,17 @@ nsComputedDOMStyle::GetQueryableProperty
     COMPUTED_STYLE_MAP_ENTRY(pointer_events,                PointerEvents),
     COMPUTED_STYLE_MAP_ENTRY(position,                      Position),
     COMPUTED_STYLE_MAP_ENTRY(quotes,                        Quotes),
     COMPUTED_STYLE_MAP_ENTRY(resize,                        Resize),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(right,                  Right),
     //// COMPUTED_STYLE_MAP_ENTRY(size,                     Size),
     COMPUTED_STYLE_MAP_ENTRY(table_layout,                  TableLayout),
     COMPUTED_STYLE_MAP_ENTRY(text_align,                    TextAlign),
+    COMPUTED_STYLE_MAP_ENTRY(text_align_last,               TextAlignLast),
     COMPUTED_STYLE_MAP_ENTRY(text_decoration,               TextDecoration),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(text_indent,            TextIndent),
     COMPUTED_STYLE_MAP_ENTRY(text_overflow,                 TextOverflow),
     COMPUTED_STYLE_MAP_ENTRY(text_shadow,                   TextShadow),
     COMPUTED_STYLE_MAP_ENTRY(text_transform,                TextTransform),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(top,                    Top),
     COMPUTED_STYLE_MAP_ENTRY(unicode_bidi,                  UnicodeBidi),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(vertical_align,         VerticalAlign),
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -308,16 +308,17 @@ private:
   nsIDOMCSSValue* DoGetListStyleImage();
   nsIDOMCSSValue* DoGetListStylePosition();
   nsIDOMCSSValue* DoGetListStyleType();
   nsIDOMCSSValue* DoGetImageRegion();
 
   /* Text Properties */
   nsIDOMCSSValue* DoGetLineHeight();
   nsIDOMCSSValue* DoGetTextAlign();
+  nsIDOMCSSValue* DoGetTextAlignLast();
   nsIDOMCSSValue* DoGetMozTextBlink();
   nsIDOMCSSValue* DoGetTextDecoration();
   nsIDOMCSSValue* DoGetMozTextDecorationColor();
   nsIDOMCSSValue* DoGetMozTextDecorationLine();
   nsIDOMCSSValue* DoGetMozTextDecorationStyle();
   nsIDOMCSSValue* DoGetTextIndent();
   nsIDOMCSSValue* DoGetTextOverflow();
   nsIDOMCSSValue* DoGetTextTransform();
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -3360,16 +3360,21 @@ nsRuleNode::ComputeTextData(void* aStart
     text->mTextAlign = (NS_STYLE_TEXT_ALIGN_DEFAULT == parentAlign) ?
       NS_STYLE_TEXT_ALIGN_CENTER : parentAlign;
   } else
     SetDiscrete(*textAlignValue, text->mTextAlign, canStoreInRuleTree,
                 SETDSC_ENUMERATED, parentText->mTextAlign,
                 NS_STYLE_TEXT_ALIGN_DEFAULT,
                 0, 0, 0, 0);
 
+  // text-align-last: enum, inherit, initial
+  SetDiscrete(*aRuleData->ValueForTextAlignLast(), text->mTextAlignLast,
+              canStoreInRuleTree, SETDSC_ENUMERATED, parentText->mTextAlignLast,
+              NS_STYLE_TEXT_ALIGN_AUTO, 0, 0, 0, 0);
+
   // text-indent: length, percent, calc, inherit, initial
   SetCoord(*aRuleData->ValueForTextIndent(), text->mTextIndent, parentText->mTextIndent,
            SETCOORD_LPH | SETCOORD_INITIAL_ZERO | SETCOORD_STORE_CALC,
            aContext, mPresContext, canStoreInRuleTree);
 
   // text-transform: enum, inherit, initial
   SetDiscrete(*aRuleData->ValueForTextTransform(), text->mTextTransform, canStoreInRuleTree,
               SETDSC_ENUMERATED, parentText->mTextTransform,
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2809,16 +2809,17 @@ CalcShadowDifference(nsCSSShadowArray* l
 // --------------------
 // nsStyleText
 //
 
 nsStyleText::nsStyleText(void)
 { 
   MOZ_COUNT_CTOR(nsStyleText);
   mTextAlign = NS_STYLE_TEXT_ALIGN_DEFAULT;
+  mTextAlignLast = NS_STYLE_TEXT_ALIGN_AUTO;
   mTextTransform = NS_STYLE_TEXT_TRANSFORM_NONE;
   mWhiteSpace = NS_STYLE_WHITESPACE_NORMAL;
   mWordWrap = NS_STYLE_WORDWRAP_NORMAL;
   mHyphens = NS_STYLE_HYPHENS_MANUAL;
   mTextSizeAdjust = NS_STYLE_TEXT_SIZE_ADJUST_AUTO;
 
   mLetterSpacing.SetNormalValue();
   mLineHeight.SetNormalValue();
@@ -2826,16 +2827,17 @@ nsStyleText::nsStyleText(void)
   mWordSpacing = 0;
 
   mTextShadow = nsnull;
   mTabSize = NS_STYLE_TABSIZE_INITIAL;
 }
 
 nsStyleText::nsStyleText(const nsStyleText& aSource)
   : mTextAlign(aSource.mTextAlign),
+    mTextAlignLast(aSource.mTextAlignLast),
     mTextTransform(aSource.mTextTransform),
     mWhiteSpace(aSource.mWhiteSpace),
     mWordWrap(aSource.mWordWrap),
     mHyphens(aSource.mHyphens),
     mTextSizeAdjust(aSource.mTextSizeAdjust),
     mTabSize(aSource.mTabSize),
     mLetterSpacing(aSource.mLetterSpacing),
     mLineHeight(aSource.mLineHeight),
@@ -2854,16 +2856,17 @@ nsStyleText::~nsStyleText(void)
 nsChangeHint nsStyleText::CalcDifference(const nsStyleText& aOther) const
 {
   if (NewlineIsSignificant() != aOther.NewlineIsSignificant()) {
     // This may require construction of suppressed text frames
     return NS_STYLE_HINT_FRAMECHANGE;
   }
 
   if ((mTextAlign != aOther.mTextAlign) ||
+      (mTextAlignLast != aOther.mTextAlignLast) ||
       (mTextTransform != aOther.mTextTransform) ||
       (mWhiteSpace != aOther.mWhiteSpace) ||
       (mWordWrap != aOther.mWordWrap) ||
       (mHyphens != aOther.mHyphens) ||
       (mTextSizeAdjust != aOther.mTextSizeAdjust) ||
       (mLetterSpacing != aOther.mLetterSpacing) ||
       (mLineHeight != aOther.mLineHeight) ||
       (mTextIndent != aOther.mTextIndent) ||
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1272,16 +1272,17 @@ struct nsStyleText {
 
   nsChangeHint CalcDifference(const nsStyleText& aOther) const;
 #ifdef DEBUG
   static nsChangeHint MaxDifference();
 #endif
   static bool ForceCompare() { return false; }
 
   PRUint8 mTextAlign;                   // [inherited] see nsStyleConsts.h
+  PRUint8 mTextAlignLast;               // [inherited] see nsStyleConsts.h
   PRUint8 mTextTransform;               // [inherited] see nsStyleConsts.h
   PRUint8 mWhiteSpace;                  // [inherited] see nsStyleConsts.h
   PRUint8 mWordWrap;                    // [inherited] see nsStyleConsts.h
   PRUint8 mHyphens;                     // [inherited] see nsStyleConsts.h
   PRUint8 mTextSizeAdjust;              // [inherited] see nsStyleConsts.h
   PRInt32 mTabSize;                     // [inherited] see nsStyleConsts.h
 
   nsStyleCoord  mLetterSpacing;         // [inherited] coord, normal
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -2641,16 +2641,24 @@ var gCSSProperties = {
 		domProp: "textAlign",
 		inherited: true,
 		type: CSS_TYPE_LONGHAND,
 		// don't know whether left and right are same as start
 		initial_values: [ "start" ],
 		other_values: [ "center", "justify", "end" ],
 		invalid_values: []
 	},
+	"-moz-text-align-last": {
+		domProp: "MozTextAlignLast",
+		inherited: true,
+		type: CSS_TYPE_LONGHAND,
+		initial_values: [ "auto" ],
+		other_values: [ "center", "justify", "start", "end", "left", "right" ],
+		invalid_values: []
+	},
 	"-moz-text-blink": {
 		domProp: "MozTextBlink",
 		inherited: false,
 		type: CSS_TYPE_LONGHAND,
 		initial_values: [ "none" ],
 		other_values: [ "blink" ],
 		invalid_values: [ "underline", "overline", "line-through", "none underline", "underline blink", "blink underline" ]
 	},