Bug 1575495: part 4) Add `AddLineBreak` to `CurrentLineContent`. r=hsivonen
authorMirko Brodesser <mbrodesser@mozilla.com>
Fri, 23 Aug 2019 14:00:50 +0000
changeset 553586 60893d2dc2d52d63d1c200154bfefbf55740db0d
parent 553585 4f533bb7002667afaf762c30f33a675fd8afad42
child 553587 03f0d268860463a053a72413b7e10596c97fbd88
push id2165
push userffxbld-merge
push dateMon, 14 Oct 2019 16:30:58 +0000
treeherdermozilla-release@0eae18af659f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershsivonen
bugs1575495
milestone70.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 1575495: part 4) Add `AddLineBreak` to `CurrentLineContent`. r=hsivonen Differential Revision: https://phabricator.services.mozilla.com/D42840
dom/base/nsPlainTextSerializer.cpp
dom/base/nsPlainTextSerializer.h
xpcom/ds/nsCRT.h
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -49,16 +49,18 @@ static const int32_t kIndentIncrementHea
                                               indent h(x+1) this many
                                               columns more than h(x) */
 static const int32_t kIndentSizeList = kTabSize;
 // Indention of non-first lines of ul and ol
 static const int32_t kIndentSizeDD = kTabSize;  // Indention of <dd>
 static const char16_t kNBSP = 160;
 static const char16_t kSPACE = ' ';
 
+constexpr int32_t kNoFlags = 0;
+
 static int32_t HeaderLevel(nsAtom* aTag);
 static int32_t GetUnicharWidth(char16_t ucs);
 static int32_t GetUnicharStringWidth(const char16_t* pwcs, int32_t n);
 
 // Someday may want to make this non-const:
 static const uint32_t TagStackSize = 500;
 static const uint32_t OLStackSize = 100;
 
@@ -76,27 +78,56 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTION(nsPlainTextSerializer, mElement)
 
 nsresult NS_NewPlainTextSerializer(nsIContentSerializer** aSerializer) {
   RefPtr<nsPlainTextSerializer> it = new nsPlainTextSerializer();
   it.forget(aSerializer);
   return NS_OK;
 }
 
