Bug 1575218: part 1) Factor out `nsPlainTextSerializer`'s members to `Settings` struct. r=hsivonen
authorMirko Brodesser <mbrodesser@mozilla.com>
Wed, 21 Aug 2019 10:31:31 +0000
changeset 489161 76a17f3d411d2ae05ba9e73c597325708cb171cc
parent 489160 d7d41033f5615723ac638929850221c1d683d34c
child 489162 0e2e5f40690bb2fedf5720d92f87c98c329f6340
push id36465
push userdvarga@mozilla.com
push dateWed, 21 Aug 2019 16:47:43 +0000
treeherdermozilla-central@4ab60925635c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewershsivonen
bugs1575218
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 1575218: part 1) Factor out `nsPlainTextSerializer`'s members to `Settings` struct. r=hsivonen Differential Revision: https://phabricator.services.mozilla.com/D42649
dom/base/nsPlainTextSerializer.cpp
dom/base/nsPlainTextSerializer.h
--- a/dom/base/nsPlainTextSerializer.cpp
+++ b/dom/base/nsPlainTextSerializer.cpp
@@ -77,45 +77,41 @@ NS_IMPL_CYCLE_COLLECTION(nsPlainTextSeri
 
 nsresult NS_NewPlainTextSerializer(nsIContentSerializer** aSerializer) {
   RefPtr<nsPlainTextSerializer> it = new nsPlainTextSerializer();
   it.forget(aSerializer);
   return NS_OK;
 }
 
 nsPlainTextSerializer::nsPlainTextSerializer()
-    : mFlags(0),
-      mFloatingLines(-1),
+    : mFloatingLines(-1),
       mLineBreakDue(false),
       kSpace(NS_LITERAL_STRING(" "))  // Init of "constant"
 {
   mOutputString = nullptr;
   mHeadLevel = 0;
   mAtFirstColumn = true;
   mIndent = 0;
   mCiteQuoteLevel = 0;
-  mStructs = true;                              // will be read from prefs later
-  mHeaderStrategy = 1 /*indent increasingly*/;  // ditto
   mHasWrittenCiteBlockquote = false;
   mSpanLevel = 0;
   for (int32_t i = 0; i <= 6; i++) {
     mHeaderCounter[i] = 0;
   }
 
   // Line breaker
   mWrapColumn = 72;  // XXX magic number, we expect someone to reset this
   mCurrentLineWidth = 0;
 
   // Flow
   mEmptyLines = 1;  // The start of the document is an "empty line" in itself,
   mInWhitespace = false;
   mPreFormattedMail = false;
 
   mPreformattedBlockBoundary = false;
-  mWithRubyAnnotation = false;  // will be read from pref and flag later
 
   // initialize the tag stack to zero:
   // The stack only ever contains pointers to static atoms, so they don't
   // need refcounting.
   mTagStack = new nsAtom*[TagStackSize];
   mTagStackIndex = 0;
   mIgnoreAboveIndex = (uint32_t)kNotFound;
 
@@ -156,61 +152,62 @@ nsPlainTextSerializer::Init(uint32_t aFl
   if (aFlags & nsIDocumentEncoder::OutputFormatted) {
     NS_ASSERTION(
         !(aFlags & nsIDocumentEncoder::OutputPreformatted),
         "Can't do formatted and preformatted output at the same time!");
   }
 #endif
 
   *aNeedsPreformatScanning = true;
-  mFlags = aFlags;
+  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 ((mFlags & nsIDocumentEncoder::OutputCRLineBreak) &&
-      (mFlags & nsIDocumentEncoder::OutputLFLineBreak)) {
+  if ((mSettings.mFlags & nsIDocumentEncoder::OutputCRLineBreak) &&
+      (mSettings.mFlags & nsIDocumentEncoder::OutputLFLineBreak)) {
     // Windows
     mLineBreak.AssignLiteral("\r\n");
-  } else if (mFlags & nsIDocumentEncoder::OutputCRLineBreak) {
+  } else if (mSettings.mFlags & nsIDocumentEncoder::OutputCRLineBreak) {
     // Mac
     mLineBreak.Assign(char16_t('\r'));
-  } else if (mFlags & nsIDocumentEncoder::OutputLFLineBreak) {
+  } 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 (mFlags & nsIDocumentEncoder::OutputFormatted) {
+  if (mSettings.mFlags & nsIDocumentEncoder::OutputFormatted) {
     // Get some prefs that controls how we do formatted output
-    mStructs = Preferences::GetBool(PREF_STRUCTS, mStructs);
+    mSettings.mStructs = Preferences::GetBool(PREF_STRUCTS, mSettings.mStructs);
 
-    mHeaderStrategy =
-        Preferences::GetInt(PREF_HEADER_STRATEGY, mHeaderStrategy);
+    mSettings.mHeaderStrategy =
+        Preferences::GetInt(PREF_HEADER_STRATEGY, mSettings.mHeaderStrategy);
   }
 
   // The pref is default inited to false in libpref, but we use true
   // as fallback value because we don't want to affect behavior in
   // other places which use this serializer currently.
-  mWithRubyAnnotation =
-      gAlwaysIncludeRuby || (mFlags & nsIDocumentEncoder::OutputRubyAnnotation);
+  mSettings.mWithRubyAnnotation =
+      gAlwaysIncludeRuby ||
+      (mSettings.mFlags & nsIDocumentEncoder::OutputRubyAnnotation);
 
   // XXX We should let the caller decide whether to do this or not
-  mFlags &= ~nsIDocumentEncoder::OutputNoFramesContent;
+  mSettings.mFlags &= ~nsIDocumentEncoder::OutputNoFramesContent;
 
   return NS_OK;
 }
 
 bool nsPlainTextSerializer::GetLastBool(const nsTArray<bool>& aStack) {
   uint32_t size = aStack.Length();
   if (size == 0) {
     return false;
@@ -237,17 +234,17 @@ bool nsPlainTextSerializer::PopBool(nsTA
   if (size > 0) {
     returnValue = aStack.ElementAt(size - 1);
     aStack.RemoveElementAt(size - 1);
   }
   return returnValue;
 }
 
 bool nsPlainTextSerializer::IsIgnorableRubyAnnotation(nsAtom* aTag) {
-  if (mWithRubyAnnotation) {
+  if (mSettings.mWithRubyAnnotation) {
     return false;
   }
 
   return aTag == nsGkAtoms::rp || aTag == nsGkAtoms::rt ||
          aTag == nsGkAtoms::rtc;
 }
 
 // Return true if aElement has 'display:none' or if we just don't know.
@@ -443,26 +440,26 @@ nsresult nsPlainTextSerializer::DoOpenCo
     mIgnoredChildNodeLevel++;
     return NS_OK;
   }
   if (IsIgnorableScriptOrStyle(mElement)) {
     mIgnoredChildNodeLevel++;
     return NS_OK;
   }
 
-  if (mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) {
+  if (mSettings.mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) {
     if (mPreformattedBlockBoundary && DoOutput()) {
       // Should always end a line, but get no more whitespace
       if (mFloatingLines < 0) mFloatingLines = 0;
       mLineBreakDue = true;
     }
     mPreformattedBlockBoundary = false;
   }
 
-  if (mFlags & nsIDocumentEncoder::OutputRaw) {
+  if (mSettings.mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
   }
 
@@ -488,19 +485,19 @@ nsresult nsPlainTextSerializer::DoOpenCo
     nsresult rv = GetAttributeValue(nsGkAtoms::type, value);
     isInCiteBlockquote = NS_SUCCEEDED(rv) && value.EqualsIgnoreCase("cite");
   }
 
   if (mLineBreakDue && !isInCiteBlockquote) EnsureVerticalSpace(mFloatingLines);
 
   // Check if this tag's content that should not be output
   if ((aTag == nsGkAtoms::noscript &&
-       !(mFlags & nsIDocumentEncoder::OutputNoScriptContent)) ||
+       !(mSettings.mFlags & nsIDocumentEncoder::OutputNoScriptContent)) ||
       ((aTag == nsGkAtoms::iframe || aTag == nsGkAtoms::noframes) &&
-       !(mFlags & nsIDocumentEncoder::OutputNoFramesContent))) {
+       !(mSettings.mFlags & nsIDocumentEncoder::OutputNoFramesContent))) {
     // Ignore everything that follows the current tag in
     // question until a matching end tag is encountered.
     mIgnoreAboveIndex = mTagStackIndex - 1;
     return NS_OK;
   }
 
   if (aTag == nsGkAtoms::body) {
     // Try to figure out here whether we have a
@@ -595,34 +592,34 @@ nsresult nsPlainTextSerializer::DoOpenCo
   } else if (aTag == nsGkAtoms::ul) {
     // Indent here to support nested lists, which aren't included in li :-(
     EnsureVerticalSpace(mULCount + mOLStackIndex == 0 ? 1 : 0);
     // Must end the current line before we change indention
     mIndent += kIndentSizeList;
     mULCount++;
   } else if (aTag == nsGkAtoms::ol) {
     EnsureVerticalSpace(mULCount + mOLStackIndex == 0 ? 1 : 0);
-    if (mFlags & nsIDocumentEncoder::OutputFormatted) {
+    if (mSettings.mFlags & nsIDocumentEncoder::OutputFormatted) {
       // Must end the current line before we change indention
       if (mOLStackIndex < OLStackSize) {
         nsAutoString startAttr;
         int32_t startVal = 1;
         if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::start, startAttr))) {
           nsresult rv = NS_OK;
           startVal = startAttr.ToInteger(&rv);
           if (NS_FAILED(rv)) startVal = 1;
         }
         mOLStack[mOLStackIndex++] = startVal;
       }
     } else {
       mOLStackIndex++;
     }
     mIndent += kIndentSizeList;  // see ul
   } else if (aTag == nsGkAtoms::li &&
-             (mFlags & nsIDocumentEncoder::OutputFormatted)) {
+             (mSettings.mFlags & nsIDocumentEncoder::OutputFormatted)) {
     if (mTagStackIndex > 1 && IsInOL()) {
       if (mOLStackIndex > 0) {
         nsAutoString valueAttr;
         if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::value, valueAttr))) {
           nsresult rv = NS_OK;
           int32_t valueAttrVal = valueAttr.ToInteger(&rv);
           if (NS_SUCCEEDED(rv)) mOLStack[mOLStackIndex - 1] = valueAttrVal;
         }
@@ -667,31 +664,31 @@ nsresult nsPlainTextSerializer::DoOpenCo
 
   // Else make sure we'll separate block level tags,
   // even if we're about to leave, before doing any other formatting.
   else if (IsCssBlockLevelElement(mElement)) {
     EnsureVerticalSpace(0);
   }
 
   //////////////////////////////////////////////////////////////
-  if (!(mFlags & nsIDocumentEncoder::OutputFormatted)) {
+  if (!(mSettings.mFlags & nsIDocumentEncoder::OutputFormatted)) {
     return NS_OK;
   }
   //////////////////////////////////////////////////////////////
   // The rest of this routine is formatted output stuff,
   // which we should skip if we're not formatted:
   //////////////////////////////////////////////////////////////
 
   // Push on stack
   bool currentNodeIsConverted = IsCurrentNodeConverted();
 
   if (aTag == nsGkAtoms::h1 || aTag == nsGkAtoms::h2 || aTag == nsGkAtoms::h3 ||
       aTag == nsGkAtoms::h4 || aTag == nsGkAtoms::h5 || aTag == nsGkAtoms::h6) {
     EnsureVerticalSpace(2);
-    if (mHeaderStrategy == 2) {  // numbered
+    if (mSettings.mHeaderStrategy == 2) {  // numbered
       mIndent += kIndentSizeHeaders;
       // Caching
       int32_t level = HeaderLevel(aTag);
       // Increase counter for current level
       mHeaderCounter[level]++;
       // Reset all lower levels
       int32_t i;
 
@@ -702,42 +699,46 @@ nsresult nsPlainTextSerializer::DoOpenCo
       // Construct numbers
       nsAutoString leadup;
       for (i = 1; i <= level; i++) {
         leadup.AppendInt(mHeaderCounter[i]);
         leadup.Append(char16_t('.'));
       }
       leadup.Append(char16_t(' '));
       Write(leadup);
-    } else if (mHeaderStrategy == 1) {  // indent increasingly
+    } else if (mSettings.mHeaderStrategy == 1) {  // indent increasingly
       mIndent += kIndentSizeHeaders;
       for (int32_t i = HeaderLevel(aTag); i > 1; i--) {
         // for h(x), run x-1 times
         mIndent += kIndentIncrementHeaders;
       }
     }
   } else if (aTag == nsGkAtoms::a && !currentNodeIsConverted) {
     nsAutoString url;
     if (NS_SUCCEEDED(GetAttributeValue(nsGkAtoms::href, url)) &&
         !url.IsEmpty()) {
       mURL = url;
     }
-  } else if (aTag == nsGkAtoms::sup && mStructs && !currentNodeIsConverted) {
+  } else if (aTag == nsGkAtoms::sup && mSettings.mStructs &&
+             !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("^"));
-  } else if (aTag == nsGkAtoms::sub && mStructs && !currentNodeIsConverted) {
+  } else if (aTag == nsGkAtoms::sub && mSettings.mStructs &&
+             !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("_"));
-  } else if (aTag == nsGkAtoms::code && mStructs && !currentNodeIsConverted) {
+  } else if (aTag == nsGkAtoms::code && mSettings.mStructs &&
+             !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("|"));
-  } else if ((aTag == nsGkAtoms::strong || aTag == nsGkAtoms::b) && mStructs &&
-             !currentNodeIsConverted) {
+  } else if ((aTag == nsGkAtoms::strong || aTag == nsGkAtoms::b) &&
+             mSettings.mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("*"));
-  } else if ((aTag == nsGkAtoms::em || aTag == nsGkAtoms::i) && mStructs &&
+  } else if ((aTag == nsGkAtoms::em || aTag == nsGkAtoms::i) &&
+             mSettings.mStructs && !currentNodeIsConverted) {
+    Write(NS_LITERAL_STRING("/"));
+  } else if (aTag == nsGkAtoms::u && mSettings.mStructs &&
              !currentNodeIsConverted) {
-    Write(NS_LITERAL_STRING("/"));
-  } else if (aTag == nsGkAtoms::u && mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("_"));
   }
 
   /* Container elements are always block elements, so we shouldn't
      output any whitespace immediately after the container tag even if
      there's extra whitespace there because the HTML is pretty-printed
      or something. To ensure that happens, tell the serializer we're
      already in whitespace so it won't output more. */
