Bug 1019555 - Treat newlines and tabs as space characters in SVG xml:space="preserve" text. r=roc
authorCameron McCormack <cam@mcc.id.au>
Thu, 05 Jun 2014 13:25:15 +1000
changeset 186778 25abaa7e7bb5
parent 186777 ec751fbe4ebd
child 186779 c69a670294fa
push id44430
push usercmccormack@mozilla.com
push date2014-06-05 12:11 +0000
treeherdermozilla-inbound@25abaa7e7bb5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs1019555
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1019555 - Treat newlines and tabs as space characters in SVG xml:space="preserve" text. r=roc
layout/generic/nsTextFrame.cpp
layout/generic/nsTextFrameUtils.cpp
layout/generic/nsTextFrameUtils.h
layout/reftests/text/pre-discard-newlines-1-ref.html
layout/reftests/text/pre-discard-newlines-1.html
layout/reftests/text/pre-space-1-ref.html
layout/reftests/text/pre-space-1.html
layout/reftests/text/reftest.list
layout/style/nsCSSKeywordList.h
layout/style/nsCSSProps.cpp
layout/style/nsStyleConsts.h
layout/style/nsStyleStruct.h
layout/style/test/property_database.js
layout/svg/svg.css
--- a/layout/generic/nsTextFrame.cpp
+++ b/layout/generic/nsTextFrame.cpp
@@ -670,18 +670,17 @@ static bool IsCSSWordSpacingSpace(const 
 
   char16_t ch = aFrag->CharAt(aPos);
   switch (ch) {
   case ' ':
   case CH_NBSP:
     return !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
   case '\r':
   case '\t': return !aStyleText->WhiteSpaceIsSignificant();
-  case '\n': return !aStyleText->NewlineIsSignificant() &&
-                    !aStyleText->NewlineIsDiscarded();
+  case '\n': return !aStyleText->NewlineIsSignificant();
   default: return false;
   }
 }
 
 // Check whether the string aChars/aLength starts with space that's
 // trimmable according to CSS 'white-space:normal/nowrap'. 
 static bool IsTrimmableSpace(const char16_t* aChars, uint32_t aLength)
 {
@@ -704,17 +703,17 @@ static bool IsTrimmableSpace(const nsTex
                                const nsStyleText* aStyleText)
 {
   NS_ASSERTION(aPos < aFrag->GetLength(), "No text for IsSpace!");
 
   switch (aFrag->CharAt(aPos)) {
   case ' ': return !aStyleText->WhiteSpaceIsSignificant() &&
                    !IsSpaceCombiningSequenceTail(aFrag, aPos + 1);
   case '\n': return !aStyleText->NewlineIsSignificant() &&
-                    !aStyleText->NewlineIsDiscarded();
+                    aStyleText->mWhiteSpace != NS_STYLE_WHITESPACE_PRE_SPACE;
   case '\t':
   case '\r':
   case '\f': return !aStyleText->WhiteSpaceIsSignificant();
   default: return false;
   }
 }
 
 static bool IsSelectionSpace(const nsTextFragment* aFrag, uint32_t aPos)
@@ -768,31 +767,16 @@ IsAllWhitespace(const nsTextFragment* aF
     char ch = str[i];
     if (ch == ' ' || ch == '\t' || ch == '\r' || (ch == '\n' && aAllowNewline))
       continue;
     return false;
   }
   return true;
 }
 
-static bool
-IsAllNewlines(const nsTextFragment* aFrag)
-{
-  if (aFrag->Is2b())
-    return false;
-  int32_t len = aFrag->GetLength();
-  const char* str = aFrag->Get1b();
-  for (int32_t i = 0; i < len; ++i) {
-    char ch = str[i];
-    if (ch != '\n')
-      return false;
-  }
-  return true;
-}
-
 static void
 CreateObserverForAnimatedGlyphs(nsTextFrame* aFrame, const nsTArray<gfxFont*>& aFonts)
 {
   if (!(aFrame->GetStateBits() & TEXT_IN_TEXTRUN_USER_DATA)) {
     // Maybe the textrun was created for uninflated text.
     return;
   }
 
@@ -1811,26 +1795,26 @@ GetFirstFontMetrics(gfxFontGroup* aFontG
   return font->GetMetrics();
 }
 
 PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_NORMAL == 0);
 PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE == 1);
 PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_NOWRAP == 2);
 PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE_WRAP == 3);
 PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE_LINE == 4);
-PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE_DISCARD_NEWLINES == 5);
+PR_STATIC_ASSERT(NS_STYLE_WHITESPACE_PRE_SPACE == 5);
 
 static const nsTextFrameUtils::CompressionMode CSSWhitespaceToCompressionMode[] =
 {
-  nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE, // normal
-  nsTextFrameUtils::COMPRESS_NONE,               // pre
-  nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE, // nowrap
-  nsTextFrameUtils::COMPRESS_NONE,               // pre-wrap
-  nsTextFrameUtils::COMPRESS_WHITESPACE,         // pre-line
-  nsTextFrameUtils::DISCARD_NEWLINE              // -moz-pre-discard-newlines
+  nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE,     // normal
+  nsTextFrameUtils::COMPRESS_NONE,                   // pre
+  nsTextFrameUtils::COMPRESS_WHITESPACE_NEWLINE,     // nowrap
+  nsTextFrameUtils::COMPRESS_NONE,                   // pre-wrap
+  nsTextFrameUtils::COMPRESS_WHITESPACE,             // pre-line
+  nsTextFrameUtils::COMPRESS_NONE_TRANSFORM_TO_SPACE // -moz-pre-space
 };
 
 gfxTextRun*
 BuildTextRunsScanner::BuildTextRunForFrames(void* aTextBuffer)
 {
   gfxSkipChars skipChars;
 
   const void* textPtr = aTextBuffer;
@@ -7154,34 +7138,34 @@ nsTextFrame::AddInlinePrefWidthForFlow(n
   
   const nsStyleText* textStyle = StyleText();
   const nsTextFragment* frag = mContent->GetText();
   PropertyProvider provider(textRun, textStyle, frag, this,
                             iter, INT32_MAX, nullptr, 0, aTextRunType);
 
   bool collapseWhitespace = !textStyle->WhiteSpaceIsSignificant();
   bool preformatNewlines = textStyle->NewlineIsSignificant();
-  bool preformatTabs = textStyle->WhiteSpaceIsSignificant();
+  bool preformatTabs = textStyle->TabIsSignificant();
   gfxFloat tabWidth = -1;
   uint32_t start =
     FindStartAfterSkippingWhitespace(&provider, aData, textStyle, &iter, flowEndInTextRun);
 
   // XXX Should we consider hyphenation here?
   // If newlines and tabs aren't preformatted, nothing to do inside
   // the loop so make i skip to the end
   uint32_t loopStart = (preformatNewlines || preformatTabs) ? start : flowEndInTextRun;
   for (uint32_t i = loopStart, lineStart = start; i <= flowEndInTextRun; ++i) {
     bool preformattedNewline = false;
     bool preformattedTab = false;
     if (i < flowEndInTextRun) {
       // XXXldb Shouldn't we be including the newline as part of the
       // segment that it ends rather than part of the segment that it
       // starts?
-      NS_ASSERTION(preformatNewlines || textStyle->NewlineIsDiscarded(),
-                   "We can't be here unless newlines are hard breaks or are discarded");
+      NS_ASSERTION(preformatNewlines,
+                   "We can't be here unless newlines are hard breaks");
       preformattedNewline = preformatNewlines && textRun->CharIsNewline(i);
       preformattedTab = preformatTabs && textRun->CharIsTab(i);
       if (!preformattedNewline && !preformattedTab) {
         // we needn't break here (and it's not the end of the flow)
         continue;
       }
     }
 
@@ -8265,18 +8249,20 @@ nsTextFrame::RecomputeOverflow(const nsH
   UnionAdditionalOverflow(PresContext(), aBlockReflowState, provider,
                           &vis, true);
   return result;
 }
 static char16_t TransformChar(const nsStyleText* aStyle, gfxTextRun* aTextRun,
                                uint32_t aSkippedOffset, char16_t aChar)
 {
   if (aChar == '\n') {
-    return aStyle->NewlineIsSignificant() || aStyle->NewlineIsDiscarded() ?
-             aChar : ' ';
+    return aStyle->NewlineIsSignificant() ? aChar : ' ';
+  }
+  if (aChar == '\t') {
+    return aStyle->TabIsSignificant() ? aChar : ' ';
   }
   switch (aStyle->mTextTransform) {
   case NS_STYLE_TEXT_TRANSFORM_LOWERCASE:
     aChar = ToLowerCase(aChar);
     break;
   case NS_STYLE_TEXT_TRANSFORM_UPPERCASE:
     aChar = ToUpperCase(aChar);
     break;
@@ -8375,35 +8361,32 @@ nsTextFrame::GetType() const
 nsTextFrame::IsEmpty()
 {
   NS_ASSERTION(!(mState & TEXT_IS_ONLY_WHITESPACE) ||
                !(mState & TEXT_ISNOT_ONLY_WHITESPACE),
                "Invalid state");
   
   // XXXldb Should this check compatibility mode as well???
   const nsStyleText* textStyle = StyleText();
-  if (textStyle->WhiteSpaceIsSignificant() &&
-      textStyle->mWhiteSpace != NS_STYLE_WHITESPACE_PRE_DISCARD_NEWLINES) {
+  if (textStyle->WhiteSpaceIsSignificant()) {
     // XXX shouldn't we return true if the length is zero?
     return false;
   }
 
   if (mState & TEXT_ISNOT_ONLY_WHITESPACE) {
     return false;
   }
 
   if (mState & TEXT_IS_ONLY_WHITESPACE) {
     return true;
   }
 
   bool isEmpty =
-    textStyle->mWhiteSpace == NS_STYLE_WHITESPACE_PRE_DISCARD_NEWLINES ?
-      IsAllNewlines(mContent->GetText()) :
-      IsAllWhitespace(mContent->GetText(),
-                      textStyle->mWhiteSpace != NS_STYLE_WHITESPACE_PRE_LINE);
+    IsAllWhitespace(mContent->GetText(),
+                    textStyle->mWhiteSpace != NS_STYLE_WHITESPACE_PRE_LINE);
   mState |= (isEmpty ? TEXT_IS_ONLY_WHITESPACE : TEXT_ISNOT_ONLY_WHITESPACE);
   return isEmpty;
 }
 
 #ifdef DEBUG_FRAME_DUMP
 // Translate the mapped content into a string that's printable
 void
 nsTextFrame::ToCString(nsCString& aBuf, int32_t* aTotalContentLength) const
--- a/layout/generic/nsTextFrameUtils.cpp
+++ b/layout/generic/nsTextFrameUtils.cpp
@@ -42,30 +42,37 @@ nsTextFrameUtils::TransformText(const ch
                                 uint32_t* aAnalysisFlags)
 {
   uint32_t flags = 0;
   char16_t* outputStart = aOutput;
 
   bool lastCharArabic = false;
 
   if (aCompression == COMPRESS_NONE ||
-      aCompression == DISCARD_NEWLINE) {
+      aCompression == COMPRESS_NONE_TRANSFORM_TO_SPACE) {
     // Skip discardables.
     uint32_t i;
     for (i = 0; i < aLength; ++i) {
       char16_t ch = *aText++;
-      if (IsDiscardable(ch, &flags) ||
-          (ch == '\n' && aCompression == DISCARD_NEWLINE)) {
+      if (IsDiscardable(ch, &flags)) {
         aSkipChars->SkipChar();
       } else {
         aSkipChars->KeepChar();
         if (ch > ' ') {
           lastCharArabic = IS_ARABIC_CHAR(ch);
-        } else if (ch == '\t') {
-          flags |= TEXT_HAS_TAB;
+        } else if (aCompression == COMPRESS_NONE_TRANSFORM_TO_SPACE) {
+          if (ch == '\t' || ch == '\n') {
+            ch = ' ';
+            flags |= TEXT_WAS_TRANSFORMED;
+          }
+        } else {
+          // aCompression == COMPRESS_NONE
+          if (ch == '\t') {
+            flags |= TEXT_HAS_TAB;
+          }
         }
         *aOutput++ = ch;
       }
     }
     if (lastCharArabic) {
       *aIncomingFlags |= INCOMING_ARABICCHAR;
     } else {
       *aIncomingFlags &= ~INCOMING_ARABICCHAR;
@@ -142,28 +149,35 @@ nsTextFrameUtils::TransformText(const ui
                                 uint8_t* aIncomingFlags,
                                 gfxSkipChars* aSkipChars,
                                 uint32_t* aAnalysisFlags)
 {
   uint32_t flags = 0;
   uint8_t* outputStart = aOutput;
 
   if (aCompression == COMPRESS_NONE ||
-      aCompression == DISCARD_NEWLINE) {
+      aCompression == COMPRESS_NONE_TRANSFORM_TO_SPACE) {
     // Skip discardables.
     uint32_t i;
     for (i = 0; i < aLength; ++i) {
       uint8_t ch = *aText++;
-      if (IsDiscardable(ch, &flags) ||
-          (ch == '\n' && aCompression == DISCARD_NEWLINE)) {
+      if (IsDiscardable(ch, &flags)) {
         aSkipChars->SkipChar();
       } else {
         aSkipChars->KeepChar();
-        if (ch == '\t') {
-          flags |= TEXT_HAS_TAB;
+        if (aCompression == COMPRESS_NONE_TRANSFORM_TO_SPACE) {
+          if (ch == '\t' || ch == '\n') {
+            ch = ' ';
+            flags |= TEXT_WAS_TRANSFORMED;
+          }
+        } else {
+          // aCompression == COMPRESS_NONE
+          if (ch == '\t') {
+            flags |= TEXT_HAS_TAB;
+          }
         }
         *aOutput++ = ch;
       }
     }
     *aIncomingFlags &= ~(INCOMING_ARABICCHAR | INCOMING_WHITESPACE);
   } else {
     bool inWhitespace = (*aIncomingFlags & INCOMING_WHITESPACE) != 0;
     uint32_t i;
--- a/layout/generic/nsTextFrameUtils.h
+++ b/layout/generic/nsTextFrameUtils.h
@@ -77,17 +77,17 @@ public:
   IsSpaceCombiningSequenceTail(const char16_t* aChars, int32_t aLength) {
     return aLength > 0 && aChars[0] == 0x200D; // ZWJ
   }
 
   enum CompressionMode {
     COMPRESS_NONE,
     COMPRESS_WHITESPACE,
     COMPRESS_WHITESPACE_NEWLINE,
-    DISCARD_NEWLINE
+    COMPRESS_NONE_TRANSFORM_TO_SPACE
   };
 
   /**
    * Create a text run from a run of Unicode text. The text may have whitespace
    * compressed. A preformatted tab is sent to the text run as a single space.
    * (Tab spacing must be performed by textframe later.) Certain other
    * characters are discarded.
    * 
deleted file mode 100644
--- a/layout/reftests/text/pre-discard-newlines-1-ref.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<style>
-p { font: 16px monospace; white-space: pre; }
-span { background-color: lime }
-</style>
-<p><span>a  b</span></p>
-<p><span>ab</span></p>
-<p><span>a  b</span></p>
-<p><span>  a  b</span></p>
-<p><span>a  </span></p>
-<p><span></span></p>
-<p><span>    </span></p>
-<p><span>. </span></p>
deleted file mode 100644
--- a/layout/reftests/text/pre-discard-newlines-1.html
+++ /dev/null
@@ -1,13 +0,0 @@
-<!DOCTYPE html>
-<style>
-p { font: 16px monospace; white-space: -moz-pre-discard-newlines; }
-span { background-color: lime }
-</style>
-<p><span>a  b</span></p>
-<p><span>a&#xA;b</span></p>
-<p><span>a &#xA; b</span></p>
-<p><span>&#xA;  a&#xA;  b&#xA;</span></p>
-<p><span>a  </span></p>
-<p><span>&#xA;&#xA;&#xA;</span></p>
-<p><span>  &#xA;&#xA;&#xA;  </span></p>
-<p><span>. &#xA;</span></p>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/pre-space-1-ref.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<style>
+p { font: 16px monospace; white-space: pre; }
+span { background-color: lime }
+</style>
+<p><span>a  b</span></p>
+<p><span>a b</span></p>
+<p><span>a   b</span></p>
+<p><span>   a   b </span></p>
+<p><span>a  </span></p>
+<p><span>   </span></p>
+<p><span>       </span></p>
+<p><span>.  </span></p>
+<p><span>a b</span></p>
+<p><span>a   b</span></p>
+<p><span>   a   b </span></p>
+<p><span>a  </span></p>
+<p><span>   </span></p>
+<p><span>       </span></p>
+<p><span>.  </span></p>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/text/pre-space-1.html
@@ -0,0 +1,20 @@
+<!DOCTYPE html>
+<style>
+p { font: 16px monospace; white-space: -moz-pre-space; }
+span { background-color: lime }
+</style>
+<p><span>a  b</span></p>
+<p><span>a&#xA;b</span></p>
+<p><span>a &#xA; b</span></p>
+<p><span>&#xA;  a&#xA;  b&#xA;</span></p>
+<p><span>a  </span></p>
+<p><span>&#xA;&#xA;&#xA;</span></p>
+<p><span>  &#xA;&#xA;&#xA;  </span></p>
+<p><span>. &#xA;</span></p>
+<p><span>a&#x9;b</span></p>
+<p><span>a &#x9; b</span></p>
+<p><span>&#x9;  a&#x9;  b&#x9;</span></p>
+<p><span>a  </span></p>
+<p><span>&#x9;&#x9;&#x9;</span></p>
+<p><span>  &#x9;&#x9;&#x9;  </span></p>
+<p><span>. &#x9;</span></p>
--- a/layout/reftests/text/reftest.list
+++ b/layout/reftests/text/reftest.list
@@ -24,17 +24,17 @@ random-if(B2G&&browserIsRemote) == line-
 random-if(B2G&&browserIsRemote) == line-editing-1e.html line-editing-1-ref.html
 fails-if(cocoaWidget||winWidget) HTTP(..) == lineheight-metrics-1.html lineheight-metrics-1-ref.html # bug 657864
 == lineheight-percentage-1.html lineheight-percentage-1-ref.html
 skip-if(B2G) == long-1.html long-ref.html
 fuzzy-if(Android,255,42) == pre-line-1.html pre-line-1-ref.html
 == pre-line-2.html pre-line-2-ref.html
 == pre-line-3.html pre-line-3-ref.html
 == pre-line-4.html pre-line-4-ref.html
-== pre-discard-newlines-1.html pre-discard-newlines-1-ref.html
+== pre-space-1.html pre-space-1-ref.html
 == soft-hyphens-1a.html soft-hyphens-1-ref.html
 == soft-hyphens-1b.html soft-hyphens-1-ref.html
 == soft-hyphens-1c.html soft-hyphens-1-ref.html
 == soft-hyphens-break-word-1a.html soft-hyphens-break-word-1-ref.html
 == soft-hyphens-break-word-1b.html soft-hyphens-break-word-1-ref.html
 == soft-hyphens-break-word-1c.html soft-hyphens-break-word-1-ref.html
 # Tests for soft hyphens in table cells, bug 418975
 != soft-hyphen-in-table-1.html soft-hyphen-in-table-1-notref.html
--- a/layout/style/nsCSSKeywordList.h
+++ b/layout/style/nsCSSKeywordList.h
@@ -123,17 +123,17 @@ CSS_KEY(-moz-min-content, _moz_min_conte
 CSS_KEY(-moz-myanmar, _moz_myanmar)
 CSS_KEY(-moz-nativehyperlinktext, _moz_nativehyperlinktext)
 CSS_KEY(-moz-none, _moz_none)
 CSS_KEY(-moz-oddtreerow, _moz_oddtreerow)
 CSS_KEY(-moz-oriya, _moz_oriya)
 CSS_KEY(-moz-persian, _moz_persian)
 CSS_KEY(-moz-plaintext, _moz_plaintext)
 CSS_KEY(-moz-popup, _moz_popup)
-CSS_KEY(-moz-pre-discard-newlines, _moz_pre_discard_newlines)
+CSS_KEY(-moz-pre-space, _moz_pre_space)
 CSS_KEY(-moz-pull-down-menu, _moz_pull_down_menu)
 CSS_KEY(-moz-right, _moz_right)
 CSS_KEY(-moz-scrollbars-horizontal, _moz_scrollbars_horizontal)
 CSS_KEY(-moz-scrollbars-none, _moz_scrollbars_none)
 CSS_KEY(-moz-scrollbars-vertical, _moz_scrollbars_vertical)
 CSS_KEY(-moz-show-background, _moz_show_background)
 CSS_KEY(-moz-simp-chinese-formal, _moz_simp_chinese_formal)
 CSS_KEY(-moz-simp-chinese-informal, _moz_simp_chinese_informal)
--- a/layout/style/nsCSSProps.cpp
+++ b/layout/style/nsCSSProps.cpp
@@ -1710,17 +1710,17 @@ const KTableValue nsCSSProps::kVisibilit
 };
 
 const KTableValue nsCSSProps::kWhitespaceKTable[] = {
   eCSSKeyword_normal, NS_STYLE_WHITESPACE_NORMAL,
   eCSSKeyword_pre, NS_STYLE_WHITESPACE_PRE,
   eCSSKeyword_nowrap, NS_STYLE_WHITESPACE_NOWRAP,
   eCSSKeyword_pre_wrap, NS_STYLE_WHITESPACE_PRE_WRAP,
   eCSSKeyword_pre_line, NS_STYLE_WHITESPACE_PRE_LINE,
-  eCSSKeyword__moz_pre_discard_newlines, NS_STYLE_WHITESPACE_PRE_DISCARD_NEWLINES,
+  eCSSKeyword__moz_pre_space, NS_STYLE_WHITESPACE_PRE_SPACE,
   eCSSKeyword_UNKNOWN,-1
 };
 
 const KTableValue nsCSSProps::kWidthKTable[] = {
   eCSSKeyword__moz_max_content, NS_STYLE_WIDTH_MAX_CONTENT,
   eCSSKeyword__moz_min_content, NS_STYLE_WIDTH_MIN_CONTENT,
   eCSSKeyword__moz_fit_content, NS_STYLE_WIDTH_FIT_CONTENT,
   eCSSKeyword__moz_available, NS_STYLE_WIDTH_AVAILABLE,
--- a/layout/style/nsStyleConsts.h
+++ b/layout/style/nsStyleConsts.h
@@ -817,17 +817,17 @@ static inline mozilla::css::Side operato
 #define NS_STYLE_TABSIZE_INITIAL                8
 
 // See nsStyleText
 #define NS_STYLE_WHITESPACE_NORMAL               0
 #define NS_STYLE_WHITESPACE_PRE                  1
 #define NS_STYLE_WHITESPACE_NOWRAP               2
 #define NS_STYLE_WHITESPACE_PRE_WRAP             3
 #define NS_STYLE_WHITESPACE_PRE_LINE             4
-#define NS_STYLE_WHITESPACE_PRE_DISCARD_NEWLINES 5
+#define NS_STYLE_WHITESPACE_PRE_SPACE            5
 
 // See nsStyleText
 #define NS_STYLE_WORDBREAK_NORMAL               0
 #define NS_STYLE_WORDBREAK_BREAK_ALL            1
 #define NS_STYLE_WORDBREAK_KEEP_ALL             2
 
 // See nsStyleText
 #define NS_STYLE_WORDWRAP_NORMAL                0
--- a/layout/style/nsStyleStruct.h
+++ b/layout/style/nsStyleStruct.h
@@ -1524,34 +1524,35 @@ struct nsStyleText {
   nsStyleCoord  mLineHeight;            // [inherited] coord, factor, normal
   nsStyleCoord  mTextIndent;            // [inherited] coord, percent, calc
 
   nsRefPtr<nsCSSShadowArray> mTextShadow; // [inherited] nullptr in case of a zero-length
 
   bool WhiteSpaceIsSignificant() const {
     return mWhiteSpace == NS_STYLE_WHITESPACE_PRE ||
            mWhiteSpace == NS_STYLE_WHITESPACE_PRE_WRAP ||
-           mWhiteSpace == NS_STYLE_WHITESPACE_PRE_DISCARD_NEWLINES;
+           mWhiteSpace == NS_STYLE_WHITESPACE_PRE_SPACE;
   }
 
   bool NewlineIsSignificant() const {
     return mWhiteSpace == NS_STYLE_WHITESPACE_PRE ||
            mWhiteSpace == NS_STYLE_WHITESPACE_PRE_WRAP ||
            mWhiteSpace == NS_STYLE_WHITESPACE_PRE_LINE;
   }
 
-  bool NewlineIsDiscarded() const {
-    return mWhiteSpace == NS_STYLE_WHITESPACE_PRE_DISCARD_NEWLINES;
-  }
-
   bool WhiteSpaceOrNewlineIsSignificant() const {
     return mWhiteSpace == NS_STYLE_WHITESPACE_PRE ||
            mWhiteSpace == NS_STYLE_WHITESPACE_PRE_WRAP ||
            mWhiteSpace == NS_STYLE_WHITESPACE_PRE_LINE ||
-           mWhiteSpace == NS_STYLE_WHITESPACE_PRE_DISCARD_NEWLINES;
+           mWhiteSpace == NS_STYLE_WHITESPACE_PRE_SPACE;
+  }
+
+  bool TabIsSignificant() const {
+    return mWhiteSpace == NS_STYLE_WHITESPACE_PRE ||
+           mWhiteSpace == NS_STYLE_WHITESPACE_PRE_WRAP;
   }
 
   bool WhiteSpaceCanWrapStyle() const {
     return mWhiteSpace == NS_STYLE_WHITESPACE_NORMAL ||
            mWhiteSpace == NS_STYLE_WHITESPACE_PRE_WRAP ||
            mWhiteSpace == NS_STYLE_WHITESPACE_PRE_LINE;
   }
 
--- a/layout/style/test/property_database.js
+++ b/layout/style/test/property_database.js
@@ -3330,17 +3330,17 @@ var gCSSProperties = {
     other_values: [ "hidden", "collapse" ],
     invalid_values: []
   },
   "white-space": {
     domProp: "whiteSpace",
     inherited: true,
     type: CSS_TYPE_LONGHAND,
     initial_values: [ "normal" ],
-    other_values: [ "pre", "nowrap", "pre-wrap", "pre-line", "-moz-pre-discard-newlines" ],
+    other_values: [ "pre", "nowrap", "pre-wrap", "pre-line", "-moz-pre-space" ],
     invalid_values: []
   },
   "widows": {
     domProp: "widows",
     inherited: true,
     backend_only: true,
     type: CSS_TYPE_LONGHAND,
     // XXX requires display:block
--- a/layout/svg/svg.css
+++ b/layout/svg/svg.css
@@ -60,17 +60,17 @@ foreignObject {
 }
 
 *|*::-moz-svg-text {
   unicode-bidi: inherit;
   vector-effect: inherit;
 }
 
 *[xml|space=preserve] {
-  white-space: -moz-pre-discard-newlines;
+  white-space: -moz-pre-space;
 }
 
 *|*::-moz-svg-marker-anon-child {
   clip-path: inherit;
   filter: inherit;
   mask: inherit;
   opacity: inherit;
 }