-void nsPlainTextSerializer::CurrentLineContent::MaybeReplaceNbsps(
-    const int32_t aFlags) {
-  if (!(aFlags & nsIDocumentEncoder::OutputPersistNBSP)) {
+// @param aFlags As defined in nsIDocumentEncoder.idl.
+static void DetermineLineBreak(const int32_t aFlags, nsAString& aLineBreak) {
+  // Set the line break character:
+  if ((aFlags & nsIDocumentEncoder::OutputCRLineBreak) &&
+      (aFlags & nsIDocumentEncoder::OutputLFLineBreak)) {
+    // Windows
+    aLineBreak.AssignLiteral(u"\r\n");
+  } else if (aFlags & nsIDocumentEncoder::OutputCRLineBreak) {
+    // Mac
+    aLineBreak.AssignLiteral(u"\r");
+  } else if (aFlags & nsIDocumentEncoder::OutputLFLineBreak) {
+    // Unix/DOM
+    aLineBreak.AssignLiteral(u"\n");
+  } else {
+    // Platform/default
+    aLineBreak.AssignLiteral(NS_ULINEBREAK);
+  }
+}
+
+nsPlainTextSerializer::CurrentLineContent::CurrentLineContent(
+    const int32_t aFlags)
+    : mFlags(aFlags) {
+  DetermineLineBreak(mFlags, mLineBreak);
+}
+
+void nsPlainTextSerializer::CurrentLineContent::MaybeReplaceNbsps() {
+  if (!(mFlags & nsIDocumentEncoder::OutputPersistNBSP)) {
     // First, replace all nbsp characters with spaces,
     // which the unicode encoder won't do for us.
     mValue.ReplaceChar(kNBSP, kSPACE);
   }
 }
 
+void nsPlainTextSerializer::CurrentLineContent::AppendLineBreak() {
+  mValue.Append(mLineBreak);
+}
+
 nsPlainTextSerializer::nsPlainTextSerializer()
-    : mFloatingLines(-1),
+    : mCurrentLineContent{kNoFlags},
+      mFloatingLines(-1),
       mLineBreakDue(false),
       kSpace(NS_LITERAL_STRING(" "))  // Init of "constant"
 {
   mOutputString = nullptr;
   mHeadLevel = 0;
   mAtFirstColumn = true;
   mIndent = 0;
   mCiteQuoteLevel = 0;
@@ -140,17 +171,17 @@ nsPlainTextSerializer::nsPlainTextSerial
 
 nsPlainTextSerializer::~nsPlainTextSerializer() {
   delete[] mTagStack;
   delete[] mOLStack;
   NS_WARNING_ASSERTION(mHeadLevel == 0, "Wrong head level!");
 }
 
 NS_IMETHODIMP
-nsPlainTextSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn,
+nsPlainTextSerializer::Init(const uint32_t aFlags, uint32_t aWrapColumn,
                             const Encoding* aEncoding, bool aIsCopying,
                             bool aIsWholeDocument,
                             bool* aNeedsPreformatScanning) {
 #ifdef DEBUG
   // Check if the major control flags are set correctly.
   if (aFlags & nsIDocumentEncoder::OutputFormatFlowed) {
     NS_ASSERTION(aFlags & nsIDocumentEncoder::OutputFormatted,
                  "If you want format=flowed, you must combine it with "
@@ -168,32 +199,16 @@ nsPlainTextSerializer::Init(uint32_t aFl
   mSettings.mFlags = aFlags;
   mWrapColumn = aWrapColumn;
 
   // Only create a linebreaker if we will handle wrapping.
   if (MayWrap() && MayBreakLines()) {
     mLineBreaker = nsContentUtils::LineBreaker();
   }
 
-  // Set the line break character:
-  if ((mSettings.mFlags & nsIDocumentEncoder::OutputCRLineBreak) &&
-      (mSettings.mFlags & nsIDocumentEncoder::OutputLFLineBreak)) {
-    // Windows
-    mLineBreak.AssignLiteral("\r\n");
-  } else if (mSettings.mFlags & nsIDocumentEncoder::OutputCRLineBreak) {
-    // Mac
-    mLineBreak.Assign(char16_t('\r'));
-  } else if (mSettings.mFlags & nsIDocumentEncoder::OutputLFLineBreak) {
-    // Unix/DOM
-    mLineBreak.Assign(char16_t('\n'));
-  } else {
-    // Platform/default
-    mLineBreak.AssignLiteral(NS_LINEBREAK);
-  }
-
   mLineBreakDue = false;
   mFloatingLines = -1;
 
   mPreformattedBlockBoundary = false;
 
   if (mSettings.mFlags & nsIDocumentEncoder::OutputFormatted) {
     // Get some prefs that controls how we do formatted output
     mSettings.mStructs = Preferences::GetBool(PREF_STRUCTS, mSettings.mStructs);
@@ -207,16 +222,18 @@ nsPlainTextSerializer::Init(uint32_t aFl
   // other places which use this serializer currently.
   mSettings.mWithRubyAnnotation =
       gAlwaysIncludeRuby ||
       (mSettings.mFlags & nsIDocumentEncoder::OutputRubyAnnotation);
 
   // XXX We should let the caller decide whether to do this or not
   mSettings.mFlags &= ~nsIDocumentEncoder::OutputNoFramesContent;
 
+  mCurrentLineContent = CurrentLineContent{mSettings.mFlags};
+
   return NS_OK;
 }
 
 bool nsPlainTextSerializer::GetLastBool(const nsTArray<bool>& aStack) {
   uint32_t size = aStack.Length();
   if (size == 0) {
     return false;
   }
@@ -321,17 +338,17 @@ nsPlainTextSerializer::AppendText(nsICon
   int32_t offset = textstr.FindCharInSet("\n\r");
   while (offset != kNotFound) {
     if (offset > start) {
       // Pass in the line
       DoAddText(false, Substring(textstr, start, offset - start));
     }
 
     // Pass in a newline
-    DoAddText(true, mLineBreak);
+    DoAddText();
 
     start = offset + 1;
     offset = textstr.FindCharInSet("\n\r", start);
   }
 
   // Consume the last bit of the string if there's any left
   if (start < length) {
     if (start) {
@@ -955,16 +972,18 @@ bool nsPlainTextSerializer::MustSuppress
     // Might be nice, eventually, to output just the selected element.
     // Read more in bug 31994.
     return true;
   }
 
   return false;
 }
 
+void nsPlainTextSerializer::DoAddText() { DoAddText(true, EmptyString()); }
+
 void nsPlainTextSerializer::DoAddText(bool aIsLineBreak,
                                       const nsAString& aText) {
   // If we don't want any output, just return
   if (!DoOutput()) {
     return;
   }
 
   if (!aIsLineBreak) {
@@ -1096,17 +1115,17 @@ void nsPlainTextSerializer::EnsureVertic
  * at the start of the line.
  */
 void nsPlainTextSerializer::FlushLine() {
   if (!mCurrentLineContent.mValue.IsEmpty()) {
     if (mAtFirstColumn) {
       OutputQuotesAndIndent();  // XXX: Should we always do this? Bug?
     }
 
-    mCurrentLineContent.MaybeReplaceNbsps(mSettings.mFlags);
+    mCurrentLineContent.MaybeReplaceNbsps();
     Output(mCurrentLineContent.mValue);
     mAtFirstColumn = false;
     mCurrentLineContent.mValue.Truncate();
     mCurrentLineContent.mWidth = 0;
   }
 }
 
 void nsPlainTextSerializer::Output(nsString& aString) {
@@ -1356,18 +1375,18 @@ void nsPlainTextSerializer::EndLine(bool
   if (mAtFirstColumn) {
     // If we don't have anything "real" to output we have to
     // make sure the indent doesn't end in a space since that
     // would trick a format=flowed-aware receiver.
     bool stripTrailingSpaces = mCurrentLineContent.mValue.IsEmpty();
     OutputQuotesAndIndent(stripTrailingSpaces);
   }
 
-  mCurrentLineContent.mValue.Append(mLineBreak);
-  mCurrentLineContent.MaybeReplaceNbsps(mSettings.mFlags);
+  mCurrentLineContent.MaybeReplaceNbsps();
+  mCurrentLineContent.AppendLineBreak();
   Output(mCurrentLineContent.mValue);
   mCurrentLineContent.mValue.Truncate();
   mCurrentLineContent.mWidth = 0;
   mAtFirstColumn = true;
   mInWhitespace = true;
   mLineBreakDue = false;
   mFloatingLines = -1;
 }
@@ -1552,21 +1571,22 @@ void nsPlainTextSerializer::Write(const 
       }
       mCurrentLineContent.mValue.Append(stringpart);
 
       if (outputQuotes) {
         // Note: this call messes with mAtFirstColumn
         OutputQuotesAndIndent();
       }
 
-      mCurrentLineContent.MaybeReplaceNbsps(mSettings.mFlags);
-      Output(mCurrentLineContent.mValue);
+      mCurrentLineContent.MaybeReplaceNbsps();
       if (outputLineBreak) {
-        Output(mLineBreak);
+        mCurrentLineContent.AppendLineBreak();
       }
+      Output(mCurrentLineContent.mValue);
+
       mAtFirstColumn = atFirstColumn;
     }
 
     // Reset mCurrentLineContent.mValue.
     mCurrentLineContent.mValue.Truncate();
 
 #ifdef DEBUG_wrapping
     printf("No wrapping: newline is %d, totLen is %d\n", newline, totLen);
--- a/dom/base/nsPlainTextSerializer.h
+++ b/dom/base/nsPlainTextSerializer.h
@@ -104,26 +104,27 @@ class nsPlainTextSerializer final : publ
    * Returns the local name of the element as an atom if the element is an
    * HTML element and the atom is a static atom. Otherwise, nullptr is returned.
    */
   static nsAtom* GetIdForContent(nsIContent* aContent);
   nsresult DoOpenContainer(nsAtom* aTag);
   nsresult DoCloseContainer(nsAtom* aTag);
   nsresult DoAddLeaf(nsAtom* aTag);
 
+  void DoAddText();
   // @param aText Ignored if aIsLineBreak is true.
   void DoAddText(bool aIsLineBreak, const nsAString& aText);
 
   // Inlined functions
-  inline bool MayWrap() {
+  inline bool MayWrap() const {
     return mWrapColumn &&
            ((mSettings.mFlags & nsIDocumentEncoder::OutputFormatted) ||
             (mSettings.mFlags & nsIDocumentEncoder::OutputWrap));
   }
-  inline bool MayBreakLines() {
+  inline bool MayBreakLines() const {
     return !(mSettings.mFlags & nsIDocumentEncoder::OutputDisallowLineBreaking);
   }
 
   inline bool DoOutput() const { return mHeadLevel == 0; }
 
   inline bool IsQuotedLine(const nsAString& aLine) {
     return !aLine.IsEmpty() && aLine.First() == char16_t('>');
   }
@@ -163,26 +164,36 @@ class nsPlainTextSerializer final : publ
     int32_t mFlags = 0;
 
     // Whether the output should include ruby annotations.
     bool mWithRubyAnnotation = false;
   };
 
   Settings mSettings;
 
+  // Excludes indentation and quotes.
   class CurrentLineContent {
    public:
     // @param aFlags As defined in nsIDocumentEncoder.idl.
-    void MaybeReplaceNbsps(int32_t aFlags);
+    explicit CurrentLineContent(int32_t aFlags);
 
-    // Excludes indentation and quotes.
+    void MaybeReplaceNbsps();
+
+    void AppendLineBreak();
+
     nsString mValue;
 
     // The width of the line as it will appear on the screen (approx.).
     uint32_t mWidth = 0;
+
+   private:
+    // As defined in nsIDocumentEncoder.idl.
+    int32_t mFlags;
+
+    nsString mLineBreak;
   };
 
   CurrentLineContent mCurrentLineContent;
 
   // If we've just written out a cite blockquote, we need to remember it
   // so we don't duplicate spaces before a <pre wrap> (which mail uses to quote
   // old messages).
   bool mHasWrittenCiteBlockquote;
@@ -252,17 +263,16 @@ class nsPlainTextSerializer final : publ
   uint32_t mIgnoreAboveIndex;
 
   // The stack for ordered lists
   int32_t* mOLStack;
   uint32_t mOLStackIndex;
 
   uint32_t mULCount;
 
-  nsString mLineBreak;
   RefPtr<mozilla::intl::LineBreaker> mLineBreaker;
 
   // Conveniance constant. It would be nice to have it as a const static
   // variable, but that causes issues with OpenBSD and module unloading.
   const nsString kSpace;
 
   // mIgnoredChildNodeLevel is used to tell if current node is an ignorable
   // child node. The initial value of mIgnoredChildNodeLevel is 0. When
--- a/xpcom/ds/nsCRT.h
+++ b/xpcom/ds/nsCRT.h
@@ -9,20 +9,22 @@
 #include <stdlib.h>
 #include <ctype.h>
 #include "plstr.h"
 #include "nscore.h"
 #include "nsCRTGlue.h"
 
 #if defined(XP_WIN)
 #  define NS_LINEBREAK "\015\012"
+#  define NS_ULINEBREAK u"\015\012"
 #  define NS_LINEBREAK_LEN 2
 #else
 #  ifdef XP_UNIX
 #    define NS_LINEBREAK "\012"
+#    define NS_ULINEBREAK u"\012"
 #    define NS_LINEBREAK_LEN 1
 #  endif /* XP_UNIX */
 #endif   /* XP_WIN */
 
 extern const char16_t kIsoLatin1ToUCS2[256];
 
 /// This is a wrapper class around all the C runtime functions.