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 186834 25abaa7e7bb518a7fc79219b73f4f7e262afcd0e
parent 186833 ec751fbe4ebd611c941cd6b4cf9e2a3ad42e39d7
child 186835 c69a670294fa6bc8d81f8a8bf46b03d7c4dfbe42
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs1019555
milestone32.0a1
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;
 }