Merge m-i to m-c
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 27 Oct 2013 21:44:08 -0700
changeset 167201 e0c5c0f62aef5e8459d1d93ce5e9b3261f253c3f
parent 167179 36fd56ac71ddf21d05eef1041f81026183cf601a (current diff)
parent 167200 222175ec725f3d2e2bfda3fe066218db6749b627 (diff)
child 167204 59ff3a2a708a6d95894e4bcf6c6ed8c75190e30c
push id428
push userbbajaj@mozilla.com
push dateTue, 28 Jan 2014 00:16:25 +0000
treeherdermozilla-release@cd72a7ff3a75 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone27.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
Merge m-i to m-c
--- a/content/canvas/src/CanvasRenderingContext2D.cpp
+++ b/content/canvas/src/CanvasRenderingContext2D.cpp
@@ -2854,37 +2854,37 @@ CanvasRenderingContext2D::SetMozDashOffs
 {
   ContextState& state = CurrentState();
   if (!state.dash.IsEmpty()) {
     state.dashOffset = mozDashOffset;
   }
 }
 
 void
-CanvasRenderingContext2D::SetLineDash(const mozilla::dom::AutoSequence<double>& mSegments) {
+CanvasRenderingContext2D::SetLineDash(const mozilla::dom::AutoSequence<double>& aSegments) {
   FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
   dash.Clear();
 
-  for(mozilla::dom::AutoSequence<double>::index_type x = 0; x < mSegments.Length(); x++) {
-    dash.AppendElement(mSegments[x]);
+  for (uint32_t x = 0; x < aSegments.Length(); x++) {
+    dash.AppendElement(aSegments[x]);
   }
-  if(mSegments.Length()%2) { // If the number of elements is odd, concatenate again
-    for(mozilla::dom::AutoSequence<double>::index_type x = 0; x < mSegments.Length(); x++) {
-      dash.AppendElement(mSegments[x]);
+  if (aSegments.Length() % 2) { // If the number of elements is odd, concatenate again
+    for (uint32_t x = 0; x < aSegments.Length(); x++) {
+      dash.AppendElement(aSegments[x]);
     }
   }
 }
 
 void
-CanvasRenderingContext2D::GetLineDash(nsTArray<double>& mSegments) const {
+CanvasRenderingContext2D::GetLineDash(nsTArray<double>& aSegments) const {
   const FallibleTArray<mozilla::gfx::Float>& dash = CurrentState().dash;
-  mSegments.Clear();
-
-  for(FallibleTArray<mozilla::gfx::Float>::index_type x = 0; x < dash.Length(); x++) {
-    mSegments.AppendElement(dash[x]);
+  aSegments.Clear();
+
+  for (uint32_t x = 0; x < dash.Length(); x++) {
+    aSegments.AppendElement(dash[x]);
   }
 }
 
 void
 CanvasRenderingContext2D::SetLineDashOffset(double mOffset) {
   CurrentState().dashOffset = mOffset;
 }
 
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -1536,46 +1536,59 @@ TabChild::ProcessUpdateFrame(const Frame
   {
     if (!mGlobal || !mTabChildGlobal) {
         return true;
     }
 
     CSSRect cssCompositedRect = aFrameMetrics.CalculateCompositedRectInCssPixels();
     // The BrowserElementScrolling helper must know about these updated metrics
     // for other functions it performs, such as double tap handling.
+    // Note, %f must not be used because it is locale specific!
     nsCString data;
-    data += nsPrintfCString("{ \"x\" : %d", NS_lround(aFrameMetrics.mScrollOffset.x));
-    data += nsPrintfCString(", \"y\" : %d", NS_lround(aFrameMetrics.mScrollOffset.y));
-    data += nsPrintfCString(", \"viewport\" : ");
-        data += nsPrintfCString("{ \"width\" : %f", aFrameMetrics.mViewport.width);
-        data += nsPrintfCString(", \"height\" : %f", aFrameMetrics.mViewport.height);
-        data += nsPrintfCString(" }");
-    data += nsPrintfCString(", \"displayPort\" : ");
-        data += nsPrintfCString("{ \"x\" : %f", aFrameMetrics.mDisplayPort.x);
-        data += nsPrintfCString(", \"y\" : %f", aFrameMetrics.mDisplayPort.y);
-        data += nsPrintfCString(", \"width\" : %f", aFrameMetrics.mDisplayPort.width);
-        data += nsPrintfCString(", \"height\" : %f", aFrameMetrics.mDisplayPort.height);
-        data += nsPrintfCString(" }");
-    data += nsPrintfCString(", \"compositionBounds\" : ");
-        data += nsPrintfCString("{ \"x\" : %d", aFrameMetrics.mCompositionBounds.x);
-        data += nsPrintfCString(", \"y\" : %d", aFrameMetrics.mCompositionBounds.y);
-        data += nsPrintfCString(", \"width\" : %d", aFrameMetrics.mCompositionBounds.width);
-        data += nsPrintfCString(", \"height\" : %d", aFrameMetrics.mCompositionBounds.height);
-        data += nsPrintfCString(" }");
-    data += nsPrintfCString(", \"cssPageRect\" : ");
-        data += nsPrintfCString("{ \"x\" : %f", aFrameMetrics.mScrollableRect.x);
-        data += nsPrintfCString(", \"y\" : %f", aFrameMetrics.mScrollableRect.y);
-        data += nsPrintfCString(", \"width\" : %f", aFrameMetrics.mScrollableRect.width);
-        data += nsPrintfCString(", \"height\" : %f", aFrameMetrics.mScrollableRect.height);
-        data += nsPrintfCString(" }");
-    data += nsPrintfCString(", \"cssCompositedRect\" : ");
-            data += nsPrintfCString("{ \"width\" : %f", cssCompositedRect.width);
-            data += nsPrintfCString(", \"height\" : %f", cssCompositedRect.height);
-            data += nsPrintfCString(" }");
-    data += nsPrintfCString(" }");
+    data.AppendPrintf("{ \"x\" : %d", NS_lround(aFrameMetrics.mScrollOffset.x));
+    data.AppendPrintf(", \"y\" : %d", NS_lround(aFrameMetrics.mScrollOffset.y));
+    data.AppendLiteral(", \"viewport\" : ");
+        data.AppendLiteral("{ \"width\" : ");
+        data.AppendFloat(aFrameMetrics.mViewport.width);
+        data.AppendLiteral(", \"height\" : ");
+        data.AppendFloat(aFrameMetrics.mViewport.height);
+        data.AppendLiteral(" }");
+    data.AppendLiteral(", \"displayPort\" : ");
+        data.AppendLiteral("{ \"x\" : ");
+        data.AppendFloat(aFrameMetrics.mDisplayPort.x);
+        data.AppendLiteral(", \"y\" : ");
+        data.AppendFloat(aFrameMetrics.mDisplayPort.y);
+        data.AppendLiteral(", \"width\" : ");
+        data.AppendFloat(aFrameMetrics.mDisplayPort.width);
+        data.AppendLiteral(", \"height\" : ");
+        data.AppendFloat(aFrameMetrics.mDisplayPort.height);
+        data.AppendLiteral(" }");
+    data.AppendLiteral(", \"compositionBounds\" : ");
+        data.AppendPrintf("{ \"x\" : %d", aFrameMetrics.mCompositionBounds.x);
+        data.AppendPrintf(", \"y\" : %d", aFrameMetrics.mCompositionBounds.y);
+        data.AppendPrintf(", \"width\" : %d", aFrameMetrics.mCompositionBounds.width);
+        data.AppendPrintf(", \"height\" : %d", aFrameMetrics.mCompositionBounds.height);
+        data.AppendLiteral(" }");
+    data.AppendLiteral(", \"cssPageRect\" : ");
+        data.AppendLiteral("{ \"x\" : ");
+        data.AppendFloat(aFrameMetrics.mScrollableRect.x);
+        data.AppendLiteral(", \"y\" : ");
+        data.AppendFloat(aFrameMetrics.mScrollableRect.y);
+        data.AppendLiteral(", \"width\" : ");
+        data.AppendFloat(aFrameMetrics.mScrollableRect.width);
+        data.AppendLiteral(", \"height\" : ");
+        data.AppendFloat(aFrameMetrics.mScrollableRect.height);
+        data.AppendLiteral(" }");
+    data.AppendLiteral(", \"cssCompositedRect\" : ");
+        data.AppendLiteral("{ \"width\" : ");
+        data.AppendFloat(cssCompositedRect.width);
+        data.AppendLiteral(", \"height\" : ");
+        data.AppendFloat(cssCompositedRect.height);
+        data.AppendLiteral(" }");
+    data.AppendLiteral(" }");
 
     DispatchMessageManagerMessage(NS_LITERAL_STRING("Viewport:Change"), data);
 
     nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
 
     APZCCallbackHelper::UpdateRootFrame(utils, aFrameMetrics);
 
     mLastMetrics = aFrameMetrics;
--- a/js/src/jit-test/tests/TypedObject/bug920463.js
+++ b/js/src/jit-test/tests/TypedObject/bug920463.js
@@ -1,10 +1,10 @@
 if (!this.hasOwnProperty("TypedObject"))
-  throw new TypeError();
+  quit();
 
 var StructType = TypedObject.StructType;
 var float64 = TypedObject.float64;
 
 var PointType3 = new StructType({ x: float64, y: float64});
 function xPlusY(p) {
   return p.x + p.y;
 }
--- a/js/src/vm/MemoryMetrics.cpp
+++ b/js/src/vm/MemoryMetrics.cpp
@@ -87,18 +87,17 @@ InefficientNonFlatteningStringHashPolicy
         c2 = ownedChars2;
     }
 
     return PodEqual(c1, c2, k->length());
 }
 
 } // namespace js
 
-namespace JS
-{
+namespace JS {
 
 NotableStringInfo::NotableStringInfo()
     : bufferSize(0),
       buffer(0)
 {}
 
 NotableStringInfo::NotableStringInfo(JSString *str, const StringInfo &info)
     : StringInfo(info)
@@ -242,16 +241,22 @@ StatsArenaCallback(JSRuntime *rt, void *
 }
 
 static CompartmentStats *
 GetCompartmentStats(JSCompartment *comp)
 {
     return static_cast<CompartmentStats *>(comp->compartmentStats);
 }
 
+enum Granularity {
+    FineGrained,    // Corresponds to CollectRuntimeStats()
+    CoarseGrained   // Corresponds to AddSizeOfTab()
+};
+
+template <Granularity granularity>
 static void
 StatsCellCallback(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind,
                   size_t thingSize)
 {
     StatsClosure *closure = static_cast<StatsClosure *>(data);
     RuntimeStats *rtStats = closure->rtStats;
     ZoneStats *zStats = rtStats->currZoneStats;
     switch (traceKind) {
@@ -278,26 +283,31 @@ StatsCellCallback(JSRuntime *rt, void *d
       }
 
       case JSTRACE_STRING: {
         JSString *str = static_cast<JSString *>(thing);
 
         size_t strCharsSize = str->sizeOfExcludingThis(rtStats->mallocSizeOf_);
         MOZ_ASSERT_IF(str->isShort(), strCharsSize == 0);
 
-        size_t shortStringThingSize = str->isShort() ? thingSize : 0;
+        size_t shortStringThingSize  =  str->isShort() ? thingSize : 0;
         size_t normalStringThingSize = !str->isShort() ? thingSize : 0;
 
-        ZoneStats::StringsHashMap::AddPtr p = zStats->strings.lookupForAdd(str);
-        if (!p) {
-            JS::StringInfo info(str->length(), shortStringThingSize,
-                                normalStringThingSize, strCharsSize);
-            zStats->strings.add(p, str, info);
-        } else {
-            p->value.add(shortStringThingSize, normalStringThingSize, strCharsSize);
+        // This string hashing is expensive.  Its results are unused when doing
+        // coarse-grained measurements, and skipping it more than doubles the
+        // profile speed for complex pages such as gmail.com.
+        if (granularity == FineGrained) {
+            ZoneStats::StringsHashMap::AddPtr p = zStats->strings.lookupForAdd(str);
+            if (!p) {
+                JS::StringInfo info(str->length(), shortStringThingSize,
+                                    normalStringThingSize, strCharsSize);
+                zStats->strings.add(p, str, info);
+            } else {
+                p->value.add(shortStringThingSize, normalStringThingSize, strCharsSize);
+            }
         }
 
         zStats->stringsShortGCHeap += shortStringThingSize;
         zStats->stringsNormalGCHeap += normalStringThingSize;
         zStats->stringsNormalMallocHeap += strCharsSize;
 
         break;
       }
@@ -439,17 +449,17 @@ JS::CollectRuntimeStats(JSRuntime *rt, R
     IterateChunks(rt, &rtStats->gcHeapDecommittedArenas,
                   DecommittedArenasChunkCallback);
 
     // Take the per-compartment measurements.
     StatsClosure closure(rtStats, opv);
     if (!closure.init())
         return false;
     IterateZonesCompartmentsArenasCells(rt, &closure, StatsZoneCallback, StatsCompartmentCallback,
-                                        StatsArenaCallback, StatsCellCallback);
+                                        StatsArenaCallback, StatsCellCallback<FineGrained>);
 
     // Take the "explicit/js/runtime/" measurements.
     rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime);
 
     for (size_t i = 0; i < rtStats->zoneStatsVector.length(); i++) {
         ZoneStats &zStats = rtStats->zoneStatsVector[i];
 
         rtStats->zTotals.add(zStats);
@@ -559,17 +569,17 @@ AddSizeOfTab(JSRuntime *rt, JSObject *ob
         return false;
 
     // Take the per-compartment measurements.
     StatsClosure closure(&rtStats, opv);
     if (!closure.init())
         return false;
     IterateZoneCompartmentsArenasCells(rt, zone, &closure, StatsZoneCallback,
                                        StatsCompartmentCallback, StatsArenaCallback,
-                                       StatsCellCallback);
+                                       StatsCellCallback<CoarseGrained>);
 
     JS_ASSERT(rtStats.zoneStatsVector.length() == 1);
     rtStats.zTotals.add(rtStats.zoneStatsVector[0]);
 
     for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) {
         CompartmentStats &cStats = rtStats.compartmentStatsVector[i];
         rtStats.cTotals.add(cStats);
     }
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -91,16 +91,17 @@ using namespace mozilla::layout;
 
 using mozilla::image::Angle;
 using mozilla::image::Flip;
 using mozilla::image::ImageOps;
 using mozilla::image::Orientation;
 
 #define FLEXBOX_ENABLED_PREF_NAME "layout.css.flexbox.enabled"
 #define STICKY_ENABLED_PREF_NAME "layout.css.sticky.enabled"
+#define TEXT_ALIGN_TRUE_ENABLED_PREF_NAME "layout.css.text-align-true-value.enabled"
 
 #ifdef DEBUG
 // TODO: remove, see bug 598468.
 bool nsLayoutUtils::gPreventAssertInCompareTreePosition = false;
 #endif // DEBUG
 
 typedef FrameMetrics::ViewID ViewID;
 
@@ -204,16 +205,55 @@ StickyEnabledPrefChangeCallback(const ch
   // OK -- now, stomp on or restore the "sticky" entry in kPositionKTable,
   // depending on whether the sticky pref is enabled vs. disabled.
   nsCSSProps::kPositionKTable[sIndexOfStickyInPositionTable] =
     isStickyEnabled ? eCSSKeyword_sticky : eCSSKeyword_UNKNOWN;
 
   return 0;
 }
 
+// When the pref "layout.css.text-align-true-value.enabled" changes, this
+// function is called to let us update kTextAlignKTable & kTextAlignLastKTable,
+// to selectively disable or restore the entries for "true" in those tables.
+static int
+TextAlignTrueEnabledPrefChangeCallback(const char* aPrefName, void* aClosure)
+{
+  NS_ASSERTION(strcmp(aPrefName, TEXT_ALIGN_TRUE_ENABLED_PREF_NAME) == 0,
+               "Did you misspell " TEXT_ALIGN_TRUE_ENABLED_PREF_NAME " ?");
+
+  static bool sIsInitialized;
+  static int32_t sIndexOfTrueInTextAlignTable;
+  static int32_t sIndexOfTrueInTextAlignLastTable;
+  bool isTextAlignTrueEnabled =
+    Preferences::GetBool(TEXT_ALIGN_TRUE_ENABLED_PREF_NAME, false);
+
+  if (!sIsInitialized) {
+    // First run: find the position of "true" in kTextAlignKTable.
+    sIndexOfTrueInTextAlignTable =
+      nsCSSProps::FindIndexOfKeyword(eCSSKeyword_true,
+                                     nsCSSProps::kTextAlignKTable);
+    // First run: find the position of "true" in kTextAlignLastKTable.
+    sIndexOfTrueInTextAlignLastTable =
+      nsCSSProps::FindIndexOfKeyword(eCSSKeyword_true,
+                                     nsCSSProps::kTextAlignLastKTable);
+    sIsInitialized = true;
+  }
+
+  // OK -- now, stomp on or restore the "true" entry in the keyword tables,
+  // depending on whether the pref is enabled vs. disabled.
+  MOZ_ASSERT(sIndexOfTrueInTextAlignTable >= 0);
+  nsCSSProps::kTextAlignKTable[sIndexOfTrueInTextAlignTable] =
+    isTextAlignTrueEnabled ? eCSSKeyword_true : eCSSKeyword_UNKNOWN;
+  MOZ_ASSERT(sIndexOfTrueInTextAlignLastTable >= 0);
+  nsCSSProps::kTextAlignLastKTable[sIndexOfTrueInTextAlignLastTable] =
+    isTextAlignTrueEnabled ? eCSSKeyword_true : eCSSKeyword_UNKNOWN;
+
+  return 0;
+}
+
 template <class AnimationsOrTransitions>
 static AnimationsOrTransitions*
 HasAnimationOrTransition(nsIContent* aContent,
                          nsIAtom* aAnimationProperty,
                          nsCSSProperty aProperty)
 {
   AnimationsOrTransitions* animations =
     static_cast<AnimationsOrTransitions*>(aContent->GetProperty(aAnimationProperty));
@@ -442,16 +482,32 @@ nsLayoutUtils::UnsetValueEnabled()
     Preferences::AddBoolVarCache(&sUnsetValueEnabled,
                                  "layout.css.unset-value.enabled",
                                  false);
   }
 
   return sUnsetValueEnabled;
 }
 
+bool
+nsLayoutUtils::IsTextAlignTrueValueEnabled()
+{
+  static bool sTextAlignTrueValueEnabled;
+  static bool sTextAlignTrueValueEnabledPrefCached = false;
+
+  if (!sTextAlignTrueValueEnabledPrefCached) {
+    sTextAlignTrueValueEnabledPrefCached = true;
+    Preferences::AddBoolVarCache(&sTextAlignTrueValueEnabled,
+                                 TEXT_ALIGN_TRUE_ENABLED_PREF_NAME,
+                                 false);
+  }
+
+  return sTextAlignTrueValueEnabled;
+}
+
 void
 nsLayoutUtils::UnionChildOverflow(nsIFrame* aFrame,
                                   nsOverflowAreas& aOverflowAreas)
 {
   // Iterate over all children except pop-ups.
   const nsIFrame::ChildListIDs skip(nsIFrame::kPopupList |
                                     nsIFrame::kSelectPopupList);
   for (nsIFrame::ChildListIterator childLists(aFrame);
@@ -5119,16 +5175,20 @@ nsLayoutUtils::Initialize()
                                "nglayout.debug.invalidation");
 
   Preferences::RegisterCallback(FlexboxEnabledPrefChangeCallback,
                                 FLEXBOX_ENABLED_PREF_NAME);
   FlexboxEnabledPrefChangeCallback(FLEXBOX_ENABLED_PREF_NAME, nullptr);
   Preferences::RegisterCallback(StickyEnabledPrefChangeCallback,
                                 STICKY_ENABLED_PREF_NAME);
   StickyEnabledPrefChangeCallback(STICKY_ENABLED_PREF_NAME, nullptr);
+  Preferences::RegisterCallback(TextAlignTrueEnabledPrefChangeCallback,
+                                TEXT_ALIGN_TRUE_ENABLED_PREF_NAME);
+  TextAlignTrueEnabledPrefChangeCallback(TEXT_ALIGN_TRUE_ENABLED_PREF_NAME,
+                                         nullptr);
 
   nsComputedDOMStyle::RegisterPrefChangeCallbacks();
 }
 
 /* static */
 void
 nsLayoutUtils::Shutdown()
 {
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1672,16 +1672,22 @@ public:
   static bool CSSFiltersEnabled();
 
   /**
    * Checks whether support for the CSS-wide "unset" value is enabled.
    */
   static bool UnsetValueEnabled();
 
   /**
+   * Checks whether support for the CSS text-align (and -moz-text-align-last)
+   * 'true' value is enabled.
+   */
+  static bool IsTextAlignTrueValueEnabled();
+
+  /**
    * Unions the overflow areas of all non-popup children of aFrame with
    * aOverflowAreas.
    */
   static void UnionChildOverflow(nsIFrame* aFrame,
                                  nsOverflowAreas& aOverflowAreas);
 
   /**
    * Return the font size inflation *ratio* for a given frame.  This is
--- a/layout/generic/nsLineLayout.cpp
+++ b/layout/generic/nsLineLayout.cpp
@@ -2465,42 +2465,43 @@ nsLineLayout::HorizontalAlignFrames(nsRe
   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);
+  nsFrame::ListTag(stdout, mBlockReflowState->frame);
+  printf(": availWidth=%d lineWidth=%d delta=%d\n",
+         availWidth, aLineBounds.width, remainingWidth);
 #endif
-  nscoord dx = 0;
-
-  if (remainingWidth > 0 &&
-      !(mBlockReflowState->frame->IsSVGText())) {
-    uint8_t 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;
+  // '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
+  //
+  nscoord dx = 0;
+  uint8_t textAlign = mStyleText->mTextAlign;
+  bool textAlignTrue = mStyleText->mTextAlignTrue;
+  if (aIsLastLine) {
+    textAlignTrue = mStyleText->mTextAlignLastTrue;
+    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 ((remainingWidth > 0 || textAlignTrue) &&
+      !(mBlockReflowState->frame->IsSVGText())) {
 
     switch (textAlign) {
       case NS_STYLE_TEXT_ALIGN_JUSTIFY:
         int32_t numSpaces;
         int32_t numLetters;
             
         ComputeJustificationWeights(psd, &numSpaces, &numLetters);
 
@@ -2544,17 +2545,17 @@ nsLineLayout::HorizontalAlignFrames(nsRe
         break;
 
       case NS_STYLE_TEXT_ALIGN_CENTER:
       case NS_STYLE_TEXT_ALIGN_MOZ_CENTER:
         dx = remainingWidth / 2;
         break;
     }
   }
-  else if (remainingWidth < 0) {
+  else if (remainingWidth < 0 || textAlignTrue) {
     if (NS_STYLE_DIRECTION_RTL == psd->mDirection) {
       dx = remainingWidth;
       psd->mX += dx;
       psd->mLeftEdge += dx;
     }
   }
 
   if (NS_STYLE_DIRECTION_RTL == psd->mDirection &&
--- a/layout/reftests/text/reftest.list
+++ b/layout/reftests/text/reftest.list
@@ -276,8 +276,10 @@ pref(gfx.font_rendering.graphite.enabled
 != auto-hyphenation-sv-1.html auto-hyphenation-sv-1-notref.html # verify swedish != english
 == auto-hyphenation-tr-1.html auto-hyphenation-tr-1-ref.html
 == auto-hyphenation-uk-1.html auto-hyphenation-uk-1-ref.html
 
 # osx-font-smoothing - with and without subpixel AA, only under OSX
 fails-if(!cocoaWidget||OSX==10.6||OSX==10.7) != osx-font-smoothing.html osx-font-smoothing-ref.html
 fails-if(!cocoaWidget||OSX==10.6||OSX==10.7) != osx-font-smoothing-2.html osx-font-smoothing-2-notref.html
 == osx-font-smoothing-2.html osx-font-smoothing-2-ref.html
+
+pref(layout.css.text-align-true-value.enabled,true) == text-align-true.html text-align-true-ref.html
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/text-align-true-ref.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<html>
+ <head>
+  <title>text-align-true</title>
+  <meta charset="utf-8">
+  <style type="text/css">
+   p {
+     overflow:hidden;
+     width:8em;
+     white-space:nowrap;
+     margin:0;
+   }
+.test1 p {
+   text-align: end;
+}
+.test2 p {
+   text-align: right;
+}
+.test3 p {
+   text-align: end;
+}
+.test4 p {
+   text-align: left;
+}
+.test5 p {
+   text-align: right;
+}
+.r {
+   float:right;
+}
+.l {
+   float:left;
+}
+  </style>
+ </head>
+ <body>
+<div class=test1>
+ <p>Lorem ipsum dolor sit amet.</p>
+ <p dir="rtl">ישים אל לבו חובתו אשר הוא&nbsp;מתעלם&nbsp;ממנה.</p>
+</div>
+
+<div class=test2>
+ <p><span class="r">Lorem ipsum dolor sit amet.</span></p>
+ <p dir="rtl">ישים אל לבו חובתו אשר הוא&nbsp;מתעלם&nbsp;ממנה.</p>
+</div>
+
+<div class=test3>
+ <p><span class="r">Lorem ipsum dolor sit amet.</span></p>
+ <p dir="rtl"><span class="l">ישים אל לבו חובתו אשר הוא&nbsp;מתעלם&nbsp;ממנה.</span></p>
+</div>
+
+<div class=test4>
+ <p>Lorem ipsum dolor sit amet.</p>
+ <p dir="rtl"><span class="l">ישים אל לבו חובתו אשר הוא&nbsp;מתעלם&nbsp;ממנה.</span></p>
+</div>
+
+<div class=test5>
+ <p><span class="r">Lorem ipsum dolor sit amet.</span></p>
+</div>
+
+ left|true right</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/text-align-true.html
@@ -0,0 +1,68 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+ <head>
+  <title>text-align-true</title>
+  <meta charset="utf-8">
+  <style type="text/css">
+   p {
+     overflow:hidden;
+     width:8em;
+     white-space:nowrap;
+     margin:0;
+   }
+.test1 p {
+   text-align: true right;
+   -moz-text-align-last: end;
+}
+.test2 p {
+   text-align: true right;
+}
+.test3 p {
+   text-align: end true;
+}
+.test4 p {
+   text-align: true left;
+}
+.test5 p {
+   text-align: left;
+   -moz-text-align-last: true right;
+}
+  </style>
+ </head>
+ <body>
+<div class=test1>
+ <p>Lorem ipsum dolor sit amet.</p>
+ <p dir="rtl">ישים אל לבו חובתו אשר הוא&nbsp;מתעלם&nbsp;ממנה.</p>
+</div>
+
+<div class=test2>
+ <p>Lorem ipsum dolor sit amet.</p>
+ <p dir="rtl">ישים אל לבו חובתו אשר הוא&nbsp;מתעלם&nbsp;ממנה.</p>
+</div>
+
+<div class=test3>
+ <p>Lorem ipsum dolor sit amet.</p>
+ <p dir="rtl">ישים אל לבו חובתו אשר הוא&nbsp;מתעלם&nbsp;ממנה.</p>
+</div>
+
+<div class=test4>
+ <p>Lorem ipsum dolor sit amet.</p>
+ <p dir="rtl">ישים אל לבו חובתו אשר הוא&nbsp;מתעלם&nbsp;ממנה.</p>
+</div>
+
+<div class=test5>
+ <p>Lorem ipsum dolor sit amet.</p>
+</div>
+
+<script>
+  var elem = document.querySelector('.test5 p');
+  var a = window.getComputedStyle(elem,null).getPropertyValue("text-align");
+  document.body.appendChild(document.createTextNode(a +"|"));
+  a = window.getComputedStyle(elem,null).getPropertyValue("-moz-text-align-last");
+  document.body.appendChild(document.createTextNode(a));
+
+  document.documentElement.removeAttribute('class');
+</script>
+
+</body>
+</html>
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -524,16 +524,17 @@ CSS_KEY(top-outside, top_outside)
 CSS_KEY(traditional, traditional)
 CSS_KEY(translate, translate)
 CSS_KEY(translate3d, translate3d)
 CSS_KEY(translatex, translatex)
 CSS_KEY(translatey, translatey)
 CSS_KEY(translatez, translatez)
 CSS_KEY(transparent, transparent) // for nsComputedDOMStyle only
 CSS_KEY(tri-state, tri_state)
+CSS_KEY(true, true)
 CSS_KEY(ultra-condensed, ultra_condensed)
 CSS_KEY(ultra-expanded, ultra_expanded)
 CSS_KEY(underline, underline)
 CSS_KEY(unicase, unicase)
 CSS_KEY(unset, unset)
 CSS_KEY(upper-alpha, upper_alpha)
 CSS_KEY(upper-latin, upper_latin)
 CSS_KEY(upper-roman, upper_roman)
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -501,16 +501,19 @@ protected:
   bool ParseMargin();
   bool ParseMarks(nsCSSValue& aValue);
   bool ParseTransform(bool aIsPrefixed);
   bool ParseOutline();
   bool ParseOverflow();
   bool ParsePadding();
   bool ParseQuotes();
   bool ParseSize();
+  bool ParseTextAlign(nsCSSValue& aValue, const int32_t aTable[]);
+  bool ParseTextAlign(nsCSSValue& aValue);
+  bool ParseTextAlignLast(nsCSSValue& aValue);
   bool ParseTextDecoration();
   bool ParseTextDecorationLine(nsCSSValue& aValue);
   bool ParseTextCombineHorizontal(nsCSSValue& aValue);
   bool ParseTextOverflow(nsCSSValue& aValue);
 
   bool ParseShadowItem(nsCSSValue& aValue, bool aIsBoxShadow);
   bool ParseShadowList(nsCSSProperty aProperty);
   bool ParseTransitionProperty();
@@ -6664,16 +6667,20 @@ CSSParserImpl::ParseSingleValueProperty(
       case eCSSProperty_font_feature_settings:
         return ParseFontFeatureSettings(aValue);
       case eCSSProperty_font_weight:
         return ParseFontWeight(aValue);
       case eCSSProperty_image_orientation:
         return ParseImageOrientation(aValue);
       case eCSSProperty_marks:
         return ParseMarks(aValue);
+      case eCSSProperty_text_align:
+        return ParseTextAlign(aValue);
+      case eCSSProperty_text_align_last:
+        return ParseTextAlignLast(aValue);
       case eCSSProperty_text_decoration_line:
         return ParseTextDecorationLine(aValue);
       case eCSSProperty_text_combine_horizontal:
         return ParseTextCombineHorizontal(aValue);
       case eCSSProperty_text_overflow:
         return ParseTextOverflow(aValue);
       default:
         NS_ABORT_IF_FALSE(false, "should not reach here");
@@ -9710,16 +9717,64 @@ CSSParserImpl::ParseTextDecoration()
   AppendValue(eCSSProperty_text_decoration_line, line);
   AppendValue(eCSSProperty_text_decoration_color, color);
   AppendValue(eCSSProperty_text_decoration_style, style);
 
   return true;
 }
 
 bool
+CSSParserImpl::ParseTextAlign(nsCSSValue& aValue, const int32_t aTable[])
+{
+  if (ParseVariant(aValue, VARIANT_INHERIT, nullptr)) {
+    // 'inherit', 'initial' and 'unset' must be alone
+    return true;
+  }
+
+  nsCSSValue left;
+  if (!ParseVariant(left, VARIANT_KEYWORD, aTable)) {
+    return false;
+  }
+
+  if (!nsLayoutUtils::IsTextAlignTrueValueEnabled()) {
+    aValue = left;
+    return true;
+  }
+
+  nsCSSValue right;
+  if (ParseVariant(right, VARIANT_KEYWORD, aTable)) {
+    // 'true' must be combined with some other value than 'true'.
+    if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE &&
+        right.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
+      return false;
+    }
+    aValue.SetPairValue(left, right);
+  } else {
+    // Single value 'true' is not allowed.
+    if (left.GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
+      return false;
+    }
+    aValue = left;
+  }
+  return true;
+}
+
+bool
+CSSParserImpl::ParseTextAlign(nsCSSValue& aValue)
+{
+  return ParseTextAlign(aValue, nsCSSProps::kTextAlignKTable);
+}
+
+bool
+CSSParserImpl::ParseTextAlignLast(nsCSSValue& aValue)
+{
+  return ParseTextAlign(aValue, nsCSSProps::kTextAlignLastKTable);
+}
+
+bool
 CSSParserImpl::ParseTextDecorationLine(nsCSSValue& aValue)
 {
   if (ParseVariant(aValue, VARIANT_HK, nsCSSProps::kTextDecorationLineKTable)) {
     if (eCSSUnit_Enumerated == aValue.GetUnit()) {
       int32_t intValue = aValue.GetIntValue();
       if (intValue != NS_STYLE_TEXT_DECORATION_LINE_NONE) {
         // look for more keywords
         nsCSSValue  keyword;
--- a/layout/style/nsCSSPropList.h
+++ b/layout/style/nsCSSPropList.h
@@ -2763,29 +2763,30 @@ CSS_PROP_TABLE(
     VARIANT_HK,
     kTableLayoutKTable,
     CSS_PROP_NO_OFFSET,
     eStyleAnimType_None)
 CSS_PROP_TEXT(
     text-align,
     text_align,
     TextAlign,
-    CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_APPLIES_TO_PLACEHOLDER,
+    CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_VALUE_PARSER_FUNCTION |
+      CSS_PROPERTY_APPLIES_TO_PLACEHOLDER,
     "",
     // 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,
+    CSS_PROPERTY_PARSE_VALUE | CSS_PROPERTY_VALUE_PARSER_FUNCTION,
     "",
     VARIANT_HK,
     kTextAlignLastKTable,
     offsetof(nsStyleText, mTextAlignLast),
     eStyleAnimType_None)
 CSS_PROP_SHORTHAND(
     text-decoration,
     text_decoration,
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -955,19 +955,17 @@ int32_t nsCSSProps::kDisplayKTable[] = {
   eCSSKeyword__moz_grid_group,    NS_STYLE_DISPLAY_GRID_GROUP,
   eCSSKeyword__moz_grid_line,     NS_STYLE_DISPLAY_GRID_LINE,
   eCSSKeyword__moz_stack,         NS_STYLE_DISPLAY_STACK,
   eCSSKeyword__moz_inline_stack,  NS_STYLE_DISPLAY_INLINE_STACK,
   eCSSKeyword__moz_deck,          NS_STYLE_DISPLAY_DECK,
   eCSSKeyword__moz_popup,         NS_STYLE_DISPLAY_POPUP,
   eCSSKeyword__moz_groupbox,      NS_STYLE_DISPLAY_GROUPBOX,
 #endif
-  // XXXdholbert NOTE: These currently need to be the last entries in the
-  // table, because the "is flexbox enabled" pref that disables these will
-  // disable all the entries after them, too.
+  // The next two entries are controlled by the layout.css.flexbox.enabled pref.
   eCSSKeyword_flex,               NS_STYLE_DISPLAY_FLEX,
   eCSSKeyword_inline_flex,        NS_STYLE_DISPLAY_INLINE_FLEX,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const int32_t nsCSSProps::kEmptyCellsKTable[] = {
   eCSSKeyword_show,                 NS_STYLE_TABLE_EMPTY_CELLS_SHOW,
   eCSSKeyword_hide,                 NS_STYLE_TABLE_EMPTY_CELLS_HIDE,
@@ -1375,19 +1373,17 @@ const int32_t nsCSSProps::kPointerEvents
   eCSSKeyword_UNKNOWN, -1
 };
 
 int32_t nsCSSProps::kPositionKTable[] = {
   eCSSKeyword_static, NS_STYLE_POSITION_STATIC,
   eCSSKeyword_relative, NS_STYLE_POSITION_RELATIVE,
   eCSSKeyword_absolute, NS_STYLE_POSITION_ABSOLUTE,
   eCSSKeyword_fixed, NS_STYLE_POSITION_FIXED,
-  // NOTE: This currently needs to be the last entry in the table,
-  // because the "layout.css.sticky.enabled" pref that disables
-  // this will disable all the entries after it, too.
+  // The next entry is controlled by the layout.css.sticky.enabled pref.
   eCSSKeyword_sticky, NS_STYLE_POSITION_STICKY,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const int32_t nsCSSProps::kRadialGradientShapeKTable[] = {
   eCSSKeyword_circle,  NS_STYLE_GRADIENT_SHAPE_CIRCULAR,
   eCSSKeyword_ellipse, NS_STYLE_GRADIENT_SHAPE_ELLIPTICAL,
   eCSSKeyword_UNKNOWN,-1
@@ -1427,37 +1423,39 @@ const int32_t nsCSSProps::kStackSizingKT
 };
 
 const int32_t nsCSSProps::kTableLayoutKTable[] = {
   eCSSKeyword_auto, NS_STYLE_TABLE_LAYOUT_AUTO,
   eCSSKeyword_fixed, NS_STYLE_TABLE_LAYOUT_FIXED,
   eCSSKeyword_UNKNOWN,-1
 };
 
-const int32_t nsCSSProps::kTextAlignKTable[] = {
+int32_t nsCSSProps::kTextAlignKTable[] = {
   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__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_true, NS_STYLE_TEXT_ALIGN_TRUE,
   eCSSKeyword_UNKNOWN,-1
 };
 
-const int32_t nsCSSProps::kTextAlignLastKTable[] = {
+int32_t 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_true, NS_STYLE_TEXT_ALIGN_TRUE,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const int32_t nsCSSProps::kTextCombineHorizontalKTable[] = {
   eCSSKeyword_none, NS_STYLE_TEXT_COMBINE_HORIZ_NONE,
   eCSSKeyword_all, NS_STYLE_TEXT_COMBINE_HORIZ_ALL,
   eCSSKeyword_digits, NS_STYLE_TEXT_COMBINE_HORIZ_DIGITS_2,  // w/o number ==> 2
   eCSSKeyword_UNKNOWN,-1
@@ -1783,25 +1781,43 @@ const int32_t nsCSSProps::kColorInterpol
 };
 
 const int32_t nsCSSProps::kColumnFillKTable[] = {
   eCSSKeyword_auto, NS_STYLE_COLUMN_FILL_AUTO,
   eCSSKeyword_balance, NS_STYLE_COLUMN_FILL_BALANCE,
   eCSSKeyword_UNKNOWN, -1
 };
 
+static bool IsKeyValSentinel(nsCSSKeyword aKey, int32_t aValue)
+{
+  return aKey == eCSSKeyword_UNKNOWN && aValue == -1;
+}
+
 int32_t
 nsCSSProps::FindIndexOfKeyword(nsCSSKeyword aKeyword, const int32_t aTable[])
 {
-  int32_t index = 0;
-  while (eCSSKeyword_UNKNOWN != nsCSSKeyword(aTable[index])) {
-    if (aKeyword == nsCSSKeyword(aTable[index])) {
-      return index;
+  if (eCSSKeyword_UNKNOWN == aKeyword) {
+    // NOTE: we can have keyword tables where eCSSKeyword_UNKNOWN is used
+    // not only for the sentinel, but also in the middle of the table to
+    // knock out values that have been disabled by prefs, e.g. kDisplayKTable.
+    // So we deal with eCSSKeyword_UNKNOWN up front to avoid returning a valid
+    // index in the loop below.
+    return -1;
+  }
+  int32_t i = 0;
+  for (;;) {
+    nsCSSKeyword key = nsCSSKeyword(aTable[i]);
+    int32_t val = aTable[i + 1];
+    if (::IsKeyValSentinel(key, val)) {
+      break;
     }
-    index += 2;
+    if (aKeyword == key) {
+      return i;
+    }
+    i += 2;
   }
   return -1;
 }
 
 bool
 nsCSSProps::FindKeyword(nsCSSKeyword aKeyword, const int32_t aTable[],
                         int32_t& aResult)
 {
@@ -1813,21 +1829,23 @@ nsCSSProps::FindKeyword(nsCSSKeyword aKe
   return false;
 }
 
 nsCSSKeyword
 nsCSSProps::ValueToKeywordEnum(int32_t aValue, const int32_t aTable[])
 {
   int32_t i = 1;
   for (;;) {
-    if (aTable[i] == -1 && aTable[i-1] == eCSSKeyword_UNKNOWN) {
+    int32_t val = aTable[i];
+    nsCSSKeyword key = nsCSSKeyword(aTable[i - 1]);
+    if (::IsKeyValSentinel(key, val)) {
       break;
     }
-    if (aValue == aTable[i]) {
-      return nsCSSKeyword(aTable[i-1]);
+    if (aValue == val) {
+      return key;
     }
     i += 2;
   }
   return eCSSKeyword_UNKNOWN;
 }
 
 const nsAFlatCString&
 nsCSSProps::ValueToKeyword(int32_t aValue, const int32_t aTable[])
--- a/layout/style/nsCSSProps.h
+++ b/layout/style/nsCSSProps.h
@@ -532,18 +532,20 @@ public:
   static const int32_t kResizeKTable[];
   static const int32_t kSpeakKTable[];
   static const int32_t kSpeakHeaderKTable[];
   static const int32_t kSpeakNumeralKTable[];
   static const int32_t kSpeakPunctuationKTable[];
   static const int32_t kSpeechRateKTable[];
   static const int32_t kStackSizingKTable[];
   static const int32_t kTableLayoutKTable[];
-  static const int32_t kTextAlignKTable[];
-  static const int32_t kTextAlignLastKTable[];
+  // Not const because we modify its entries when the pref
+  // "layout.css.text-align-true-value.enabled" changes:
+  static int32_t kTextAlignKTable[];
+  static int32_t kTextAlignLastKTable[];
   static const int32_t kTextCombineHorizontalKTable[];
   static const int32_t kTextDecorationLineKTable[];
   static const int32_t kTextDecorationStyleKTable[];
   static const int32_t kTextOrientationKTable[];
   static const int32_t kTextOverflowKTable[];
   static const int32_t kTextTransformKTable[];
   static const int32_t kTransitionTimingFunctionKTable[];
   static const int32_t kUnicodeBidiKTable[];
--- a/layout/style/nsComputedDOMStyle.cpp
+++ b/layout/style/nsComputedDOMStyle.cpp
@@ -2755,33 +2755,48 @@ nsComputedDOMStyle::DoGetVerticalAlign()
   nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
   SetValueToCoord(val, StyleTextReset()->mVerticalAlign, false,
                   &nsComputedDOMStyle::GetLineHeightCoord,
                   nsCSSProps::kVerticalAlignKTable);
   return val;
 }
 
 CSSValue*
-nsComputedDOMStyle::DoGetTextAlign()
+nsComputedDOMStyle::CreateTextAlignValue(uint8_t aAlign, bool aAlignTrue,
+                                         const int32_t aTable[])
 {
   nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
-  val->SetIdent(
-    nsCSSProps::ValueToKeywordEnum(StyleText()->mTextAlign,
-                                   nsCSSProps::kTextAlignKTable));
-  return val;
+  val->SetIdent(nsCSSProps::ValueToKeywordEnum(aAlign, aTable));
+  if (!aAlignTrue) {
+    return val;
+  }
+
+  nsROCSSPrimitiveValue* first = new nsROCSSPrimitiveValue;
+  first->SetIdent(eCSSKeyword_true);
+
+  nsDOMCSSValueList* valueList = GetROCSSValueList(false);
+  valueList->AppendCSSValue(first);
+  valueList->AppendCSSValue(val);
+  return valueList;
+}
+
+CSSValue*
+nsComputedDOMStyle::DoGetTextAlign()
+{
+  const nsStyleText* style = StyleText();
+  return CreateTextAlignValue(style->mTextAlign, style->mTextAlignTrue,
+                              nsCSSProps::kTextAlignKTable);
 }
 
 CSSValue*
 nsComputedDOMStyle::DoGetTextAlignLast()
 {
-  nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
-  val->SetIdent(
-    nsCSSProps::ValueToKeywordEnum(StyleText()->mTextAlignLast,
-                                   nsCSSProps::kTextAlignLastKTable));
-  return val;
+  const nsStyleText* style = StyleText();
+  return CreateTextAlignValue(style->mTextAlignLast, style->mTextAlignLastTrue,
+                              nsCSSProps::kTextAlignLastKTable);
 }
 
 CSSValue*
 nsComputedDOMStyle::DoGetTextCombineHorizontal()
 {
   nsROCSSPrimitiveValue *val = new nsROCSSPrimitiveValue;
   uint8_t tch = StyleText()->mTextCombineHorizontal;
 
--- a/layout/style/nsComputedDOMStyle.h
+++ b/layout/style/nsComputedDOMStyle.h
@@ -135,16 +135,21 @@ public:
 private:
   void AssertFlushedPendingReflows() {
     NS_ASSERTION(mFlushedPendingReflows,
                  "property getter should have been marked layout-dependent");
   }
 
   nsMargin GetAdjustedValuesForBoxSizing();
 
+  // Helper method for DoGetTextAlign[Last].
+  mozilla::dom::CSSValue* CreateTextAlignValue(uint8_t aAlign,
+                                               bool aAlignTrue,
+                                               const int32_t aTable[]);
+
 #define STYLE_STRUCT(name_, checkdata_cb_)                              \
   const nsStyle##name_ * Style##name_() {                               \
     return mStyleContextHolder->Style##name_();                         \
   }
 #include "nsStyleStructList.h"
 #undef STYLE_STRUCT
 
   // All of the property getters below return a pointer to a refcounted object
--- a/layout/style/nsRuleNode.cpp
+++ b/layout/style/nsRuleNode.cpp
@@ -3963,35 +3963,70 @@ nsRuleNode::ComputeTextData(void* aStart
           lh = minimumFontSize;
         }
       }
       text->mLineHeight.SetCoordValue(lh);
     }
   }
 
 
-  // text-align: enum, string, inherit, initial
+  // text-align: enum, string, pair(enum|string), inherit, initial
+  // NOTE: string is not implemented yet.
   const nsCSSValue* textAlignValue = aRuleData->ValueForTextAlign();
+  text->mTextAlignTrue = false;
   if (eCSSUnit_String == textAlignValue->GetUnit()) {
     NS_NOTYETIMPLEMENTED("align string");
   } else if (eCSSUnit_Enumerated == textAlignValue->GetUnit() &&
              NS_STYLE_TEXT_ALIGN_MOZ_CENTER_OR_INHERIT ==
                textAlignValue->GetIntValue()) {
     canStoreInRuleTree = false;
     uint8_t parentAlign = parentText->mTextAlign;
     text->mTextAlign = (NS_STYLE_TEXT_ALIGN_DEFAULT == parentAlign) ?
       NS_STYLE_TEXT_ALIGN_CENTER : parentAlign;
-  } else
+  } else {
+    if (eCSSUnit_Pair == textAlignValue->GetUnit()) {
+      // Two values were specified, one must be 'true'.
+      text->mTextAlignTrue = true;
+      const nsCSSValuePair& textAlignValuePair = textAlignValue->GetPairValue();
+      textAlignValue = &textAlignValuePair.mXValue;
+      if (eCSSUnit_Enumerated == textAlignValue->GetUnit()) {
+        if (textAlignValue->GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
+          textAlignValue = &textAlignValuePair.mYValue;
+        }
+      } else if (eCSSUnit_String == textAlignValue->GetUnit()) {
+        NS_NOTYETIMPLEMENTED("align string");
+      }
+    } else if (eCSSUnit_Inherit == textAlignValue->GetUnit() ||
+               eCSSUnit_Unset == textAlignValue->GetUnit()) {
+      text->mTextAlignTrue = parentText->mTextAlignTrue;
+    }
     SetDiscrete(*textAlignValue, text->mTextAlign, canStoreInRuleTree,
                 SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
                 parentText->mTextAlign,
                 NS_STYLE_TEXT_ALIGN_DEFAULT, 0, 0, 0, 0);
-
-  // text-align-last: enum, inherit, initial
-  SetDiscrete(*aRuleData->ValueForTextAlignLast(), text->mTextAlignLast,
+  }
+
+  // text-align-last: enum, pair(enum), inherit, initial
+  const nsCSSValue* textAlignLastValue = aRuleData->ValueForTextAlignLast();
+  text->mTextAlignLastTrue = false;
+  if (eCSSUnit_Pair == textAlignLastValue->GetUnit()) {
+    // Two values were specified, one must be 'true'.
+    text->mTextAlignLastTrue = true;
+    const nsCSSValuePair& textAlignLastValuePair = textAlignLastValue->GetPairValue();
+    textAlignLastValue = &textAlignLastValuePair.mXValue;
+    if (eCSSUnit_Enumerated == textAlignLastValue->GetUnit()) {
+      if (textAlignLastValue->GetIntValue() == NS_STYLE_TEXT_ALIGN_TRUE) {
+        textAlignLastValue = &textAlignLastValuePair.mYValue;
+      }
+    }
+  } else if (eCSSUnit_Inherit == textAlignLastValue->GetUnit() ||
+             eCSSUnit_Unset == textAlignLastValue->GetUnit()) {
+    text->mTextAlignLastTrue = parentText->mTextAlignLastTrue;
+  }
+  SetDiscrete(*textAlignLastValue, text->mTextAlignLast,
               canStoreInRuleTree,
               SETDSC_ENUMERATED | SETDSC_UNSET_INHERIT,
               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 |
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -656,16 +656,17 @@ static inline mozilla::css::Side operato
 #define NS_STYLE_TEXT_ALIGN_END                   6
 #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 11
+#define NS_STYLE_TEXT_ALIGN_TRUE                  12
 // Note: make sure that the largest NS_STYLE_TEXT_ALIGN_* value is smaller than
 // the smallest NS_STYLE_VERTICAL_ALIGN_* value below!
 
 // See nsStyleText, nsStyleFont
 #define NS_STYLE_TEXT_DECORATION_LINE_NONE         0
 #define NS_STYLE_TEXT_DECORATION_LINE_UNDERLINE    NS_FONT_DECORATION_UNDERLINE
 #define NS_STYLE_TEXT_DECORATION_LINE_OVERLINE     NS_FONT_DECORATION_OVERLINE
 #define NS_STYLE_TEXT_DECORATION_LINE_LINE_THROUGH NS_FONT_DECORATION_LINE_THROUGH
@@ -705,25 +706,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             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
+#define NS_STYLE_VERTICAL_ALIGN_BASELINE             13
+#define NS_STYLE_VERTICAL_ALIGN_SUB                  14
+#define NS_STYLE_VERTICAL_ALIGN_SUPER                15
+#define NS_STYLE_VERTICAL_ALIGN_TOP                  16
+#define NS_STYLE_VERTICAL_ALIGN_TEXT_TOP             17
+#define NS_STYLE_VERTICAL_ALIGN_MIDDLE               18
+#define NS_STYLE_VERTICAL_ALIGN_TEXT_BOTTOM          19
+#define NS_STYLE_VERTICAL_ALIGN_BOTTOM               20
+#define NS_STYLE_VERTICAL_ALIGN_MIDDLE_WITH_BASELINE 21
 
 // 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/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -2919,16 +2919,18 @@ CalcShadowDifference(nsCSSShadowArray* l
 // nsStyleText
 //
 
 nsStyleText::nsStyleText(void)
 { 
   MOZ_COUNT_CTOR(nsStyleText);
   mTextAlign = NS_STYLE_TEXT_ALIGN_DEFAULT;
   mTextAlignLast = NS_STYLE_TEXT_ALIGN_AUTO;
+  mTextAlignTrue = false;
+  mTextAlignLastTrue = false;
   mTextTransform = NS_STYLE_TEXT_TRANSFORM_NONE;
   mWhiteSpace = NS_STYLE_WHITESPACE_NORMAL;
   mWordBreak = NS_STYLE_WORDBREAK_NORMAL;
   mWordWrap = NS_STYLE_WORDWRAP_NORMAL;
   mHyphens = NS_STYLE_HYPHENS_MANUAL;
   mTextSizeAdjust = NS_STYLE_TEXT_SIZE_ADJUST_AUTO;
   mTextOrientation = NS_STYLE_TEXT_ORIENTATION_AUTO;
   mTextCombineHorizontal = NS_STYLE_TEXT_COMBINE_HORIZ_NONE;
@@ -2940,16 +2942,18 @@ nsStyleText::nsStyleText(void)
 
   mTextShadow = nullptr;
   mTabSize = NS_STYLE_TABSIZE_INITIAL;
 }
 
 nsStyleText::nsStyleText(const nsStyleText& aSource)
   : mTextAlign(aSource.mTextAlign),
     mTextAlignLast(aSource.mTextAlignLast),
+    mTextAlignTrue(false),
+    mTextAlignLastTrue(false),
     mTextTransform(aSource.mTextTransform),
     mWhiteSpace(aSource.mWhiteSpace),
     mWordBreak(aSource.mWordBreak),
     mWordWrap(aSource.mWordWrap),
     mHyphens(aSource.mHyphens),
     mTextSizeAdjust(aSource.mTextSizeAdjust),
     mTextOrientation(aSource.mTextOrientation),
     mTextCombineHorizontal(aSource.mTextCombineHorizontal),
@@ -2977,16 +2981,18 @@ nsChangeHint nsStyleText::CalcDifference
   }
 
   if (mTextCombineHorizontal != aOther.mTextCombineHorizontal) {
     return nsChangeHint_ReconstructFrame;
   }
 
   if ((mTextAlign != aOther.mTextAlign) ||
       (mTextAlignLast != aOther.mTextAlignLast) ||
+      (mTextAlignTrue != aOther.mTextAlignTrue) ||
+      (mTextAlignLastTrue != aOther.mTextAlignLastTrue) ||
       (mTextTransform != aOther.mTextTransform) ||
       (mWhiteSpace != aOther.mWhiteSpace) ||
       (mWordBreak != aOther.mWordBreak) ||
       (mWordWrap != aOther.mWordWrap) ||
       (mHyphens != aOther.mHyphens) ||
       (mTextSizeAdjust != aOther.mTextSizeAdjust) ||
       (mTextOrientation != aOther.mTextOrientation) ||
       (mLetterSpacing != aOther.mLetterSpacing) ||
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1305,16 +1305,18 @@ struct nsStyleText {
 
   nsChangeHint CalcDifference(const nsStyleText& aOther) const;
   static nsChangeHint MaxDifference() {
     return NS_STYLE_HINT_FRAMECHANGE;
   }
 
   uint8_t mTextAlign;                   // [inherited] see nsStyleConsts.h
   uint8_t mTextAlignLast;               // [inherited] see nsStyleConsts.h
+  bool mTextAlignTrue : 1;              // [inherited] see nsStyleConsts.h
+  bool mTextAlignLastTrue : 1;          // [inherited] see nsStyleConsts.h
   uint8_t mTextTransform;               // [inherited] see nsStyleConsts.h
   uint8_t mWhiteSpace;                  // [inherited] see nsStyleConsts.h
   uint8_t mWordBreak;                   // [inherited] see nsStyleConsts.h
   uint8_t mWordWrap;                    // [inherited] see nsStyleConsts.h
   uint8_t mHyphens;                     // [inherited] see nsStyleConsts.h
   uint8_t mTextSizeAdjust;              // [inherited] see nsStyleConsts.h
   uint8_t mTextOrientation;             // [inherited] see nsStyleConsts.h
   uint8_t mTextCombineHorizontal;       // [inherited] see nsStyleConsts.h
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -3074,17 +3074,17 @@ var gCSSProperties = {
 	},
 	"text-align": {
 		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: []
+		invalid_values: [ "true", "true true" ]
 	},
 	"-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: []
@@ -4788,9 +4788,14 @@ if (SpecialPowers.getBoolPref("layout.cs
   gCSSProperties["-moz-transition"].invalid_values.push("2s unset");
   gCSSProperties["-moz-transition-property"].invalid_values.push("unset, color", "color, unset");
   gCSSProperties["-moz-animation"].invalid_values.push("2s unset");
   gCSSProperties["-moz-animation-direction"].invalid_values.push("unset, normal");
   gCSSProperties["-moz-animation-name"].invalid_values.push("bounce, unset", "unset, bounce");
   if (SpecialPowers.getBoolPref("layout.css.filters.enabled")) {
     gCSSProperties["filter"].invalid_values.push("drop-shadow(unset, 2px 2px)", "drop-shadow(2px 2px, unset)");
   }
+  if (SpecialPowers.getBoolPref("layout.css.text-align-true-value.enabled")) {
+    gCSSProperties["text-align"].other_values.push("true left");
+  } else {
+    gCSSProperties["text-align"].invalid_values.push("true left");
+  }
 }
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -1881,16 +1881,19 @@ pref("layout.css.flexbox.enabled", true)
 
 // Is support for CSS sticky positioning enabled?
 #ifdef RELEASE_BUILD
 pref("layout.css.sticky.enabled", false);
 #else
 pref("layout.css.sticky.enabled", true);
 #endif
 
+// Is support for CSS "text-align: true X" enabled?
+pref("layout.css.text-align-true-value.enabled", false);
+
 // Is support for the CSS4 image-orientation property enabled?
 pref("layout.css.image-orientation.enabled", true);
 
 // Is support for CSS3 Fonts features enabled?
 // (includes font-variant-*, font-kerning, font-synthesis
 // and the @font-feature-values rule)
 // Note: with this enabled, font-feature-settings is aliased
 // to -moz-font-feature-settings.  When unprefixing, this should