@@ -751,26 +752,26 @@ nsresult nsPlainTextSerializer::DoCloseC
     mIgnoredChildNodeLevel--;
     return NS_OK;
   }
   if (IsIgnorableScriptOrStyle(mElement)) {
     mIgnoredChildNodeLevel--;
     return NS_OK;
   }
 
-  if (mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) {
+  if (mSettings.mFlags & nsIDocumentEncoder::OutputForPlainTextClipboardCopy) {
     if (DoOutput() && IsElementPreformatted() &&
         IsCssBlockLevelElement(mElement)) {
       // If we're closing a preformatted block element, output a line break
       // when we find a new container.
       mPreformattedBlockBoundary = true;
     }
   }
 
-  if (mFlags & nsIDocumentEncoder::OutputRaw) {
+  if (mSettings.mFlags & nsIDocumentEncoder::OutputRaw) {
     // Raw means raw.  Don't even think about doing anything fancy
     // here like indenting, adding line breaks or any other
     // characters such as list item bullets, quote characters
     // around <q>, etc.  I mean it!  Don't make me smack you!
 
     return NS_OK;
   }
 
@@ -789,17 +790,17 @@ nsresult nsPlainTextSerializer::DoCloseC
   }
 
   // End current line if we're ending a block level tag
   if ((aTag == nsGkAtoms::body) || (aTag == nsGkAtoms::html)) {
     // We want the output to end with a new line,
     // but in preformatted areas like text fields,
     // we can't emit newlines that weren't there.
     // So add the newline only in the case of formatted output.
-    if (mFlags & nsIDocumentEncoder::OutputFormatted) {
+    if (mSettings.mFlags & nsIDocumentEncoder::OutputFormatted) {
       EnsureVerticalSpace(0);
     } else {
       FlushLine();
     }
     // We won't want to do anything with these in formatted mode either,
     // so just return now:
     return NS_OK;
   }
@@ -810,17 +811,17 @@ nsresult nsPlainTextSerializer::DoCloseC
   }
 
   if (aTag == nsGkAtoms::tr) {
     PopBool(mHasWrittenCellsForRow);
     // Should always end a line, but get no more whitespace
     if (mFloatingLines < 0) mFloatingLines = 0;
     mLineBreakDue = true;
   } else if (((aTag == nsGkAtoms::li) || (aTag == nsGkAtoms::dt)) &&
-             (mFlags & nsIDocumentEncoder::OutputFormatted)) {
+             (mSettings.mFlags & nsIDocumentEncoder::OutputFormatted)) {
     // Items that should always end a line, but get no more whitespace
     if (mFloatingLines < 0) mFloatingLines = 0;
     mLineBreakDue = true;
   } else if (aTag == nsGkAtoms::pre) {
     mFloatingLines = GetLastBool(mIsInCiteBlockquote) ? 0 : 1;
     mLineBreakDue = true;
   } else if (aTag == nsGkAtoms::ul) {
     FlushLine();
@@ -868,68 +869,70 @@ nsresult nsPlainTextSerializer::DoCloseC
     mLineBreakDue = true;
   } else if (aTag == nsGkAtoms::q) {
     Write(NS_LITERAL_STRING("\""));
   } else if (IsCssBlockLevelElement(mElement)) {
     // All other blocks get 1 vertical space after them
     // in formatted mode, otherwise 0.
     // This is hard. Sometimes 0 is a better number, but
     // how to know?
-    if (mFlags & nsIDocumentEncoder::OutputFormatted)
+    if (mSettings.mFlags & nsIDocumentEncoder::OutputFormatted)
       EnsureVerticalSpace(1);
     else {
       if (mFloatingLines < 0) mFloatingLines = 0;
       mLineBreakDue = true;
     }
   }
 
   //////////////////////////////////////////////////////////////
-  if (!(mFlags & nsIDocumentEncoder::OutputFormatted)) {
+  if (!(mSettings.mFlags & nsIDocumentEncoder::OutputFormatted)) {
     return NS_OK;
   }
   //////////////////////////////////////////////////////////////
   // The rest of this routine is formatted output stuff,
   // which we should skip if we're not formatted:
   //////////////////////////////////////////////////////////////
 
   // Pop the currentConverted stack
   bool currentNodeIsConverted = IsCurrentNodeConverted();
 
   if (aTag == nsGkAtoms::h1 || aTag == nsGkAtoms::h2 || aTag == nsGkAtoms::h3 ||
       aTag == nsGkAtoms::h4 || aTag == nsGkAtoms::h5 || aTag == nsGkAtoms::h6) {
-    if (mHeaderStrategy) { /*numbered or indent increasingly*/
+    if (mSettings.mHeaderStrategy) { /*numbered or indent increasingly*/
       mIndent -= kIndentSizeHeaders;
     }
-    if (mHeaderStrategy == 1 /*indent increasingly*/) {
+    if (mSettings.mHeaderStrategy == 1 /*indent increasingly*/) {
       for (int32_t i = HeaderLevel(aTag); i > 1; i--) {
         // for h(x), run x-1 times
         mIndent -= kIndentIncrementHeaders;
       }
     }
     EnsureVerticalSpace(1);
   } else if (aTag == nsGkAtoms::a && !currentNodeIsConverted &&
              !mURL.IsEmpty()) {
     nsAutoString temp;
     temp.AssignLiteral(" <");
     temp += mURL;
     temp.Append(char16_t('>'));
     Write(temp);
     mURL.Truncate();
-  } else if ((aTag == nsGkAtoms::sup || aTag == nsGkAtoms::sub) && mStructs &&
+  } else if ((aTag == nsGkAtoms::sup || aTag == nsGkAtoms::sub) &&
+             mSettings.mStructs && !currentNodeIsConverted) {
+    Write(kSpace);
+  } else if (aTag == nsGkAtoms::code && mSettings.mStructs &&
              !currentNodeIsConverted) {
-    Write(kSpace);
-  } else if (aTag == nsGkAtoms::code && mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("|"));
-  } else if ((aTag == nsGkAtoms::strong || aTag == nsGkAtoms::b) && mStructs &&
-             !currentNodeIsConverted) {
+  } else if ((aTag == nsGkAtoms::strong || aTag == nsGkAtoms::b) &&
+             mSettings.mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("*"));
-  } else if ((aTag == nsGkAtoms::em || aTag == nsGkAtoms::i) && mStructs &&
+  } else if ((aTag == nsGkAtoms::em || aTag == nsGkAtoms::i) &&
+             mSettings.mStructs && !currentNodeIsConverted) {
+    Write(NS_LITERAL_STRING("/"));
+  } else if (aTag == nsGkAtoms::u && mSettings.mStructs &&
              !currentNodeIsConverted) {
-    Write(NS_LITERAL_STRING("/"));
-  } else if (aTag == nsGkAtoms::u && mStructs && !currentNodeIsConverted) {
     Write(NS_LITERAL_STRING("_"));
   }
 
   return NS_OK;
 }
 
 bool nsPlainTextSerializer::MustSuppressLeaf() {
   if (mIgnoredChildNodeLevel > 0) {
@@ -969,17 +972,17 @@ void nsPlainTextSerializer::DoAddText(bo
 
   if (aIsLineBreak) {
     // The only times we want to pass along whitespace from the original
     // html source are if we're forced into preformatted mode via flags,
     // or if we're prettyprinting and we're inside a <pre>.
     // Otherwise, either we're collapsing to minimal text, or we're
     // prettyprinting to mimic the html format, and in neither case
     // does the formatting of the html source help us.
-    if ((mFlags & nsIDocumentEncoder::OutputPreformatted) ||
+    if ((mSettings.mFlags & nsIDocumentEncoder::OutputPreformatted) ||
         (mPreFormattedMail && !mWrapColumn) || IsElementPreformatted()) {
       EnsureVerticalSpace(mEmptyLines + 1);
     } else if (!mInWhitespace) {
       Write(kSpace);
       mInWhitespace = true;
     }
     return;
   }
@@ -1014,17 +1017,17 @@ nsresult nsPlainTextSerializer::DoAddLea
     //      of non-HTML element.
     // XXX Do we need to call `EnsureVerticalSpace()` when the <br> element
     //     is not an HTML element?
     HTMLBRElement* brElement = HTMLBRElement::FromNodeOrNull(mElement);
     if (!brElement || !brElement->IsPaddingForEmptyLastLine()) {
       EnsureVerticalSpace(mEmptyLines + 1);
     }
   } else if (aTag == nsGkAtoms::hr &&
-             (mFlags & nsIDocumentEncoder::OutputFormatted)) {
+             (mSettings.mFlags & nsIDocumentEncoder::OutputFormatted)) {
     EnsureVerticalSpace(0);
 
     // Make a line of dashes as wide as the wrap width
     // XXX honoring percentage would be nice
     nsAutoString line;
     uint32_t width = (mWrapColumn > 0 ? mWrapColumn : 25);
     while (line.Length() < width) {
       line.Append(char16_t('-'));
@@ -1095,17 +1098,17 @@ void nsPlainTextSerializer::FlushLine() 
     mAtFirstColumn = mAtFirstColumn && mCurrentLine.IsEmpty();
     mCurrentLine.Truncate();
     mCurrentLineWidth = 0;
   }
 }
 
 void nsPlainTextSerializer::MaybeReplaceNbspsForOutput(
     nsString& aString) const {
-  if (!(mFlags & nsIDocumentEncoder::OutputPersistNBSP)) {
+  if (!(mSettings.mFlags & nsIDocumentEncoder::OutputPersistNBSP)) {
     // First, replace all nbsp characters with spaces,
     // which the unicode encoder won't do for us.
     aString.ReplaceChar(kNBSP, kSPACE);
   }
 }
 
 void nsPlainTextSerializer::Output(nsString& aString) {
   mOutputString->Append(aString);
@@ -1134,17 +1137,17 @@ void nsPlainTextSerializer::AddToLine(co
 
   int32_t linelength = mCurrentLine.Length();
   if (0 == linelength) {
     if (0 == aLineFragmentLength) {
       // Nothing at all. Are you kidding me?
       return;
     }
 
-    if (mFlags & nsIDocumentEncoder::OutputFormatFlowed) {
+    if (mSettings.mFlags & nsIDocumentEncoder::OutputFormatFlowed) {
       if (IsSpaceStuffable(aLineFragment) &&
           mCiteQuoteLevel == 0  // We space-stuff quoted lines anyway
       ) {
         // Space stuffing a la RFC 2646 (format=flowed).
         mCurrentLine.Append(char16_t(' '));
 
         if (MayWrap()) {
           mCurrentLineWidth += GetUnicharWidth(' ');
@@ -1257,17 +1260,17 @@ void nsPlainTextSerializer::AddToLine(co
           mCurrentLine.Right(restOfLine, linelength - goodSpace);
         }
         // if breaker was U+0020, it has to consider for delsp=yes support
         const bool breakBySpace = mCurrentLine.CharAt(goodSpace) == ' ';
         mCurrentLine.Truncate(goodSpace);
         EndLine(true, breakBySpace);
         mCurrentLine.Truncate();
         // Space stuff new line?
-        if (mFlags & nsIDocumentEncoder::OutputFormatFlowed) {
+        if (mSettings.mFlags & nsIDocumentEncoder::OutputFormatFlowed) {
           if (!restOfLine.IsEmpty() && IsSpaceStuffable(restOfLine.get()) &&
               mCiteQuoteLevel == 0  // We space-stuff quoted lines anyway
           ) {
             // Space stuffing a la RFC 2646 (format=flowed).
             mCurrentLine.Append(char16_t(' '));
             // XXX doesn't seem to work correctly for ' '
           }
         }
@@ -1300,36 +1303,38 @@ void nsPlainTextSerializer::EndLine(bool
   }
 
   /* In non-preformatted mode, remove spaces from the end of the line for
    * format=flowed compatibility. Don't do this for these special cases:
    * "-- ", the signature separator (RFC 2646) shouldn't be touched and
    * "- -- ", the OpenPGP dash-escaped signature separator in inline
    * signed messages according to the OpenPGP standard (RFC 2440).
    */
-  if (!(mFlags & nsIDocumentEncoder::OutputPreformatted) &&
+  if (!(mSettings.mFlags & nsIDocumentEncoder::OutputPreformatted) &&
       (aSoftlinebreak || !(mCurrentLine.EqualsLiteral("-- ") ||
                            mCurrentLine.EqualsLiteral("- -- ")))) {
     // Remove spaces from the end of the line.
     while (currentlinelength > 0 &&
            mCurrentLine[currentlinelength - 1] == ' ') {
       --currentlinelength;
     }
     mCurrentLine.SetLength(currentlinelength);
   }
 
-  if (aSoftlinebreak && (mFlags & nsIDocumentEncoder::OutputFormatFlowed) &&
+  if (aSoftlinebreak &&
+      (mSettings.mFlags & nsIDocumentEncoder::OutputFormatFlowed) &&
       (mIndent == 0)) {
     // Add the soft part of the soft linebreak (RFC 2646 4.1)
     // We only do this when there is no indentation since format=flowed
     // lines and indentation doesn't work well together.
 
     // If breaker character is ASCII space with RFC 3676 support (delsp=yes),
     // add twice space.
-    if ((mFlags & nsIDocumentEncoder::OutputFormatDelSp) && aBreakBySpace)
+    if ((mSettings.mFlags & nsIDocumentEncoder::OutputFormatDelSp) &&
+        aBreakBySpace)
       mCurrentLine.AppendLiteral("  ");
     else
       mCurrentLine.Append(char16_t(' '));
   }
 
   if (aSoftlinebreak) {
     mEmptyLines = 0;
   } else {
@@ -1437,17 +1442,17 @@ void nsPlainTextSerializer::Write(const 
 
   int32_t totLen = str.Length();
 
   // If the string is empty, do nothing:
   if (totLen <= 0) return;
 
   // For Flowed text change nbsp-ses to spaces at end of lines to allow them
   // to be cut off along with usual spaces if required. (bug #125928)
-  if (mFlags & nsIDocumentEncoder::OutputFormatFlowed) {
+  if (mSettings.mFlags & nsIDocumentEncoder::OutputFormatFlowed) {
     for (int32_t i = totLen - 1; i >= 0; i--) {
       char16_t c = str[i];
       if ('\n' == c || '\r' == c || ' ' == c || '\t' == c) continue;
       if (kNBSP == c)
         str.Replace(i, 1, ' ');
       else
         break;
     }
@@ -1526,17 +1531,17 @@ void nsPlainTextSerializer::Write(const 
           // There was a CRLF in the input. This used to be illegal and
           // stripped by the parser. Apparently not anymore. Let's skip
           // over the LF.
           bol++;
         }
       }
 
       mCurrentLine.Truncate();
-      if (mFlags & nsIDocumentEncoder::OutputFormatFlowed) {
+      if (mSettings.mFlags & nsIDocumentEncoder::OutputFormatFlowed) {
         if ((outputLineBreak || !spacesOnly) &&  // bugs 261467,125928
             !IsQuotedLine(stringpart) && !stringpart.EqualsLiteral("-- ") &&
             !stringpart.EqualsLiteral("- -- "))
           stringpart.Trim(" ", false, true, true);
         if (IsSpaceStuffable(stringpart.get()) && !IsQuotedLine(stringpart))
           mCurrentLine.Append(char16_t(' '));
       }
       mCurrentLine.Append(stringpart);
@@ -1597,17 +1602,17 @@ void nsPlainTextSerializer::Write(const 
           offsetIntoBuffer = str.get() + bol;
           AddToLine(offsetIntoBuffer, nextpos - bol);
           bol = nextpos + 1;
           continue;
         }
       }
       // If we're already in whitespace and not preformatted, just skip it:
       if (mInWhitespace && (nextpos == bol) && !mPreFormattedMail &&
-          !(mFlags & nsIDocumentEncoder::OutputPreformatted)) {
+          !(mSettings.mFlags & nsIDocumentEncoder::OutputPreformatted)) {
         // Skip whitespace
         bol++;
         continue;
       }
 
       if (nextpos == bol) {
         // Note that we are in whitespace.
         mInWhitespace = true;
@@ -1616,17 +1621,17 @@ void nsPlainTextSerializer::Write(const 
         bol++;
         continue;
       }
 
       mInWhitespace = true;
 
       offsetIntoBuffer = str.get() + bol;
       if (mPreFormattedMail ||
-          (mFlags & nsIDocumentEncoder::OutputPreformatted)) {
+          (mSettings.mFlags & nsIDocumentEncoder::OutputPreformatted)) {
         // Preserve the real whitespace character
         nextpos++;
         AddToLine(offsetIntoBuffer, nextpos - bol);
         bol = nextpos;
       } else {
         // Replace the whitespace with a space
         AddToLine(offsetIntoBuffer, nextpos - bol);
         AddToLine(kSpace.get(), 1);
--- a/dom/base/nsPlainTextSerializer.h
+++ b/dom/base/nsPlainTextSerializer.h
@@ -109,21 +109,22 @@ class nsPlainTextSerializer final : publ
   static nsAtom* GetIdForContent(nsIContent* aContent);
   nsresult DoOpenContainer(nsAtom* aTag);
   nsresult DoCloseContainer(nsAtom* aTag);
   nsresult DoAddLeaf(nsAtom* aTag);
   void DoAddText(bool aIsWhitespace, const nsAString& aText);
 
   // Inlined functions
   inline bool MayWrap() {
-    return mWrapColumn && ((mFlags & nsIDocumentEncoder::OutputFormatted) ||
-                           (mFlags & nsIDocumentEncoder::OutputWrap));
+    return mWrapColumn &&
+           ((mSettings.mFlags & nsIDocumentEncoder::OutputFormatted) ||
+            (mSettings.mFlags & nsIDocumentEncoder::OutputWrap));
   }
   inline bool MayBreakLines() {
-    return !(mFlags & nsIDocumentEncoder::OutputDisallowLineBreaking);
+    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('>');
   }
 
@@ -143,29 +144,47 @@ class nsPlainTextSerializer final : publ
   // https://drafts.csswg.org/css-display/#block-level
   static bool IsCssBlockLevelElement(mozilla::dom::Element* aElement);
 
  private:
   nsString mCurrentLine;
   uint32_t mHeadLevel;
   bool mAtFirstColumn;
 
-  bool mStructs;  // Output structs (pref)
+  struct Settings {
+    // Pref: converter.html2txt.structs.
+    bool mStructs = true;
+
+    // Pref: converter.html2txt.header_strategy.
+    int32_t mHeaderStrategy = 1; /* Header strategy (pref)
+                                  0 = no indention
+                                  1 = indention, increased with
+                                      header level (default)
+                                  2 = numbering and slight indention */
+
+    // Flags defined in nsIDocumentEncoder.idl.
+    int32_t mFlags = 0;
+
+    // Whether the output should include ruby annotations.
+    bool mWithRubyAnnotation = false;
+  };
+
+  Settings mSettings;
+
 
   // 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;
 
   int32_t mIndent;
   // mInIndentString keeps a header that has to be written in the indent.
   // That could be, for instance, the bullet in a bulleted list.
   nsString mInIndentString;
   int32_t mCiteQuoteLevel;
-  int32_t mFlags;
   int32_t mFloatingLines;  // To store the number of lazy line breaks
 
   // The wrap column is how many standard sized chars (western languages)
   // should be allowed on a line. There could be less chars if the chars
   // are wider than latin chars of more if the chars are more narrow.
   uint32_t mWrapColumn;
 
   // The width of the line as it will appear on the screen (approx.)
@@ -186,25 +205,17 @@ class nsPlainTextSerializer final : publ
   // While handling a new tag, this variable should remind if any line break
   // is due because of a closing tag. Setting it to "TRUE" while closing the
   // tags. Hence opening tags are guaranteed to start with appropriate line
   // breaks.
   bool mLineBreakDue;
 
   bool mPreformattedBlockBoundary;
 
-  // Whether the output should include ruby annotations.
-  bool mWithRubyAnnotation;
-
   nsString mURL;
-  int32_t mHeaderStrategy;   /* Header strategy (pref)
-                                0 = no indention
-                                1 = indention, increased with
-                                    header level (default)
-                                2 = numbering and slight indention */
   int32_t mHeaderCounter[7]; /* For header-numbering:
                                 Number of previous headers of
                                 the same depth and in the same
                                 section.
                                 mHeaderCounter[1] for <h1> etc. */
 
   RefPtr<mozilla::dom::Element> mElement;