Implement text-align-last. Bug 536557, r=dbaron
authorSimon Montagu <smontagu@smontagu.org>
Thu, 12 Jan 2012 19:52:21 +0200
changeset 85581 aa62396ae6bdd594211e3acab8efa6cc02ee7b34
parent 85580 69eb27cd5313d32e2377316aeee579d7e33e60d9
child 85582 37b69f47d522c61a43e9ac8c6ddc9022440e981d
push id805
push userakeybl@mozilla.com
push dateWed, 01 Feb 2012 18:17:35 +0000
treeherdermozilla-aurora@6fb3bf232436 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdbaron
bugs536557
milestone12.0a1
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));
 
@@ -4642,16 +4652,17 @@ nsComputedDOMStyle::GetQueryableProperty
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_bottomLeft, OutlineRadiusBottomLeft),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_bottomRight,OutlineRadiusBottomRight),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_topLeft,    OutlineRadiusTopLeft),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_outline_radius_topRight,   OutlineRadiusTopRight),
     COMPUTED_STYLE_MAP_ENTRY(perspective,                   MozPerspective),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(perspective_origin,     MozPerspectiveOrigin),
     COMPUTED_STYLE_MAP_ENTRY(stack_sizing,                  StackSizing),
     COMPUTED_STYLE_MAP_ENTRY(_moz_tab_size,                 MozTabSize),
+    COMPUTED_STYLE_MAP_ENTRY(text_align_last,               TextAlignLast),
     COMPUTED_STYLE_MAP_ENTRY(text_blink,                    MozTextBlink),
     COMPUTED_STYLE_MAP_ENTRY(text_decoration_color,         MozTextDecorationColor),
     COMPUTED_STYLE_MAP_ENTRY(text_decoration_line,          MozTextDecorationLine),
     COMPUTED_STYLE_MAP_ENTRY(text_decoration_style,         MozTextDecorationStyle),
     COMPUTED_STYLE_MAP_ENTRY(text_size_adjust,              TextSizeAdjust),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_transform,         MozTransform),
     COMPUTED_STYLE_MAP_ENTRY_LAYOUT(_moz_transform_origin,  MozTransformOrigin),
     COMPUTED_STYLE_MAP_ENTRY(transform_style,               MozTransformStyle),
--- 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" ]
 	},