tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsXMLContentSerializer.cpp (62953B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 * nsIContentSerializer implementation that can be used with an
      9 * nsIDocumentEncoder to convert an XML DOM to an XML string that
     10 * could be parsed into more or less the original DOM.
     11 */
     12 
     13 #include "nsXMLContentSerializer.h"
     14 
     15 #include "mozilla/Encoding.h"
     16 #include "mozilla/Sprintf.h"
     17 #include "mozilla/dom/CharacterDataBuffer.h"
     18 #include "mozilla/dom/Comment.h"
     19 #include "mozilla/dom/CustomElementRegistry.h"
     20 #include "mozilla/dom/Document.h"
     21 #include "mozilla/dom/DocumentType.h"
     22 #include "mozilla/dom/Element.h"
     23 #include "mozilla/dom/ProcessingInstruction.h"
     24 #include "mozilla/dom/Text.h"
     25 #include "mozilla/intl/Segmenter.h"
     26 #include "nsAttrName.h"
     27 #include "nsCRT.h"
     28 #include "nsContentUtils.h"
     29 #include "nsElementTable.h"
     30 #include "nsGkAtoms.h"
     31 #include "nsIContent.h"
     32 #include "nsIContentInlines.h"
     33 #include "nsIDocumentEncoder.h"
     34 #include "nsNameSpaceManager.h"
     35 #include "nsParserConstants.h"
     36 #include "nsString.h"
     37 #include "nsUnicharUtils.h"
     38 
     39 using namespace mozilla;
     40 using namespace mozilla::dom;
     41 
     42 #define kXMLNS "xmlns"
     43 
     44 // to be readable, we assume that an indented line contains
     45 // at least this number of characters (arbitrary value here).
     46 // This is a limit for the indentation.
     47 #define MIN_INDENTED_LINE_LENGTH 15
     48 
     49 // the string used to indent.
     50 #define INDENT_STRING "  "
     51 #define INDENT_STRING_LENGTH 2
     52 
     53 nsresult NS_NewXMLContentSerializer(nsIContentSerializer** aSerializer) {
     54  RefPtr<nsXMLContentSerializer> it = new nsXMLContentSerializer();
     55  it.forget(aSerializer);
     56  return NS_OK;
     57 }
     58 
     59 nsXMLContentSerializer::nsXMLContentSerializer()
     60    : mPrefixIndex(0),
     61      mColPos(0),
     62      mIndentOverflow(0),
     63      mIsIndentationAddedOnCurrentLine(false),
     64      mInAttribute(false),
     65      mAddNewlineForRootNode(false),
     66      mAddSpace(false),
     67      mMayIgnoreLineBreakSequence(false),
     68      mBodyOnly(false),
     69      mInBody(0) {}
     70 
     71 nsXMLContentSerializer::~nsXMLContentSerializer() = default;
     72 
     73 NS_IMPL_ISUPPORTS(nsXMLContentSerializer, nsIContentSerializer)
     74 
     75 NS_IMETHODIMP
     76 nsXMLContentSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn,
     77                             const Encoding* aEncoding, bool aIsCopying,
     78                             bool aRewriteEncodingDeclaration,
     79                             bool* aNeedsPreformatScanning,
     80                             nsAString& aOutput) {
     81  *aNeedsPreformatScanning = false;
     82  mPrefixIndex = 0;
     83  mColPos = 0;
     84  mIndentOverflow = 0;
     85  mIsIndentationAddedOnCurrentLine = false;
     86  mInAttribute = false;
     87  mAddNewlineForRootNode = false;
     88  mAddSpace = false;
     89  mMayIgnoreLineBreakSequence = false;
     90  mBodyOnly = false;
     91  mInBody = 0;
     92 
     93  if (aEncoding) {
     94    aEncoding->Name(mCharset);
     95  }
     96  mFlags = aFlags;
     97 
     98  // Set the line break character:
     99  if ((mFlags & nsIDocumentEncoder::OutputCRLineBreak) &&
    100      (mFlags & nsIDocumentEncoder::OutputLFLineBreak)) {  // Windows
    101    mLineBreak.AssignLiteral("\r\n");
    102  } else if (mFlags & nsIDocumentEncoder::OutputCRLineBreak) {  // Mac
    103    mLineBreak.Assign('\r');
    104  } else if (mFlags & nsIDocumentEncoder::OutputLFLineBreak) {  // Unix/DOM
    105    mLineBreak.Assign('\n');
    106  } else {
    107    mLineBreak.AssignLiteral(NS_LINEBREAK);  // Platform/default
    108  }
    109 
    110  mDoRaw = !!(mFlags & nsIDocumentEncoder::OutputRaw);
    111 
    112  mDoFormat = (mFlags & nsIDocumentEncoder::OutputFormatted && !mDoRaw);
    113 
    114  mDoWrap = (mFlags & nsIDocumentEncoder::OutputWrap && !mDoRaw);
    115 
    116  mAllowLineBreaking =
    117      !(mFlags & nsIDocumentEncoder::OutputDisallowLineBreaking);
    118 
    119  if (!aWrapColumn) {
    120    mMaxColumn = 72;
    121  } else {
    122    mMaxColumn = aWrapColumn;
    123  }
    124 
    125  mOutput = &aOutput;
    126  mPreLevel = 0;
    127  mIsIndentationAddedOnCurrentLine = false;
    128  return NS_OK;
    129 }
    130 
    131 nsresult nsXMLContentSerializer::AppendTextData(Text* aText,
    132                                                int32_t aStartOffset,
    133                                                int32_t aEndOffset,
    134                                                nsAString& aStr,
    135                                                bool aTranslateEntities) {
    136  const CharacterDataBuffer* characterDataBuffer = nullptr;
    137  if (!aText || !(characterDataBuffer = aText->GetCharacterDataBuffer())) {
    138    return NS_ERROR_FAILURE;
    139  }
    140 
    141  int32_t fragLength = characterDataBuffer->GetLength();
    142  int32_t endoffset =
    143      (aEndOffset == -1) ? fragLength : std::min(aEndOffset, fragLength);
    144  int32_t length = endoffset - aStartOffset;
    145 
    146  NS_ASSERTION(aStartOffset >= 0, "Negative start offset for text fragment!");
    147  NS_ASSERTION(aStartOffset <= endoffset,
    148               "A start offset is beyond the end of the text fragment!");
    149 
    150  if (length <= 0) {
    151    // XXX Zero is a legal value, maybe non-zero values should be an
    152    // error.
    153    return NS_OK;
    154  }
    155 
    156  if (characterDataBuffer->Is2b()) {
    157    const char16_t* strStart = characterDataBuffer->Get2b() + aStartOffset;
    158    if (aTranslateEntities) {
    159      NS_ENSURE_TRUE(AppendAndTranslateEntities(
    160                         Substring(strStart, strStart + length), aStr),
    161                     NS_ERROR_OUT_OF_MEMORY);
    162    } else {
    163      NS_ENSURE_TRUE(aStr.Append(Substring(strStart, strStart + length),
    164                                 mozilla::fallible),
    165                     NS_ERROR_OUT_OF_MEMORY);
    166    }
    167  } else {
    168    nsAutoString utf16;
    169    if (!CopyASCIItoUTF16(
    170            Span(characterDataBuffer->Get1b() + aStartOffset, length), utf16,
    171            mozilla::fallible_t())) {
    172      return NS_ERROR_OUT_OF_MEMORY;
    173    }
    174    if (aTranslateEntities) {
    175      NS_ENSURE_TRUE(AppendAndTranslateEntities(utf16, aStr),
    176                     NS_ERROR_OUT_OF_MEMORY);
    177    } else {
    178      NS_ENSURE_TRUE(aStr.Append(utf16, mozilla::fallible),
    179                     NS_ERROR_OUT_OF_MEMORY);
    180    }
    181  }
    182 
    183  return NS_OK;
    184 }
    185 
    186 NS_IMETHODIMP
    187 nsXMLContentSerializer::AppendText(Text* aText, int32_t aStartOffset,
    188                                   int32_t aEndOffset) {
    189  NS_ENSURE_ARG(aText);
    190  NS_ENSURE_STATE(mOutput);
    191 
    192  nsAutoString data;
    193  nsresult rv;
    194 
    195  rv = AppendTextData(aText, aStartOffset, aEndOffset, data, true);
    196  if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
    197 
    198  if (mDoRaw || PreLevel() > 0) {
    199    NS_ENSURE_TRUE(AppendToStringConvertLF(data, *mOutput),
    200                   NS_ERROR_OUT_OF_MEMORY);
    201  } else if (mDoFormat) {
    202    NS_ENSURE_TRUE(AppendToStringFormatedWrapped(data, *mOutput),
    203                   NS_ERROR_OUT_OF_MEMORY);
    204  } else if (mDoWrap) {
    205    NS_ENSURE_TRUE(AppendToStringWrapped(data, *mOutput),
    206                   NS_ERROR_OUT_OF_MEMORY);
    207  } else {
    208    NS_ENSURE_TRUE(AppendToStringConvertLF(data, *mOutput),
    209                   NS_ERROR_OUT_OF_MEMORY);
    210  }
    211 
    212  return NS_OK;
    213 }
    214 
    215 NS_IMETHODIMP
    216 nsXMLContentSerializer::AppendCDATASection(Text* aCDATASection,
    217                                           int32_t aStartOffset,
    218                                           int32_t aEndOffset) {
    219  NS_ENSURE_ARG(aCDATASection);
    220  NS_ENSURE_STATE(mOutput);
    221  MOZ_ASSERT(aCDATASection->NodeType() == nsINode::CDATA_SECTION_NODE);
    222 
    223  nsresult rv;
    224 
    225  constexpr auto cdata = u"<![CDATA["_ns;
    226 
    227  if (mDoRaw || PreLevel() > 0) {
    228    NS_ENSURE_TRUE(AppendToString(cdata, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    229  } else if (mDoFormat) {
    230    NS_ENSURE_TRUE(AppendToStringFormatedWrapped(cdata, *mOutput),
    231                   NS_ERROR_OUT_OF_MEMORY);
    232  } else if (mDoWrap) {
    233    NS_ENSURE_TRUE(AppendToStringWrapped(cdata, *mOutput),
    234                   NS_ERROR_OUT_OF_MEMORY);
    235  } else {
    236    NS_ENSURE_TRUE(AppendToString(cdata, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    237  }
    238 
    239  nsAutoString data;
    240  rv = AppendTextData(aCDATASection, aStartOffset, aEndOffset, data, false);
    241  if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
    242 
    243  NS_ENSURE_TRUE(AppendToStringConvertLF(data, *mOutput),
    244                 NS_ERROR_OUT_OF_MEMORY);
    245 
    246  NS_ENSURE_TRUE(AppendToString(u"]]>"_ns, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    247 
    248  return NS_OK;
    249 }
    250 
    251 NS_IMETHODIMP
    252 nsXMLContentSerializer::AppendProcessingInstruction(ProcessingInstruction* aPI,
    253                                                    int32_t aStartOffset,
    254                                                    int32_t aEndOffset) {
    255  NS_ENSURE_STATE(mOutput);
    256 
    257  nsAutoString target, data, start;
    258 
    259  NS_ENSURE_TRUE(MaybeAddNewlineForRootNode(*mOutput), NS_ERROR_OUT_OF_MEMORY);
    260 
    261  aPI->GetTarget(target);
    262 
    263  aPI->GetData(data);
    264 
    265  NS_ENSURE_TRUE(start.AppendLiteral("<?", mozilla::fallible),
    266                 NS_ERROR_OUT_OF_MEMORY);
    267  NS_ENSURE_TRUE(start.Append(target, mozilla::fallible),
    268                 NS_ERROR_OUT_OF_MEMORY);
    269 
    270  if (mDoRaw || PreLevel() > 0) {
    271    NS_ENSURE_TRUE(AppendToString(start, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    272  } else if (mDoFormat) {
    273    if (mAddSpace) {
    274      NS_ENSURE_TRUE(AppendNewLineToString(*mOutput), NS_ERROR_OUT_OF_MEMORY);
    275    }
    276    NS_ENSURE_TRUE(AppendToStringFormatedWrapped(start, *mOutput),
    277                   NS_ERROR_OUT_OF_MEMORY);
    278  } else if (mDoWrap) {
    279    NS_ENSURE_TRUE(AppendToStringWrapped(start, *mOutput),
    280                   NS_ERROR_OUT_OF_MEMORY);
    281  } else {
    282    NS_ENSURE_TRUE(AppendToString(start, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    283  }
    284 
    285  if (!data.IsEmpty()) {
    286    NS_ENSURE_TRUE(AppendToString(char16_t(' '), *mOutput),
    287                   NS_ERROR_OUT_OF_MEMORY);
    288    NS_ENSURE_TRUE(AppendToStringConvertLF(data, *mOutput),
    289                   NS_ERROR_OUT_OF_MEMORY);
    290  }
    291  NS_ENSURE_TRUE(AppendToString(u"?>"_ns, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    292 
    293  MaybeFlagNewlineForRootNode(aPI);
    294 
    295  return NS_OK;
    296 }
    297 
    298 NS_IMETHODIMP
    299 nsXMLContentSerializer::AppendComment(Comment* aComment, int32_t aStartOffset,
    300                                      int32_t aEndOffset) {
    301  NS_ENSURE_STATE(mOutput);
    302 
    303  nsAutoString data;
    304  aComment->GetData(data);
    305 
    306  int32_t dataLength = data.Length();
    307  if (aStartOffset || (aEndOffset != -1 && aEndOffset < dataLength)) {
    308    int32_t length =
    309        (aEndOffset == -1) ? dataLength : std::min(aEndOffset, dataLength);
    310    length -= aStartOffset;
    311 
    312    nsAutoString frag;
    313    if (length > 0) {
    314      data.Mid(frag, aStartOffset, length);
    315    }
    316    data.Assign(frag);
    317  }
    318 
    319  NS_ENSURE_TRUE(MaybeAddNewlineForRootNode(*mOutput), NS_ERROR_OUT_OF_MEMORY);
    320 
    321  constexpr auto startComment = u"<!--"_ns;
    322 
    323  if (mDoRaw || PreLevel() > 0) {
    324    NS_ENSURE_TRUE(AppendToString(startComment, *mOutput),
    325                   NS_ERROR_OUT_OF_MEMORY);
    326  } else if (mDoFormat) {
    327    if (mAddSpace) {
    328      NS_ENSURE_TRUE(AppendNewLineToString(*mOutput), NS_ERROR_OUT_OF_MEMORY);
    329    }
    330    NS_ENSURE_TRUE(AppendToStringFormatedWrapped(startComment, *mOutput),
    331                   NS_ERROR_OUT_OF_MEMORY);
    332  } else if (mDoWrap) {
    333    NS_ENSURE_TRUE(AppendToStringWrapped(startComment, *mOutput),
    334                   NS_ERROR_OUT_OF_MEMORY);
    335  } else {
    336    NS_ENSURE_TRUE(AppendToString(startComment, *mOutput),
    337                   NS_ERROR_OUT_OF_MEMORY);
    338  }
    339 
    340  // Even if mDoformat, we don't format the content because it
    341  // could have been preformated by the author
    342  NS_ENSURE_TRUE(AppendToStringConvertLF(data, *mOutput),
    343                 NS_ERROR_OUT_OF_MEMORY);
    344  NS_ENSURE_TRUE(AppendToString(u"-->"_ns, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    345 
    346  MaybeFlagNewlineForRootNode(aComment);
    347 
    348  return NS_OK;
    349 }
    350 
    351 NS_IMETHODIMP
    352 nsXMLContentSerializer::AppendDoctype(DocumentType* aDocType) {
    353  NS_ENSURE_STATE(mOutput);
    354 
    355  nsAutoString name, publicId, systemId;
    356  aDocType->GetName(name);
    357  aDocType->GetPublicId(publicId);
    358  aDocType->GetSystemId(systemId);
    359 
    360  NS_ENSURE_TRUE(MaybeAddNewlineForRootNode(*mOutput), NS_ERROR_OUT_OF_MEMORY);
    361 
    362  NS_ENSURE_TRUE(AppendToString(u"<!DOCTYPE "_ns, *mOutput),
    363                 NS_ERROR_OUT_OF_MEMORY);
    364  NS_ENSURE_TRUE(AppendToString(name, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    365 
    366  char16_t quote;
    367  if (!publicId.IsEmpty()) {
    368    NS_ENSURE_TRUE(AppendToString(u" PUBLIC "_ns, *mOutput),
    369                   NS_ERROR_OUT_OF_MEMORY);
    370    if (publicId.FindChar(char16_t('"')) == -1) {
    371      quote = char16_t('"');
    372    } else {
    373      quote = char16_t('\'');
    374    }
    375    NS_ENSURE_TRUE(AppendToString(quote, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    376    NS_ENSURE_TRUE(AppendToString(publicId, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    377    NS_ENSURE_TRUE(AppendToString(quote, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    378 
    379    if (!systemId.IsEmpty()) {
    380      NS_ENSURE_TRUE(AppendToString(char16_t(' '), *mOutput),
    381                     NS_ERROR_OUT_OF_MEMORY);
    382      if (systemId.FindChar(char16_t('"')) == -1) {
    383        quote = char16_t('"');
    384      } else {
    385        quote = char16_t('\'');
    386      }
    387      NS_ENSURE_TRUE(AppendToString(quote, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    388      NS_ENSURE_TRUE(AppendToString(systemId, *mOutput),
    389                     NS_ERROR_OUT_OF_MEMORY);
    390      NS_ENSURE_TRUE(AppendToString(quote, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    391    }
    392  } else if (!systemId.IsEmpty()) {
    393    if (systemId.FindChar(char16_t('"')) == -1) {
    394      quote = char16_t('"');
    395    } else {
    396      quote = char16_t('\'');
    397    }
    398    NS_ENSURE_TRUE(AppendToString(u" SYSTEM "_ns, *mOutput),
    399                   NS_ERROR_OUT_OF_MEMORY);
    400    NS_ENSURE_TRUE(AppendToString(quote, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    401    NS_ENSURE_TRUE(AppendToString(systemId, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    402    NS_ENSURE_TRUE(AppendToString(quote, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    403  }
    404 
    405  NS_ENSURE_TRUE(AppendToString(kGreaterThan, *mOutput),
    406                 NS_ERROR_OUT_OF_MEMORY);
    407  MaybeFlagNewlineForRootNode(aDocType);
    408 
    409  return NS_OK;
    410 }
    411 
    412 nsresult nsXMLContentSerializer::PushNameSpaceDecl(const nsAString& aPrefix,
    413                                                   const nsAString& aURI,
    414                                                   nsIContent* aOwner) {
    415  NameSpaceDecl* decl = mNameSpaceStack.AppendElement();
    416  if (!decl) return NS_ERROR_OUT_OF_MEMORY;
    417 
    418  decl->mPrefix.Assign(aPrefix);
    419  decl->mURI.Assign(aURI);
    420  // Don't addref - this weak reference will be removed when
    421  // we pop the stack
    422  decl->mOwner = aOwner;
    423  return NS_OK;
    424 }
    425 
    426 void nsXMLContentSerializer::PopNameSpaceDeclsFor(nsIContent* aOwner) {
    427  int32_t index, count;
    428 
    429  count = mNameSpaceStack.Length();
    430  for (index = count - 1; index >= 0; index--) {
    431    if (mNameSpaceStack[index].mOwner != aOwner) {
    432      break;
    433    }
    434    mNameSpaceStack.RemoveLastElement();
    435  }
    436 }
    437 
    438 bool nsXMLContentSerializer::ConfirmPrefix(nsAString& aPrefix,
    439                                           const nsAString& aURI,
    440                                           nsIContent* aElement,
    441                                           bool aIsAttribute) {
    442  if (aPrefix.EqualsLiteral(kXMLNS)) {
    443    return false;
    444  }
    445 
    446  if (aURI.EqualsLiteral("http://www.w3.org/XML/1998/namespace")) {
    447    // The prefix must be xml for this namespace. We don't need to declare it,
    448    // so always just set the prefix to xml.
    449    aPrefix.AssignLiteral("xml");
    450 
    451    return false;
    452  }
    453 
    454  bool mustHavePrefix;
    455  if (aIsAttribute) {
    456    if (aURI.IsEmpty()) {
    457      // Attribute in the null namespace.  This just shouldn't have a prefix.
    458      // And there's no need to push any namespace decls
    459      aPrefix.Truncate();
    460      return false;
    461    }
    462 
    463    // Attribute not in the null namespace -- must have a prefix
    464    mustHavePrefix = true;
    465  } else {
    466    // Not an attribute, so doesn't _have_ to have a prefix
    467    mustHavePrefix = false;
    468  }
    469 
    470  // Keep track of the closest prefix that's bound to aURI and whether we've
    471  // found such a thing.  closestURIMatch holds the prefix, and uriMatch
    472  // indicates whether we actually have one.
    473  nsAutoString closestURIMatch;
    474  bool uriMatch = false;
    475 
    476  // Also keep track of whether we've seen aPrefix already.  If we have, that
    477  // means that it's already bound to a URI different from aURI, so even if we
    478  // later (so in a more outer scope) see it bound to aURI we can't reuse it.
    479  bool haveSeenOurPrefix = false;
    480 
    481  int32_t count = mNameSpaceStack.Length();
    482  int32_t index = count - 1;
    483  while (index >= 0) {
    484    NameSpaceDecl& decl = mNameSpaceStack.ElementAt(index);
    485    // Check if we've found a prefix match
    486    if (aPrefix.Equals(decl.mPrefix)) {
    487      // If the URIs match and aPrefix is not bound to any other URI, we can
    488      // use aPrefix
    489      if (!haveSeenOurPrefix && aURI.Equals(decl.mURI)) {
    490        // Just use our uriMatch stuff.  That will deal with an empty aPrefix
    491        // the right way.  We can break out of the loop now, though.
    492        uriMatch = true;
    493        closestURIMatch = aPrefix;
    494        break;
    495      }
    496 
    497      haveSeenOurPrefix = true;
    498 
    499      // If they don't, and either:
    500      // 1) We have a prefix (so we'd be redeclaring this prefix to point to a
    501      //    different namespace) or
    502      // 2) We're looking at an existing default namespace decl on aElement (so
    503      //    we can't create a new default namespace decl for this URI)
    504      // then generate a new prefix.  Note that we do NOT generate new prefixes
    505      // if we happen to have aPrefix == decl->mPrefix == "" and mismatching
    506      // URIs when |decl| doesn't have aElement as its owner.  In that case we
    507      // can simply push the new namespace URI as the default namespace for
    508      // aElement.
    509      if (!aPrefix.IsEmpty() || decl.mOwner == aElement) {
    510        NS_ASSERTION(!aURI.IsEmpty(),
    511                     "Not allowed to add a xmlns attribute with an empty "
    512                     "namespace name unless it declares the default "
    513                     "namespace.");
    514 
    515        GenerateNewPrefix(aPrefix);
    516        // Now we need to validate our new prefix/uri combination; check it
    517        // against the full namespace stack again.  Note that just restarting
    518        // the while loop is ok, since we haven't changed aURI, so the
    519        // closestURIMatch and uriMatch state is not affected.
    520        index = count - 1;
    521        haveSeenOurPrefix = false;
    522        continue;
    523      }
    524    }
    525 
    526    // If we've found a URI match, then record the first one
    527    if (!uriMatch && aURI.Equals(decl.mURI)) {
    528      // Need to check that decl->mPrefix is not declared anywhere closer to
    529      // us.  If it is, we can't use it.
    530      bool prefixOK = true;
    531      int32_t index2;
    532      for (index2 = count - 1; index2 > index && prefixOK; --index2) {
    533        prefixOK = (mNameSpaceStack[index2].mPrefix != decl.mPrefix);
    534      }
    535 
    536      if (prefixOK) {
    537        uriMatch = true;
    538        closestURIMatch.Assign(decl.mPrefix);
    539      }
    540    }
    541 
    542    --index;
    543  }
    544 
    545  // At this point the following invariants hold:
    546  // 1) The prefix in closestURIMatch is mapped to aURI in our scope if
    547  //    uriMatch is set.
    548  // 2) There is nothing on the namespace stack that has aPrefix as the prefix
    549  //    and a _different_ URI, except for the case aPrefix.IsEmpty (and
    550  //    possible default namespaces on ancestors)
    551 
    552  // So if uriMatch is set it's OK to use the closestURIMatch prefix.  The one
    553  // exception is when closestURIMatch is actually empty (default namespace
    554  // decl) and we must have a prefix.
    555  if (uriMatch && (!mustHavePrefix || !closestURIMatch.IsEmpty())) {
    556    aPrefix.Assign(closestURIMatch);
    557    return false;
    558  }
    559 
    560  if (aPrefix.IsEmpty()) {
    561    // At this point, aPrefix is empty (which means we never had a prefix to
    562    // start with).  If we must have a prefix, just generate a new prefix and
    563    // then send it back through the namespace stack checks to make sure it's
    564    // OK.
    565    if (mustHavePrefix) {
    566      GenerateNewPrefix(aPrefix);
    567      return ConfirmPrefix(aPrefix, aURI, aElement, aIsAttribute);
    568    }
    569 
    570    // One final special case.  If aPrefix is empty and we never saw an empty
    571    // prefix (default namespace decl) on the namespace stack and we're in the
    572    // null namespace there is no reason to output an |xmlns=""| here.  It just
    573    // makes the output less readable.
    574    if (!haveSeenOurPrefix && aURI.IsEmpty()) {
    575      return false;
    576    }
    577  }
    578 
    579  // Now just set aURI as the new default namespace URI.  Indicate that we need
    580  // to create a namespace decl for the final prefix
    581  return true;
    582 }
    583 
    584 void nsXMLContentSerializer::GenerateNewPrefix(nsAString& aPrefix) {
    585  aPrefix.Assign('a');
    586  aPrefix.AppendInt(mPrefixIndex++);
    587 }
    588 
    589 bool nsXMLContentSerializer::SerializeAttr(const nsAString& aPrefix,
    590                                           const nsAString& aName,
    591                                           const nsAString& aValue,
    592                                           nsAString& aStr,
    593                                           bool aDoEscapeEntities) {
    594  // Because this method can short-circuit AppendToString for raw output, we
    595  // need to make sure that we're not inappropriately serializing attributes
    596  // from outside the body
    597  if (mBodyOnly && !mInBody) {
    598    return true;
    599  }
    600 
    601  nsAutoString attrString_;
    602  // For innerHTML we can do faster appending without
    603  // temporary strings.
    604  bool rawAppend = mDoRaw && aDoEscapeEntities;
    605  nsAString& attrString = (rawAppend) ? aStr : attrString_;
    606 
    607  NS_ENSURE_TRUE(attrString.Append(char16_t(' '), mozilla::fallible), false);
    608  if (!aPrefix.IsEmpty()) {
    609    NS_ENSURE_TRUE(attrString.Append(aPrefix, mozilla::fallible), false);
    610    NS_ENSURE_TRUE(attrString.Append(char16_t(':'), mozilla::fallible), false);
    611  }
    612  NS_ENSURE_TRUE(attrString.Append(aName, mozilla::fallible), false);
    613 
    614  if (aDoEscapeEntities) {
    615    // if problem characters are turned into character entity references
    616    // then there will be no problem with the value delimiter characters
    617    NS_ENSURE_TRUE(attrString.AppendLiteral("=\"", mozilla::fallible), false);
    618 
    619    mInAttribute = true;
    620    bool result = AppendAndTranslateEntities(aValue, attrString);
    621    mInAttribute = false;
    622    NS_ENSURE_TRUE(result, false);
    623 
    624    NS_ENSURE_TRUE(attrString.Append(char16_t('"'), mozilla::fallible), false);
    625    if (rawAppend) {
    626      return true;
    627    }
    628  } else {
    629    // Depending on whether the attribute value contains quotes or apostrophes
    630    // we need to select the delimiter character and escape characters using
    631    // character entity references, ignoring the value of aDoEscapeEntities.
    632    // See http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2.2 for
    633    // the standard on character entity references in values.  We also have to
    634    // make sure to escape any '&' characters.
    635 
    636    bool bIncludesSingle = false;
    637    bool bIncludesDouble = false;
    638    nsAString::const_iterator iCurr, iEnd;
    639    aValue.BeginReading(iCurr);
    640    aValue.EndReading(iEnd);
    641    for (; iCurr != iEnd; ++iCurr) {
    642      if (*iCurr == char16_t('\'')) {
    643        bIncludesSingle = true;
    644        if (bIncludesDouble) {
    645          break;
    646        }
    647      } else if (*iCurr == char16_t('"')) {
    648        bIncludesDouble = true;
    649        if (bIncludesSingle) {
    650          break;
    651        }
    652      }
    653    }
    654 
    655    // Delimiter and escaping is according to the following table
    656    //    bIncludesDouble   bIncludesSingle   Delimiter    Escape Double Quote
    657    //    FALSE             FALSE             "            FALSE
    658    //    FALSE             TRUE              "            FALSE
    659    //    TRUE              FALSE             '            FALSE
    660    //    TRUE              TRUE              "            TRUE
    661    char16_t cDelimiter =
    662        (bIncludesDouble && !bIncludesSingle) ? char16_t('\'') : char16_t('"');
    663    NS_ENSURE_TRUE(attrString.Append(char16_t('='), mozilla::fallible), false);
    664    NS_ENSURE_TRUE(attrString.Append(cDelimiter, mozilla::fallible), false);
    665    nsAutoString sValue(aValue);
    666    NS_ENSURE_TRUE(
    667        sValue.ReplaceSubstring(u"&"_ns, u"&amp;"_ns, mozilla::fallible),
    668        false);
    669    if (bIncludesDouble && bIncludesSingle) {
    670      NS_ENSURE_TRUE(
    671          sValue.ReplaceSubstring(u"\""_ns, u"&quot;"_ns, mozilla::fallible),
    672          false);
    673    }
    674    NS_ENSURE_TRUE(attrString.Append(sValue, mozilla::fallible), false);
    675    NS_ENSURE_TRUE(attrString.Append(cDelimiter, mozilla::fallible), false);
    676  }
    677 
    678  if (mDoWrap && mColPos + attrString.Length() > mMaxColumn) {
    679    // Attr would cause us to overrun the max width, so begin a new line.
    680    NS_ENSURE_TRUE(AppendNewLineToString(aStr), false);
    681 
    682    // Chomp the leading space.
    683    nsDependentSubstring chomped(attrString, 1);
    684    if (mDoFormat && mIndent.Length() + chomped.Length() <= mMaxColumn) {
    685      NS_ENSURE_TRUE(AppendIndentation(aStr), false);
    686    }
    687    NS_ENSURE_TRUE(AppendToStringConvertLF(chomped, aStr), false);
    688  } else {
    689    NS_ENSURE_TRUE(AppendToStringConvertLF(attrString, aStr), false);
    690  }
    691 
    692  return true;
    693 }
    694 
    695 uint32_t nsXMLContentSerializer::ScanNamespaceDeclarations(
    696    Element* aElement, Element* aOriginalElement,
    697    const nsAString& aTagNamespaceURI) {
    698  uint32_t index, count;
    699  nsAutoString uriStr, valueStr;
    700 
    701  count = aElement->GetAttrCount();
    702 
    703  // First scan for namespace declarations, pushing each on the stack
    704  uint32_t skipAttr = count;
    705  for (index = 0; index < count; index++) {
    706    const BorrowedAttrInfo info = aElement->GetAttrInfoAt(index);
    707    const nsAttrName* name = info.mName;
    708 
    709    int32_t namespaceID = name->NamespaceID();
    710    nsAtom* attrName = name->LocalName();
    711 
    712    if (namespaceID == kNameSpaceID_XMLNS ||
    713        // Also push on the stack attrs named "xmlns" in the null
    714        // namespace... because once we serialize those out they'll look like
    715        // namespace decls.  :(
    716        // XXXbz what if we have both "xmlns" in the null namespace and "xmlns"
    717        // in the xmlns namespace?
    718        (namespaceID == kNameSpaceID_None && attrName == nsGkAtoms::xmlns)) {
    719      info.mValue->ToString(uriStr);
    720 
    721      if (!name->GetPrefix()) {
    722        if (aTagNamespaceURI.IsEmpty() && !uriStr.IsEmpty()) {
    723          // If the element is in no namespace we need to add a xmlns
    724          // attribute to declare that. That xmlns attribute must not have a
    725          // prefix (see http://www.w3.org/TR/REC-xml-names/#dt-prefix), ie it
    726          // must declare the default namespace. We just found an xmlns
    727          // attribute that declares the default namespace to something
    728          // non-empty. We're going to ignore this attribute, for children we
    729          // will detect that we need to add it again and attributes aren't
    730          // affected by the default namespace.
    731          skipAttr = index;
    732        } else {
    733          // Default NS attribute does not have prefix (and the name is "xmlns")
    734          PushNameSpaceDecl(u""_ns, uriStr, aOriginalElement);
    735        }
    736      } else {
    737        PushNameSpaceDecl(nsDependentAtomString(attrName), uriStr,
    738                          aOriginalElement);
    739      }
    740    }
    741  }
    742  return skipAttr;
    743 }
    744 
    745 bool nsXMLContentSerializer::IsJavaScript(nsIContent* aContent,
    746                                          nsAtom* aAttrNameAtom,
    747                                          int32_t aAttrNamespaceID,
    748                                          const nsAString& aValueString) {
    749  bool isHtml = aContent->IsHTMLElement();
    750  bool isXul = aContent->IsXULElement();
    751  bool isSvg = aContent->IsSVGElement();
    752 
    753  if (aAttrNamespaceID == kNameSpaceID_None && (isHtml || isXul || isSvg) &&
    754      (aAttrNameAtom == nsGkAtoms::href || aAttrNameAtom == nsGkAtoms::src)) {
    755    static const char kJavaScript[] = "javascript";
    756    int32_t pos = aValueString.FindChar(':');
    757    if (pos < (int32_t)(sizeof kJavaScript - 1)) return false;
    758    nsAutoString scheme(Substring(aValueString, 0, pos));
    759    scheme.StripWhitespace();
    760    if ((scheme.Length() == (sizeof kJavaScript - 1)) &&
    761        scheme.EqualsIgnoreCase(kJavaScript))
    762      return true;
    763    else
    764      return false;
    765  }
    766 
    767  return aContent->IsEventAttributeName(aAttrNameAtom);
    768 }
    769 
    770 bool nsXMLContentSerializer::SerializeAttributes(
    771    Element* aElement, Element* aOriginalElement, nsAString& aTagPrefix,
    772    const nsAString& aTagNamespaceURI, nsAtom* aTagName, nsAString& aStr,
    773    uint32_t aSkipAttr, bool aAddNSAttr) {
    774  nsAutoString prefixStr, uriStr, valueStr;
    775  nsAutoString xmlnsStr;
    776  xmlnsStr.AssignLiteral(kXMLNS);
    777  uint32_t index, count;
    778 
    779  MaybeSerializeIsValue(aElement, aStr);
    780 
    781  // If we had to add a new namespace declaration, serialize
    782  // and push it on the namespace stack
    783  if (aAddNSAttr) {
    784    if (aTagPrefix.IsEmpty()) {
    785      // Serialize default namespace decl
    786      NS_ENSURE_TRUE(
    787          SerializeAttr(u""_ns, xmlnsStr, aTagNamespaceURI, aStr, true), false);
    788    } else {
    789      // Serialize namespace decl
    790      NS_ENSURE_TRUE(
    791          SerializeAttr(xmlnsStr, aTagPrefix, aTagNamespaceURI, aStr, true),
    792          false);
    793    }
    794    PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement);
    795  }
    796 
    797  count = aElement->GetAttrCount();
    798 
    799  // Now serialize each of the attributes
    800  // XXX Unfortunately we need a namespace manager to get
    801  // attribute URIs.
    802  for (index = 0; index < count; index++) {
    803    if (aSkipAttr == index) {
    804      continue;
    805    }
    806 
    807    const nsAttrName* name = aElement->GetAttrNameAt(index);
    808    int32_t namespaceID = name->NamespaceID();
    809    nsAtom* attrName = name->LocalName();
    810    nsAtom* attrPrefix = name->GetPrefix();
    811 
    812    // Filter out any attribute starting with [-|_]moz
    813    nsDependentAtomString attrNameStr(attrName);
    814    if (StringBeginsWith(attrNameStr, u"_moz"_ns) ||
    815        StringBeginsWith(attrNameStr, u"-moz"_ns)) {
    816      continue;
    817    }
    818 
    819    if (attrPrefix) {
    820      attrPrefix->ToString(prefixStr);
    821    } else {
    822      prefixStr.Truncate();
    823    }
    824 
    825    bool addNSAttr = false;
    826    if (kNameSpaceID_XMLNS != namespaceID) {
    827      nsNameSpaceManager::GetInstance()->GetNameSpaceURI(namespaceID, uriStr);
    828      addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true);
    829    }
    830 
    831    aElement->GetAttr(namespaceID, attrName, valueStr);
    832 
    833    nsDependentAtomString nameStr(attrName);
    834    bool isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr);
    835 
    836    NS_ENSURE_TRUE(SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS),
    837                   false);
    838 
    839    if (addNSAttr) {
    840      NS_ASSERTION(!prefixStr.IsEmpty(),
    841                   "Namespaced attributes must have a prefix");
    842      NS_ENSURE_TRUE(SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, true),
    843                     false);
    844      PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement);
    845    }
    846  }
    847 
    848  return true;
    849 }
    850 
    851 NS_IMETHODIMP
    852 nsXMLContentSerializer::AppendElementStart(Element* aElement,
    853                                           Element* aOriginalElement) {
    854  NS_ENSURE_ARG(aElement);
    855  NS_ENSURE_STATE(mOutput);
    856 
    857  bool forceFormat = false;
    858  nsresult rv = NS_OK;
    859  if (!CheckElementStart(aElement, forceFormat, *mOutput, rv)) {
    860    // When we go to AppendElementEnd for this element, we're going to
    861    // MaybeLeaveFromPreContent().  So make sure to MaybeEnterInPreContent()
    862    // now, so our PreLevel() doesn't get confused.
    863    MaybeEnterInPreContent(aElement);
    864    return rv;
    865  }
    866 
    867  NS_ENSURE_SUCCESS(rv, rv);
    868 
    869  nsAutoString tagPrefix, tagLocalName, tagNamespaceURI;
    870  aElement->NodeInfo()->GetPrefix(tagPrefix);
    871  aElement->NodeInfo()->GetName(tagLocalName);
    872  aElement->NodeInfo()->GetNamespaceURI(tagNamespaceURI);
    873 
    874  uint32_t skipAttr =
    875      ScanNamespaceDeclarations(aElement, aOriginalElement, tagNamespaceURI);
    876 
    877  nsAtom* name = aElement->NodeInfo()->NameAtom();
    878  bool lineBreakBeforeOpen =
    879      LineBreakBeforeOpen(aElement->GetNameSpaceID(), name);
    880 
    881  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
    882    if (mColPos && lineBreakBeforeOpen) {
    883      NS_ENSURE_TRUE(AppendNewLineToString(*mOutput), NS_ERROR_OUT_OF_MEMORY);
    884    } else {
    885      NS_ENSURE_TRUE(MaybeAddNewlineForRootNode(*mOutput),
    886                     NS_ERROR_OUT_OF_MEMORY);
    887    }
    888    if (!mColPos) {
    889      NS_ENSURE_TRUE(AppendIndentation(*mOutput), NS_ERROR_OUT_OF_MEMORY);
    890    } else if (mAddSpace) {
    891      NS_ENSURE_TRUE(AppendToString(char16_t(' '), *mOutput),
    892                     NS_ERROR_OUT_OF_MEMORY);
    893      mAddSpace = false;
    894    }
    895  } else if (mAddSpace) {
    896    NS_ENSURE_TRUE(AppendToString(char16_t(' '), *mOutput),
    897                   NS_ERROR_OUT_OF_MEMORY);
    898    mAddSpace = false;
    899  } else {
    900    NS_ENSURE_TRUE(MaybeAddNewlineForRootNode(*mOutput),
    901                   NS_ERROR_OUT_OF_MEMORY);
    902  }
    903 
    904  // Always reset to avoid false newlines in case MaybeAddNewlineForRootNode
    905  // wasn't called
    906  mAddNewlineForRootNode = false;
    907 
    908  bool addNSAttr;
    909  addNSAttr =
    910      ConfirmPrefix(tagPrefix, tagNamespaceURI, aOriginalElement, false);
    911 
    912  // Serialize the qualified name of the element
    913  NS_ENSURE_TRUE(AppendToString(kLessThan, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    914  if (!tagPrefix.IsEmpty()) {
    915    NS_ENSURE_TRUE(AppendToString(tagPrefix, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    916    NS_ENSURE_TRUE(AppendToString(u":"_ns, *mOutput), NS_ERROR_OUT_OF_MEMORY);
    917  }
    918  NS_ENSURE_TRUE(AppendToString(tagLocalName, *mOutput),
    919                 NS_ERROR_OUT_OF_MEMORY);
    920 
    921  MaybeEnterInPreContent(aElement);
    922 
    923  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
    924    NS_ENSURE_TRUE(IncrIndentation(name), NS_ERROR_OUT_OF_MEMORY);
    925  }
    926 
    927  NS_ENSURE_TRUE(
    928      SerializeAttributes(aElement, aOriginalElement, tagPrefix,
    929                          tagNamespaceURI, name, *mOutput, skipAttr, addNSAttr),
    930      NS_ERROR_OUT_OF_MEMORY);
    931 
    932  NS_ENSURE_TRUE(AppendEndOfElementStart(aElement, aOriginalElement, *mOutput),
    933                 NS_ERROR_OUT_OF_MEMORY);
    934 
    935  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel() &&
    936      LineBreakAfterOpen(aElement->GetNameSpaceID(), name)) {
    937    NS_ENSURE_TRUE(AppendNewLineToString(*mOutput), NS_ERROR_OUT_OF_MEMORY);
    938  }
    939 
    940  NS_ENSURE_TRUE(AfterElementStart(aElement, aOriginalElement, *mOutput),
    941                 NS_ERROR_OUT_OF_MEMORY);
    942 
    943  return NS_OK;
    944 }
    945 
    946 // aElement is the actual element we're outputting.  aOriginalElement is the one
    947 // in the original DOM, which is the one we have to test for kids.
    948 static bool ElementNeedsSeparateEndTag(Element* aElement,
    949                                       Element* aOriginalElement) {
    950  if (aOriginalElement->GetChildCount()) {
    951    // We have kids, so we need a separate end tag.  This needs to be checked on
    952    // aOriginalElement because that's the one that's actually in the DOM and
    953    // might have kids.
    954    return true;
    955  }
    956 
    957  if (!aElement->IsHTMLElement()) {
    958    // Empty non-HTML elements can just skip a separate end tag.
    959    return false;
    960  }
    961 
    962  // HTML container tags should have a separate end tag even if empty, per spec.
    963  // See
    964  // https://w3c.github.io/DOM-Parsing/#dfn-concept-xml-serialization-algorithm
    965  nsAtom* localName = aElement->NodeInfo()->NameAtom();
    966  bool isHTMLContainer = nsHTMLElement::IsContainer(
    967      nsHTMLTags::CaseSensitiveAtomTagToId(localName));
    968  return isHTMLContainer;
    969 }
    970 
    971 bool nsXMLContentSerializer::AppendEndOfElementStart(Element* aElement,
    972                                                     Element* aOriginalElement,
    973                                                     nsAString& aStr) {
    974  if (ElementNeedsSeparateEndTag(aElement, aOriginalElement)) {
    975    return AppendToString(kGreaterThan, aStr);
    976  }
    977 
    978  // We don't need a separate end tag.  For HTML elements (which at this point
    979  // must be non-containers), append a space before the '/', per spec.  See
    980  // https://w3c.github.io/DOM-Parsing/#dfn-concept-xml-serialization-algorithm
    981  if (aOriginalElement->IsHTMLElement()) {
    982    if (!AppendToString(kSpace, aStr)) {
    983      return false;
    984    }
    985  }
    986 
    987  return AppendToString(u"/>"_ns, aStr);
    988 }
    989 
    990 NS_IMETHODIMP
    991 nsXMLContentSerializer::AppendElementEnd(Element* aElement,
    992                                         Element* aOriginalElement) {
    993  NS_ENSURE_ARG(aElement);
    994  NS_ENSURE_STATE(mOutput);
    995 
    996  nsIContent* content = aElement;
    997 
    998  bool forceFormat = false, outputElementEnd;
    999  outputElementEnd =
   1000      CheckElementEnd(aElement, aOriginalElement, forceFormat, *mOutput);
   1001 
   1002  nsAtom* name = content->NodeInfo()->NameAtom();
   1003 
   1004  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
   1005    DecrIndentation(name);
   1006  }
   1007 
   1008  if (!outputElementEnd) {
   1009    // Keep this in sync with the cleanup at the end of this method.
   1010    PopNameSpaceDeclsFor(aElement);
   1011    MaybeLeaveFromPreContent(content);
   1012    MaybeFlagNewlineForRootNode(aElement);
   1013    AfterElementEnd(content, *mOutput);
   1014    return NS_OK;
   1015  }
   1016 
   1017  nsAutoString tagPrefix, tagLocalName, tagNamespaceURI;
   1018 
   1019  aElement->NodeInfo()->GetPrefix(tagPrefix);
   1020  aElement->NodeInfo()->GetName(tagLocalName);
   1021  aElement->NodeInfo()->GetNamespaceURI(tagNamespaceURI);
   1022 
   1023 #ifdef DEBUG
   1024  bool debugNeedToPushNamespace =
   1025 #endif
   1026      ConfirmPrefix(tagPrefix, tagNamespaceURI, aElement, false);
   1027  NS_ASSERTION(!debugNeedToPushNamespace,
   1028               "Can't push namespaces in closing tag!");
   1029 
   1030  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) {
   1031    bool lineBreakBeforeClose =
   1032        LineBreakBeforeClose(content->GetNameSpaceID(), name);
   1033 
   1034    if (mColPos && lineBreakBeforeClose) {
   1035      NS_ENSURE_TRUE(AppendNewLineToString(*mOutput), NS_ERROR_OUT_OF_MEMORY);
   1036    }
   1037    if (!mColPos) {
   1038      NS_ENSURE_TRUE(AppendIndentation(*mOutput), NS_ERROR_OUT_OF_MEMORY);
   1039    } else if (mAddSpace) {
   1040      NS_ENSURE_TRUE(AppendToString(char16_t(' '), *mOutput),
   1041                     NS_ERROR_OUT_OF_MEMORY);
   1042      mAddSpace = false;
   1043    }
   1044  } else if (mAddSpace) {
   1045    NS_ENSURE_TRUE(AppendToString(char16_t(' '), *mOutput),
   1046                   NS_ERROR_OUT_OF_MEMORY);
   1047    mAddSpace = false;
   1048  }
   1049 
   1050  NS_ENSURE_TRUE(AppendToString(kEndTag, *mOutput), NS_ERROR_OUT_OF_MEMORY);
   1051  if (!tagPrefix.IsEmpty()) {
   1052    NS_ENSURE_TRUE(AppendToString(tagPrefix, *mOutput), NS_ERROR_OUT_OF_MEMORY);
   1053    NS_ENSURE_TRUE(AppendToString(u":"_ns, *mOutput), NS_ERROR_OUT_OF_MEMORY);
   1054  }
   1055  NS_ENSURE_TRUE(AppendToString(tagLocalName, *mOutput),
   1056                 NS_ERROR_OUT_OF_MEMORY);
   1057  NS_ENSURE_TRUE(AppendToString(kGreaterThan, *mOutput),
   1058                 NS_ERROR_OUT_OF_MEMORY);
   1059 
   1060  // Keep what follows in sync with the cleanup in the !outputElementEnd case.
   1061  PopNameSpaceDeclsFor(aElement);
   1062 
   1063  MaybeLeaveFromPreContent(content);
   1064 
   1065  if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel() &&
   1066      LineBreakAfterClose(content->GetNameSpaceID(), name)) {
   1067    NS_ENSURE_TRUE(AppendNewLineToString(*mOutput), NS_ERROR_OUT_OF_MEMORY);
   1068  } else {
   1069    MaybeFlagNewlineForRootNode(aElement);
   1070  }
   1071 
   1072  AfterElementEnd(content, *mOutput);
   1073 
   1074  return NS_OK;
   1075 }
   1076 
   1077 NS_IMETHODIMP
   1078 nsXMLContentSerializer::Finish() {
   1079  NS_ENSURE_STATE(mOutput);
   1080 
   1081  mOutput = nullptr;
   1082 
   1083  return NS_OK;
   1084 }
   1085 
   1086 NS_IMETHODIMP
   1087 nsXMLContentSerializer::GetOutputLength(uint32_t& aLength) const {
   1088  NS_ENSURE_STATE(mOutput);
   1089 
   1090  aLength = mOutput->Length();
   1091 
   1092  return NS_OK;
   1093 }
   1094 
   1095 NS_IMETHODIMP
   1096 nsXMLContentSerializer::AppendDocumentStart(Document* aDocument) {
   1097  NS_ENSURE_ARG_POINTER(aDocument);
   1098  NS_ENSURE_STATE(mOutput);
   1099 
   1100  nsAutoString version, encoding, standalone;
   1101  aDocument->GetXMLDeclaration(version, encoding, standalone);
   1102 
   1103  if (version.IsEmpty())
   1104    return NS_OK;  // A declaration must have version, or there is no decl
   1105 
   1106  constexpr auto endQuote = u"\""_ns;
   1107 
   1108  *mOutput += u"<?xml version=\""_ns + version + endQuote;
   1109 
   1110  if (!mCharset.IsEmpty()) {
   1111    *mOutput +=
   1112        u" encoding=\""_ns + NS_ConvertASCIItoUTF16(mCharset) + endQuote;
   1113  }
   1114  // Otherwise just don't output an encoding attr.  Not that we expect
   1115  // mCharset to ever be empty.
   1116 #ifdef DEBUG
   1117  else {
   1118    NS_WARNING("Empty mCharset?  How come?");
   1119  }
   1120 #endif
   1121 
   1122  if (!standalone.IsEmpty()) {
   1123    *mOutput += u" standalone=\""_ns + standalone + endQuote;
   1124  }
   1125 
   1126  NS_ENSURE_TRUE(mOutput->AppendLiteral("?>", mozilla::fallible),
   1127                 NS_ERROR_OUT_OF_MEMORY);
   1128  mAddNewlineForRootNode = true;
   1129 
   1130  return NS_OK;
   1131 }
   1132 
   1133 bool nsXMLContentSerializer::CheckElementStart(Element*, bool& aForceFormat,
   1134                                               nsAString& aStr,
   1135                                               nsresult& aResult) {
   1136  aResult = NS_OK;
   1137  aForceFormat = false;
   1138  return true;
   1139 }
   1140 
   1141 bool nsXMLContentSerializer::CheckElementEnd(Element* aElement,
   1142                                             Element* aOriginalElement,
   1143                                             bool& aForceFormat,
   1144                                             nsAString& aStr) {
   1145  // We don't output a separate end tag for empty element
   1146  aForceFormat = false;
   1147  return ElementNeedsSeparateEndTag(aElement, aOriginalElement);
   1148 }
   1149 
   1150 bool nsXMLContentSerializer::AppendToString(const char16_t aChar,
   1151                                            nsAString& aOutputStr) {
   1152  if (mBodyOnly && !mInBody) {
   1153    return true;
   1154  }
   1155  mColPos += 1;
   1156  return aOutputStr.Append(aChar, mozilla::fallible);
   1157 }
   1158 
   1159 bool nsXMLContentSerializer::AppendToString(const nsAString& aStr,
   1160                                            nsAString& aOutputStr) {
   1161  if (mBodyOnly && !mInBody) {
   1162    return true;
   1163  }
   1164  mColPos += aStr.Length();
   1165  return aOutputStr.Append(aStr, mozilla::fallible);
   1166 }
   1167 
   1168 #define _ 0
   1169 
   1170 // This table indexes into kEntityStrings[].
   1171 const uint8_t nsXMLContentSerializer::kEntities[] = {
   1172    // clang-format off
   1173  _, _, _, _, _, _, _, _, _, _,
   1174  _, _, _, _, _, _, _, _, _, _,
   1175  _, _, _, _, _, _, _, _, _, _,
   1176  _, _, _, _, _, _, _, _, 2, _,
   1177  _, _, _, _, _, _, _, _, _, _,
   1178  _, _, _, _, _, _, _, _, _, _,
   1179  3, _, 4
   1180    // clang-format on
   1181 };
   1182 
   1183 // This table indexes into kEntityStrings[].
   1184 const uint8_t nsXMLContentSerializer::kAttrEntities[] = {
   1185    // clang-format off
   1186  _, _, _, _, _, _, _, _, _, 5,
   1187  6, _, _, 7, _, _, _, _, _, _,
   1188  _, _, _, _, _, _, _, _, _, _,
   1189  _, _, _, _, 1, _, _, _, 2, _,
   1190  _, _, _, _, _, _, _, _, _, _,
   1191  _, _, _, _, _, _, _, _, _, _,
   1192  3, _, 4
   1193    // clang-format on
   1194 };
   1195 
   1196 #undef _
   1197 
   1198 const char* const nsXMLContentSerializer::kEntityStrings[] = {
   1199    /* 0 */ nullptr,
   1200    /* 1 */ "&quot;",
   1201    /* 2 */ "&amp;",
   1202    /* 3 */ "&lt;",
   1203    /* 4 */ "&gt;",
   1204    /* 5 */ "&#9;",
   1205    /* 6 */ "&#xA;",
   1206    /* 7 */ "&#xD;",
   1207 };
   1208 
   1209 bool nsXMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr,
   1210                                                        nsAString& aOutputStr) {
   1211  if (mInAttribute) {
   1212    return AppendAndTranslateEntities<kGTVal>(aStr, aOutputStr, kAttrEntities,
   1213                                              kEntityStrings);
   1214  }
   1215 
   1216  return AppendAndTranslateEntities<kGTVal>(aStr, aOutputStr, kEntities,
   1217                                            kEntityStrings);
   1218 }
   1219 
   1220 /* static */
   1221 bool nsXMLContentSerializer::AppendAndTranslateEntities(
   1222    const nsAString& aStr, nsAString& aOutputStr, const uint8_t aEntityTable[],
   1223    uint16_t aMaxTableIndex, const char* const aStringTable[]) {
   1224  nsReadingIterator<char16_t> done_reading;
   1225  aStr.EndReading(done_reading);
   1226 
   1227  // for each chunk of |aString|...
   1228  uint32_t advanceLength = 0;
   1229  nsReadingIterator<char16_t> iter;
   1230 
   1231  for (aStr.BeginReading(iter); iter != done_reading;
   1232       iter.advance(int32_t(advanceLength))) {
   1233    uint32_t fragmentLength = done_reading - iter;
   1234    const char16_t* c = iter.get();
   1235    const char16_t* fragmentStart = c;
   1236    const char16_t* fragmentEnd = c + fragmentLength;
   1237    const char* entityText = nullptr;
   1238 
   1239    advanceLength = 0;
   1240    // for each character in this chunk, check if it
   1241    // needs to be replaced
   1242    for (; c < fragmentEnd; c++, advanceLength++) {
   1243      char16_t val = *c;
   1244      if ((val <= aMaxTableIndex) && aEntityTable[val]) {
   1245        entityText = aStringTable[aEntityTable[val]];
   1246        break;
   1247      }
   1248    }
   1249 
   1250    NS_ENSURE_TRUE(
   1251        aOutputStr.Append(fragmentStart, advanceLength, mozilla::fallible),
   1252        false);
   1253    if (entityText) {
   1254      NS_ENSURE_TRUE(AppendASCIItoUTF16(mozilla::MakeStringSpan(entityText),
   1255                                        aOutputStr, mozilla::fallible),
   1256                     false);
   1257      advanceLength++;
   1258    }
   1259  }
   1260 
   1261  return true;
   1262 }
   1263 
   1264 bool nsXMLContentSerializer::MaybeAddNewlineForRootNode(nsAString& aStr) {
   1265  if (mAddNewlineForRootNode) {
   1266    return AppendNewLineToString(aStr);
   1267  }
   1268 
   1269  return true;
   1270 }
   1271 
   1272 void nsXMLContentSerializer::MaybeFlagNewlineForRootNode(nsINode* aNode) {
   1273  nsINode* parent = aNode->GetParentNode();
   1274  if (parent) {
   1275    mAddNewlineForRootNode = parent->IsDocument();
   1276  }
   1277 }
   1278 
   1279 void nsXMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode) {
   1280  // support of the xml:space attribute
   1281  nsAutoString space;
   1282  if (ShouldMaintainPreLevel() && aNode->IsElement() &&
   1283      aNode->AsElement()->GetAttr(kNameSpaceID_XML, nsGkAtoms::space, space) &&
   1284      space.EqualsLiteral("preserve")) {
   1285    ++PreLevel();
   1286  }
   1287 }
   1288 
   1289 void nsXMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode) {
   1290  // support of the xml:space attribute
   1291  nsAutoString space;
   1292  if (ShouldMaintainPreLevel() && aNode->IsElement() &&
   1293      aNode->AsElement()->GetAttr(kNameSpaceID_XML, nsGkAtoms::space, space) &&
   1294      space.EqualsLiteral("preserve")) {
   1295    --PreLevel();
   1296  }
   1297 }
   1298 
   1299 bool nsXMLContentSerializer::AppendNewLineToString(nsAString& aStr) {
   1300  bool result = AppendToString(mLineBreak, aStr);
   1301  mMayIgnoreLineBreakSequence = true;
   1302  mColPos = 0;
   1303  mAddSpace = false;
   1304  mIsIndentationAddedOnCurrentLine = false;
   1305  return result;
   1306 }
   1307 
   1308 bool nsXMLContentSerializer::AppendIndentation(nsAString& aStr) {
   1309  mIsIndentationAddedOnCurrentLine = true;
   1310  bool result = AppendToString(mIndent, aStr);
   1311  mAddSpace = false;
   1312  mMayIgnoreLineBreakSequence = false;
   1313  return result;
   1314 }
   1315 
   1316 bool nsXMLContentSerializer::IncrIndentation(nsAtom* aName) {
   1317  // we want to keep the source readable
   1318  if (mDoWrap &&
   1319      mIndent.Length() >= uint32_t(mMaxColumn) - MIN_INDENTED_LINE_LENGTH) {
   1320    ++mIndentOverflow;
   1321  } else {
   1322    return mIndent.AppendLiteral(INDENT_STRING, mozilla::fallible);
   1323  }
   1324 
   1325  return true;
   1326 }
   1327 
   1328 void nsXMLContentSerializer::DecrIndentation(nsAtom* aName) {
   1329  if (mIndentOverflow)
   1330    --mIndentOverflow;
   1331  else
   1332    mIndent.Cut(0, INDENT_STRING_LENGTH);
   1333 }
   1334 
   1335 bool nsXMLContentSerializer::LineBreakBeforeOpen(int32_t aNamespaceID,
   1336                                                 nsAtom* aName) {
   1337  return mAddSpace;
   1338 }
   1339 
   1340 bool nsXMLContentSerializer::LineBreakAfterOpen(int32_t aNamespaceID,
   1341                                                nsAtom* aName) {
   1342  return false;
   1343 }
   1344 
   1345 bool nsXMLContentSerializer::LineBreakBeforeClose(int32_t aNamespaceID,
   1346                                                  nsAtom* aName) {
   1347  return mAddSpace;
   1348 }
   1349 
   1350 bool nsXMLContentSerializer::LineBreakAfterClose(int32_t aNamespaceID,
   1351                                                 nsAtom* aName) {
   1352  return false;
   1353 }
   1354 
   1355 bool nsXMLContentSerializer::AppendToStringConvertLF(const nsAString& aStr,
   1356                                                     nsAString& aOutputStr) {
   1357  if (mBodyOnly && !mInBody) {
   1358    return true;
   1359  }
   1360 
   1361  if (mDoRaw) {
   1362    NS_ENSURE_TRUE(AppendToString(aStr, aOutputStr), false);
   1363  } else {
   1364    // Convert line-endings to mLineBreak
   1365    uint32_t start = 0;
   1366    uint32_t theLen = aStr.Length();
   1367    while (start < theLen) {
   1368      int32_t eol = aStr.FindChar('\n', start);
   1369      if (eol == kNotFound) {
   1370        nsDependentSubstring dataSubstring(aStr, start, theLen - start);
   1371        NS_ENSURE_TRUE(AppendToString(dataSubstring, aOutputStr), false);
   1372        start = theLen;
   1373        // if there was a line break before this substring
   1374        // AppendNewLineToString was called, so we should reverse
   1375        // this flag
   1376        mMayIgnoreLineBreakSequence = false;
   1377      } else {
   1378        nsDependentSubstring dataSubstring(aStr, start, eol - start);
   1379        NS_ENSURE_TRUE(AppendToString(dataSubstring, aOutputStr), false);
   1380        NS_ENSURE_TRUE(AppendNewLineToString(aOutputStr), false);
   1381        start = eol + 1;
   1382      }
   1383    }
   1384  }
   1385 
   1386  return true;
   1387 }
   1388 
   1389 bool nsXMLContentSerializer::AppendFormatedWrapped_WhitespaceSequence(
   1390    nsAString::const_char_iterator& aPos,
   1391    const nsAString::const_char_iterator aEnd,
   1392    const nsAString::const_char_iterator aSequenceStart,
   1393    bool& aMayIgnoreStartOfLineWhitespaceSequence, nsAString& aOutputStr) {
   1394  // Handle the complete sequence of whitespace.
   1395  // Continue to iterate until we find the first non-whitespace char.
   1396  // Updates "aPos" to point to the first unhandled char.
   1397  // Also updates the aMayIgnoreStartOfLineWhitespaceSequence flag,
   1398  // as well as the other "global" state flags.
   1399 
   1400  bool sawBlankOrTab = false;
   1401  bool leaveLoop = false;
   1402 
   1403  do {
   1404    switch (*aPos) {
   1405      case ' ':
   1406      case '\t':
   1407        sawBlankOrTab = true;
   1408        [[fallthrough]];
   1409      case '\n':
   1410        ++aPos;
   1411        // do not increase mColPos,
   1412        // because we will reduce the whitespace to a single char
   1413        break;
   1414      default:
   1415        leaveLoop = true;
   1416        break;
   1417    }
   1418  } while (!leaveLoop && aPos < aEnd);
   1419 
   1420  if (mAddSpace) {
   1421    // if we had previously been asked to add space,
   1422    // our situation has not changed
   1423  } else if (!sawBlankOrTab && mMayIgnoreLineBreakSequence) {
   1424    // nothing to do in the case where line breaks have already been added
   1425    // before the call of AppendToStringWrapped
   1426    // and only if we found line break in the sequence
   1427    mMayIgnoreLineBreakSequence = false;
   1428  } else if (aMayIgnoreStartOfLineWhitespaceSequence) {
   1429    // nothing to do
   1430    aMayIgnoreStartOfLineWhitespaceSequence = false;
   1431  } else {
   1432    if (sawBlankOrTab) {
   1433      if (mDoWrap && mColPos + 1 >= mMaxColumn) {
   1434        // no much sense in delaying, we only have one slot left,
   1435        // let's write a break now
   1436        bool result = aOutputStr.Append(mLineBreak, mozilla::fallible);
   1437        mColPos = 0;
   1438        mIsIndentationAddedOnCurrentLine = false;
   1439        mMayIgnoreLineBreakSequence = true;
   1440        NS_ENSURE_TRUE(result, false);
   1441      } else {
   1442        // do not write out yet, we may write out either a space or a linebreak
   1443        // let's delay writing it out until we know more
   1444        mAddSpace = true;
   1445        ++mColPos;  // eat a slot of available space
   1446      }
   1447    } else {
   1448      // Asian text usually does not contain spaces, therefore we should not
   1449      // transform a linebreak into a space.
   1450      // Since we only saw linebreaks, but no spaces or tabs,
   1451      // let's write a linebreak now.
   1452      NS_ENSURE_TRUE(AppendNewLineToString(aOutputStr), false);
   1453    }
   1454  }
   1455 
   1456  return true;
   1457 }
   1458 
   1459 bool nsXMLContentSerializer::AppendWrapped_NonWhitespaceSequence(
   1460    nsAString::const_char_iterator& aPos,
   1461    const nsAString::const_char_iterator aEnd,
   1462    const nsAString::const_char_iterator aSequenceStart,
   1463    bool& aMayIgnoreStartOfLineWhitespaceSequence,
   1464    bool& aSequenceStartAfterAWhiteSpace, nsAString& aOutputStr) {
   1465  mMayIgnoreLineBreakSequence = false;
   1466  aMayIgnoreStartOfLineWhitespaceSequence = false;
   1467 
   1468  // Handle the complete sequence of non-whitespace in this block
   1469  // Iterate until we find the first whitespace char or an aEnd condition
   1470  // Updates "aPos" to point to the first unhandled char.
   1471  // Also updates the aMayIgnoreStartOfLineWhitespaceSequence flag,
   1472  // as well as the other "global" state flags.
   1473 
   1474  bool thisSequenceStartsAtBeginningOfLine = !mColPos;
   1475  bool onceAgainBecauseWeAddedBreakInFront = false;
   1476  bool foundWhitespaceInLoop;
   1477  uint32_t length, colPos;
   1478 
   1479  do {
   1480    if (mColPos) {
   1481      colPos = mColPos;
   1482    } else {
   1483      if (mDoFormat && !mDoRaw && !PreLevel() &&
   1484          !onceAgainBecauseWeAddedBreakInFront) {
   1485        colPos = mIndent.Length();
   1486      } else
   1487        colPos = 0;
   1488    }
   1489    foundWhitespaceInLoop = false;
   1490    length = 0;
   1491    // we iterate until the next whitespace character
   1492    // or until we reach the maximum of character per line
   1493    // or until the end of the string to add.
   1494    do {
   1495      if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') {
   1496        foundWhitespaceInLoop = true;
   1497        break;
   1498      }
   1499 
   1500      ++aPos;
   1501      ++length;
   1502    } while ((!mDoWrap || colPos + length < mMaxColumn) && aPos < aEnd);
   1503 
   1504    // in the case we don't reached the end of the string, but we reached the
   1505    // maxcolumn, we see if there is a whitespace after the maxcolumn if yes,
   1506    // then we can append directly the string instead of appending a new line
   1507    // etc.
   1508    if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') {
   1509      foundWhitespaceInLoop = true;
   1510    }
   1511 
   1512    if (aPos == aEnd || foundWhitespaceInLoop) {
   1513      // there is enough room for the complete block we found
   1514      if (mDoFormat && !mColPos) {
   1515        NS_ENSURE_TRUE(AppendIndentation(aOutputStr), false);
   1516      } else if (mAddSpace) {
   1517        bool result = aOutputStr.Append(char16_t(' '), mozilla::fallible);
   1518        mAddSpace = false;
   1519        NS_ENSURE_TRUE(result, false);
   1520      }
   1521 
   1522      mColPos += length;
   1523      NS_ENSURE_TRUE(aOutputStr.Append(aSequenceStart, aPos - aSequenceStart,
   1524                                       mozilla::fallible),
   1525                     false);
   1526 
   1527      // We have not yet reached the max column, we will continue to
   1528      // fill the current line in the next outer loop iteration
   1529      // (this one in AppendToStringWrapped)
   1530      // make sure we return in this outer loop
   1531      onceAgainBecauseWeAddedBreakInFront = false;
   1532    } else {  // we reach the max column
   1533      if (!thisSequenceStartsAtBeginningOfLine &&
   1534          (mAddSpace || (!mDoFormat && aSequenceStartAfterAWhiteSpace))) {
   1535        // when !mDoFormat, mAddSpace is not used, mAddSpace is always false
   1536        // so, in the case where mDoWrap && !mDoFormat, if we want to enter in
   1537        // this condition...
   1538 
   1539        // We can avoid to wrap. We try to add the whole block
   1540        // in an empty new line
   1541 
   1542        NS_ENSURE_TRUE(AppendNewLineToString(aOutputStr), false);
   1543        aPos = aSequenceStart;
   1544        thisSequenceStartsAtBeginningOfLine = true;
   1545        onceAgainBecauseWeAddedBreakInFront = true;
   1546      } else {
   1547        // we must wrap
   1548        onceAgainBecauseWeAddedBreakInFront = false;
   1549        Maybe<uint32_t> wrapPosition;
   1550 
   1551        if (mAllowLineBreaking) {
   1552          MOZ_ASSERT(aPos < aEnd,
   1553                     "We shouldn't be here if aPos reaches the end of text!");
   1554 
   1555          // Search forward from aSequenceStart until we find the largest
   1556          // wrap position less than or equal to aPos.
   1557          Maybe<uint32_t> nextWrapPosition;
   1558          Span<const char16_t> subSeq(aSequenceStart, aEnd);
   1559          intl::LineBreakIteratorUtf16 lineBreakIter(subSeq);
   1560          while (true) {
   1561            nextWrapPosition = lineBreakIter.Next();
   1562            MOZ_ASSERT(nextWrapPosition.isSome(),
   1563                       "We should've exited the loop when reaching the end of "
   1564                       "text in the previous iteration!");
   1565 
   1566            // Trim space at the tail. UAX#14 doesn't have break opportunity
   1567            // for ASCII space at the tail.
   1568            const Maybe<uint32_t> originalNextWrapPosition = nextWrapPosition;
   1569            while (*nextWrapPosition > 0 &&
   1570                   subSeq.at(*nextWrapPosition - 1) == 0x20) {
   1571              nextWrapPosition = Some(*nextWrapPosition - 1);
   1572            }
   1573            if (*nextWrapPosition == 0) {
   1574              // Restore the original nextWrapPosition.
   1575              nextWrapPosition = originalNextWrapPosition;
   1576            }
   1577 
   1578            if (aSequenceStart + *nextWrapPosition > aPos) {
   1579              break;
   1580            }
   1581            wrapPosition = nextWrapPosition;
   1582          }
   1583 
   1584          if (!wrapPosition) {
   1585            // The wrap position found in the first iteration of the above loop
   1586            // already exceeds aPos. We accept it as valid a wrap position only
   1587            // if it is not end-of-text. If the line-breaker returned
   1588            // end-of-text, we don't know that it is actually a good wrap
   1589            // position, so ignore it and continue to use the fallback code
   1590            // below.
   1591            if (*nextWrapPosition < subSeq.Length()) {
   1592              wrapPosition = nextWrapPosition;
   1593            }
   1594          }
   1595        }
   1596 
   1597        if (wrapPosition) {
   1598          if (!mColPos && mDoFormat) {
   1599            NS_ENSURE_TRUE(AppendIndentation(aOutputStr), false);
   1600          } else if (mAddSpace) {
   1601            bool result = aOutputStr.Append(char16_t(' '), mozilla::fallible);
   1602            mAddSpace = false;
   1603            NS_ENSURE_TRUE(result, false);
   1604          }
   1605          NS_ENSURE_TRUE(aOutputStr.Append(aSequenceStart, *wrapPosition,
   1606                                           mozilla::fallible),
   1607                         false);
   1608 
   1609          NS_ENSURE_TRUE(AppendNewLineToString(aOutputStr), false);
   1610          aPos = aSequenceStart + *wrapPosition;
   1611          aMayIgnoreStartOfLineWhitespaceSequence = true;
   1612        } else {
   1613          // try some simple fallback logic
   1614          // go forward up to the next whitespace position,
   1615          // in the worst case this will be all the rest of the data
   1616 
   1617          // XXX(jfkthame) Should we (conditionally) output indentation here?
   1618          // It makes for tidier-looking formatted output, at the cost of
   1619          // exceeding the target width by a greater amount on such lines.
   1620          // if (!mColPos && mDoFormat) {
   1621          //   NS_ENSURE_TRUE(AppendIndentation(aOutputStr), false);
   1622          //   mAddSpace = false;
   1623          // }
   1624 
   1625          // we update the mColPos variable with the length of
   1626          // the part already parsed.
   1627          mColPos += length;
   1628 
   1629          // now try to find the next whitespace
   1630          do {
   1631            if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') {
   1632              break;
   1633            }
   1634 
   1635            ++aPos;
   1636            ++mColPos;
   1637          } while (aPos < aEnd);
   1638 
   1639          if (mAddSpace) {
   1640            bool result = aOutputStr.Append(char16_t(' '), mozilla::fallible);
   1641            mAddSpace = false;
   1642            NS_ENSURE_TRUE(result, false);
   1643          }
   1644          NS_ENSURE_TRUE(
   1645              aOutputStr.Append(aSequenceStart, aPos - aSequenceStart,
   1646                                mozilla::fallible),
   1647              false);
   1648        }
   1649      }
   1650      aSequenceStartAfterAWhiteSpace = false;
   1651    }
   1652  } while (onceAgainBecauseWeAddedBreakInFront);
   1653 
   1654  return true;
   1655 }
   1656 
   1657 bool nsXMLContentSerializer::AppendToStringFormatedWrapped(
   1658    const nsAString& aStr, nsAString& aOutputStr) {
   1659  if (mBodyOnly && !mInBody) {
   1660    return true;
   1661  }
   1662 
   1663  nsAString::const_char_iterator pos, end, sequenceStart;
   1664 
   1665  aStr.BeginReading(pos);
   1666  aStr.EndReading(end);
   1667 
   1668  bool sequenceStartAfterAWhitespace = false;
   1669  if (pos < end) {
   1670    nsAString::const_char_iterator end2;
   1671    aOutputStr.EndReading(end2);
   1672    --end2;
   1673    if (*end2 == ' ' || *end2 == '\n' || *end2 == '\t') {
   1674      sequenceStartAfterAWhitespace = true;
   1675    }
   1676  }
   1677 
   1678  // if the current line already has text on it, such as a tag,
   1679  // leading whitespace is significant
   1680  bool mayIgnoreStartOfLineWhitespaceSequence =
   1681      (!mColPos ||
   1682       (mIsIndentationAddedOnCurrentLine && sequenceStartAfterAWhitespace &&
   1683        uint32_t(mColPos) == mIndent.Length()));
   1684 
   1685  while (pos < end) {
   1686    sequenceStart = pos;
   1687 
   1688    // if beginning of a whitespace sequence
   1689    if (*pos == ' ' || *pos == '\n' || *pos == '\t') {
   1690      NS_ENSURE_TRUE(AppendFormatedWrapped_WhitespaceSequence(
   1691                         pos, end, sequenceStart,
   1692                         mayIgnoreStartOfLineWhitespaceSequence, aOutputStr),
   1693                     false);
   1694    } else {  // any other non-whitespace char
   1695      NS_ENSURE_TRUE(
   1696          AppendWrapped_NonWhitespaceSequence(
   1697              pos, end, sequenceStart, mayIgnoreStartOfLineWhitespaceSequence,
   1698              sequenceStartAfterAWhitespace, aOutputStr),
   1699          false);
   1700    }
   1701  }
   1702 
   1703  return true;
   1704 }
   1705 
   1706 bool nsXMLContentSerializer::AppendWrapped_WhitespaceSequence(
   1707    nsAString::const_char_iterator& aPos,
   1708    const nsAString::const_char_iterator aEnd,
   1709    const nsAString::const_char_iterator aSequenceStart,
   1710    nsAString& aOutputStr) {
   1711  // Handle the complete sequence of whitespace.
   1712  // Continue to iterate until we find the first non-whitespace char.
   1713  // Updates "aPos" to point to the first unhandled char.
   1714  mAddSpace = false;
   1715  mIsIndentationAddedOnCurrentLine = false;
   1716 
   1717  bool leaveLoop = false;
   1718  nsAString::const_char_iterator lastPos = aPos;
   1719 
   1720  do {
   1721    switch (*aPos) {
   1722      case ' ':
   1723      case '\t':
   1724        // if there are too many spaces on a line, we wrap
   1725        if (mColPos >= mMaxColumn) {
   1726          if (lastPos != aPos) {
   1727            NS_ENSURE_TRUE(
   1728                aOutputStr.Append(lastPos, aPos - lastPos, mozilla::fallible),
   1729                false);
   1730          }
   1731          NS_ENSURE_TRUE(AppendToString(mLineBreak, aOutputStr), false);
   1732          mColPos = 0;
   1733          lastPos = aPos;
   1734        }
   1735 
   1736        ++mColPos;
   1737        ++aPos;
   1738        break;
   1739      case '\n':
   1740        if (lastPos != aPos) {
   1741          NS_ENSURE_TRUE(
   1742              aOutputStr.Append(lastPos, aPos - lastPos, mozilla::fallible),
   1743              false);
   1744        }
   1745        NS_ENSURE_TRUE(AppendToString(mLineBreak, aOutputStr), false);
   1746        mColPos = 0;
   1747        ++aPos;
   1748        lastPos = aPos;
   1749        break;
   1750      default:
   1751        leaveLoop = true;
   1752        break;
   1753    }
   1754  } while (!leaveLoop && aPos < aEnd);
   1755 
   1756  if (lastPos != aPos) {
   1757    NS_ENSURE_TRUE(
   1758        aOutputStr.Append(lastPos, aPos - lastPos, mozilla::fallible), false);
   1759  }
   1760 
   1761  return true;
   1762 }
   1763 
   1764 bool nsXMLContentSerializer::AppendToStringWrapped(const nsAString& aStr,
   1765                                                   nsAString& aOutputStr) {
   1766  if (mBodyOnly && !mInBody) {
   1767    return true;
   1768  }
   1769 
   1770  nsAString::const_char_iterator pos, end, sequenceStart;
   1771 
   1772  aStr.BeginReading(pos);
   1773  aStr.EndReading(end);
   1774 
   1775  // not used in this case, but needed by AppendWrapped_NonWhitespaceSequence
   1776  bool mayIgnoreStartOfLineWhitespaceSequence = false;
   1777  mMayIgnoreLineBreakSequence = false;
   1778 
   1779  bool sequenceStartAfterAWhitespace = false;
   1780  if (pos < end && !aOutputStr.IsEmpty()) {
   1781    nsAString::const_char_iterator end2;
   1782    aOutputStr.EndReading(end2);
   1783    --end2;
   1784    if (*end2 == ' ' || *end2 == '\n' || *end2 == '\t') {
   1785      sequenceStartAfterAWhitespace = true;
   1786    }
   1787  }
   1788 
   1789  while (pos < end) {
   1790    sequenceStart = pos;
   1791 
   1792    // if beginning of a whitespace sequence
   1793    if (*pos == ' ' || *pos == '\n' || *pos == '\t') {
   1794      sequenceStartAfterAWhitespace = true;
   1795      NS_ENSURE_TRUE(
   1796          AppendWrapped_WhitespaceSequence(pos, end, sequenceStart, aOutputStr),
   1797          false);
   1798    } else {  // any other non-whitespace char
   1799      NS_ENSURE_TRUE(
   1800          AppendWrapped_NonWhitespaceSequence(
   1801              pos, end, sequenceStart, mayIgnoreStartOfLineWhitespaceSequence,
   1802              sequenceStartAfterAWhitespace, aOutputStr),
   1803          false);
   1804    }
   1805  }
   1806 
   1807  return true;
   1808 }
   1809 
   1810 bool nsXMLContentSerializer::ShouldMaintainPreLevel() const {
   1811  // Only attempt to maintain the pre level for consumers who care about it.
   1812  return !mDoRaw || (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre);
   1813 }
   1814 
   1815 bool nsXMLContentSerializer::MaybeSerializeIsValue(Element* aElement,
   1816                                                   nsAString& aStr) {
   1817  CustomElementData* ceData = aElement->GetCustomElementData();
   1818  if (ceData) {
   1819    nsAtom* isAttr = ceData->GetIs(aElement);
   1820    if (isAttr && !aElement->HasAttr(nsGkAtoms::is)) {
   1821      NS_ENSURE_TRUE(aStr.AppendLiteral(" is=\"", mozilla::fallible), false);
   1822      NS_ENSURE_TRUE(
   1823          aStr.Append(nsDependentAtomString(isAttr), mozilla::fallible), false);
   1824      NS_ENSURE_TRUE(aStr.AppendLiteral("\"", mozilla::fallible), false);
   1825    }
   1826  }
   1827 
   1828  return true;
   1829 }