content/base/src/nsXMLContentSerializer.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /*
     7  * nsIContentSerializer implementation that can be used with an
     8  * nsIDocumentEncoder to convert an XML DOM to an XML string that
     9  * could be parsed into more or less the original DOM.
    10  */
    12 #include "nsXMLContentSerializer.h"
    14 #include "nsGkAtoms.h"
    15 #include "nsIDOMProcessingInstruction.h"
    16 #include "nsIDOMComment.h"
    17 #include "nsIDOMDocumentType.h"
    18 #include "nsIContent.h"
    19 #include "nsIDocument.h"
    20 #include "nsIDocumentEncoder.h"
    21 #include "nsNameSpaceManager.h"
    22 #include "nsTextFragment.h"
    23 #include "nsString.h"
    24 #include "prprf.h"
    25 #include "nsUnicharUtils.h"
    26 #include "nsCRT.h"
    27 #include "nsContentUtils.h"
    28 #include "nsAttrName.h"
    29 #include "nsILineBreaker.h"
    30 #include "mozilla/dom/Element.h"
    31 #include "nsParserConstants.h"
    33 using namespace mozilla::dom;
    35 #define kXMLNS "xmlns"
    37 // to be readable, we assume that an indented line contains
    38 // at least this number of characters (arbitrary value here).
    39 // This is a limit for the indentation.
    40 #define MIN_INDENTED_LINE_LENGTH 15
    42 // the string used to indent.
    43 #define INDENT_STRING "  "
    44 #define INDENT_STRING_LENGTH 2
    46 nsresult NS_NewXMLContentSerializer(nsIContentSerializer** aSerializer)
    47 {
    48   nsXMLContentSerializer* it = new nsXMLContentSerializer();
    49   if (!it) {
    50     return NS_ERROR_OUT_OF_MEMORY;
    51   }
    53   return CallQueryInterface(it, aSerializer);
    54 }
    56 nsXMLContentSerializer::nsXMLContentSerializer()
    57   : mPrefixIndex(0),
    58     mColPos(0),
    59     mIndentOverflow(0),
    60     mIsIndentationAddedOnCurrentLine(false),
    61     mInAttribute(false),
    62     mAddNewlineForRootNode(false),
    63     mAddSpace(false),
    64     mMayIgnoreLineBreakSequence(false),
    65     mBodyOnly(false),
    66     mInBody(0)
    67 {
    68 }
    70 nsXMLContentSerializer::~nsXMLContentSerializer()
    71 {
    72 }
    74 NS_IMPL_ISUPPORTS(nsXMLContentSerializer, nsIContentSerializer)
    76 NS_IMETHODIMP 
    77 nsXMLContentSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn,
    78                              const char* aCharSet, bool aIsCopying,
    79                              bool aRewriteEncodingDeclaration)
    80 {
    81   mPrefixIndex = 0;
    82   mColPos = 0;
    83   mIndentOverflow = 0;
    84   mIsIndentationAddedOnCurrentLine = false;
    85   mInAttribute = false;
    86   mAddNewlineForRootNode = false;
    87   mAddSpace = false;
    88   mMayIgnoreLineBreakSequence = false;
    89   mBodyOnly = false;
    90   mInBody = 0;
    92   mCharset = aCharSet;
    93   mFlags = aFlags;
    95   // Set the line break character:
    96   if ((mFlags & nsIDocumentEncoder::OutputCRLineBreak)
    97       && (mFlags & nsIDocumentEncoder::OutputLFLineBreak)) { // Windows
    98     mLineBreak.AssignLiteral("\r\n");
    99   }
   100   else if (mFlags & nsIDocumentEncoder::OutputCRLineBreak) { // Mac
   101     mLineBreak.AssignLiteral("\r");
   102   }
   103   else if (mFlags & nsIDocumentEncoder::OutputLFLineBreak) { // Unix/DOM
   104     mLineBreak.AssignLiteral("\n");
   105   }
   106   else {
   107     mLineBreak.AssignLiteral(NS_LINEBREAK);         // Platform/default
   108   }
   110   mDoRaw = !!(mFlags & nsIDocumentEncoder::OutputRaw);
   112   mDoFormat = (mFlags & nsIDocumentEncoder::OutputFormatted && !mDoRaw);
   114   mDoWrap = (mFlags & nsIDocumentEncoder::OutputWrap && !mDoRaw);
   116   if (!aWrapColumn) {
   117     mMaxColumn = 72;
   118   }
   119   else {
   120     mMaxColumn = aWrapColumn;
   121   }
   123   mPreLevel = 0;
   124   mIsIndentationAddedOnCurrentLine = false;
   125   return NS_OK;
   126 }
   128 nsresult
   129 nsXMLContentSerializer::AppendTextData(nsIContent* aNode,
   130                                        int32_t aStartOffset,
   131                                        int32_t aEndOffset,
   132                                        nsAString& aStr,
   133                                        bool aTranslateEntities)
   134 {
   135   nsIContent* content = aNode;
   136   const nsTextFragment* frag;
   137   if (!content || !(frag = content->GetText())) {
   138     return NS_ERROR_FAILURE;
   139   }
   141   int32_t fragLength = frag->GetLength();
   142   int32_t endoffset = (aEndOffset == -1) ? fragLength : std::min(aEndOffset, fragLength);
   143   int32_t length = endoffset - aStartOffset;
   145   NS_ASSERTION(aStartOffset >= 0, "Negative start offset for text fragment!");
   146   NS_ASSERTION(aStartOffset <= endoffset, "A start offset is beyond the end of the text fragment!");
   148   if (length <= 0) {
   149     // XXX Zero is a legal value, maybe non-zero values should be an
   150     // error.
   151     return NS_OK;
   152   }
   154   if (frag->Is2b()) {
   155     const char16_t *strStart = frag->Get2b() + aStartOffset;
   156     if (aTranslateEntities) {
   157       AppendAndTranslateEntities(Substring(strStart, strStart + length), aStr);
   158     }
   159     else {
   160       aStr.Append(Substring(strStart, strStart + length));
   161     }
   162   }
   163   else {
   164     if (aTranslateEntities) {
   165       AppendAndTranslateEntities(NS_ConvertASCIItoUTF16(frag->Get1b()+aStartOffset, length), aStr);
   166     }
   167     else {
   168       aStr.Append(NS_ConvertASCIItoUTF16(frag->Get1b()+aStartOffset, length));
   169     }
   170   }
   172   return NS_OK;
   173 }
   175 NS_IMETHODIMP 
   176 nsXMLContentSerializer::AppendText(nsIContent* aText,
   177                                    int32_t aStartOffset,
   178                                    int32_t aEndOffset,
   179                                    nsAString& aStr)
   180 {
   181   NS_ENSURE_ARG(aText);
   183   nsAutoString data;
   184   nsresult rv;
   186   rv = AppendTextData(aText, aStartOffset, aEndOffset, data, true);
   187   if (NS_FAILED(rv))
   188     return NS_ERROR_FAILURE;
   190   if (mPreLevel > 0 || mDoRaw) {
   191     AppendToStringConvertLF(data, aStr);
   192   }
   193   else if (mDoFormat) {
   194     AppendToStringFormatedWrapped(data, aStr);
   195   }
   196   else if (mDoWrap) {
   197     AppendToStringWrapped(data, aStr);
   198   }
   199   else {
   200     AppendToStringConvertLF(data, aStr);
   201   }
   203   return NS_OK;
   204 }
   206 NS_IMETHODIMP 
   207 nsXMLContentSerializer::AppendCDATASection(nsIContent* aCDATASection,
   208                                            int32_t aStartOffset,
   209                                            int32_t aEndOffset,
   210                                            nsAString& aStr)
   211 {
   212   NS_ENSURE_ARG(aCDATASection);
   213   nsresult rv;
   215   NS_NAMED_LITERAL_STRING(cdata , "<![CDATA[");
   217   if (mPreLevel > 0 || mDoRaw) {
   218     AppendToString(cdata, aStr);
   219   }
   220   else if (mDoFormat) {
   221     AppendToStringFormatedWrapped(cdata, aStr);
   222   }
   223   else if (mDoWrap) {
   224     AppendToStringWrapped(cdata, aStr);
   225   }
   226   else {
   227     AppendToString(cdata, aStr);
   228   }
   230   nsAutoString data;
   231   rv = AppendTextData(aCDATASection, aStartOffset, aEndOffset, data, false);
   232   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
   234   AppendToStringConvertLF(data, aStr);
   236   AppendToString(NS_LITERAL_STRING("]]>"), aStr);
   238   return NS_OK;
   239 }
   241 NS_IMETHODIMP 
   242 nsXMLContentSerializer::AppendProcessingInstruction(nsIContent* aPI,
   243                                                     int32_t aStartOffset,
   244                                                     int32_t aEndOffset,
   245                                                     nsAString& aStr)
   246 {
   247   nsCOMPtr<nsIDOMProcessingInstruction> pi = do_QueryInterface(aPI);
   248   NS_ENSURE_ARG(pi);
   249   nsresult rv;
   250   nsAutoString target, data, start;
   252   MaybeAddNewlineForRootNode(aStr);
   254   rv = pi->GetTarget(target);
   255   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
   257   rv = pi->GetData(data);
   258   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
   260   start.AppendLiteral("<?");
   261   start.Append(target);
   263   if (mPreLevel > 0 || mDoRaw) {
   264     AppendToString(start, aStr);
   265   }
   266   else if (mDoFormat) {
   267     if (mAddSpace) {
   268       AppendNewLineToString(aStr);
   269     }
   270     AppendToStringFormatedWrapped(start, aStr);
   271   }
   272   else if (mDoWrap) {
   273     AppendToStringWrapped(start, aStr);
   274   }
   275   else {
   276     AppendToString(start, aStr);
   277   }
   279   if (!data.IsEmpty()) {
   280     AppendToString(char16_t(' '), aStr);
   281     AppendToStringConvertLF(data, aStr);
   282   }
   283   AppendToString(NS_LITERAL_STRING("?>"), aStr);
   285   MaybeFlagNewlineForRootNode(aPI);
   287   return NS_OK;
   288 }
   290 NS_IMETHODIMP 
   291 nsXMLContentSerializer::AppendComment(nsIContent* aComment,
   292                                       int32_t aStartOffset,
   293                                       int32_t aEndOffset,
   294                                       nsAString& aStr)
   295 {
   296   nsCOMPtr<nsIDOMComment> comment = do_QueryInterface(aComment);
   297   NS_ENSURE_ARG(comment);
   298   nsresult rv;
   299   nsAutoString data;
   301   rv = comment->GetData(data);
   302   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
   304   int32_t dataLength = data.Length();
   305   if (aStartOffset || (aEndOffset != -1 && aEndOffset < dataLength)) {
   306     int32_t length =
   307       (aEndOffset == -1) ? dataLength : std::min(aEndOffset, dataLength);
   308     length -= aStartOffset;
   310     nsAutoString frag;
   311     if (length > 0) {
   312       data.Mid(frag, aStartOffset, length);
   313     }
   314     data.Assign(frag);
   315   }
   317   MaybeAddNewlineForRootNode(aStr);
   319   NS_NAMED_LITERAL_STRING(startComment, "<!--");
   321   if (mPreLevel > 0 || mDoRaw) {
   322     AppendToString(startComment, aStr);
   323   }
   324   else if (mDoFormat) {
   325     if (mAddSpace) {
   326       AppendNewLineToString(aStr);
   327     }
   328     AppendToStringFormatedWrapped(startComment, aStr);
   329   }
   330   else if (mDoWrap) {
   331     AppendToStringWrapped(startComment, aStr);
   332   }
   333   else {
   334     AppendToString(startComment, aStr);
   335   }
   337   // Even if mDoformat, we don't format the content because it
   338   // could have been preformated by the author
   339   AppendToStringConvertLF(data, aStr);
   340   AppendToString(NS_LITERAL_STRING("-->"), aStr);
   342   MaybeFlagNewlineForRootNode(aComment);
   344   return NS_OK;
   345 }
   347 NS_IMETHODIMP 
   348 nsXMLContentSerializer::AppendDoctype(nsIContent* aDocType,
   349                                       nsAString& aStr)
   350 {
   351   nsCOMPtr<nsIDOMDocumentType> docType = do_QueryInterface(aDocType);
   352   NS_ENSURE_ARG(docType);
   353   nsresult rv;
   354   nsAutoString name, publicId, systemId, internalSubset;
   356   rv = docType->GetName(name);
   357   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
   358   rv = docType->GetPublicId(publicId);
   359   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
   360   rv = docType->GetSystemId(systemId);
   361   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
   362   rv = docType->GetInternalSubset(internalSubset);
   363   if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
   365   MaybeAddNewlineForRootNode(aStr);
   367   AppendToString(NS_LITERAL_STRING("<!DOCTYPE "), aStr);
   368   AppendToString(name, aStr);
   370   char16_t quote;
   371   if (!publicId.IsEmpty()) {
   372     AppendToString(NS_LITERAL_STRING(" PUBLIC "), aStr);
   373     if (publicId.FindChar(char16_t('"')) == -1) {
   374       quote = char16_t('"');
   375     }
   376     else {
   377       quote = char16_t('\'');
   378     }
   379     AppendToString(quote, aStr);
   380     AppendToString(publicId, aStr);
   381     AppendToString(quote, aStr);
   383     if (!systemId.IsEmpty()) {
   384       AppendToString(char16_t(' '), aStr);
   385       if (systemId.FindChar(char16_t('"')) == -1) {
   386         quote = char16_t('"');
   387       }
   388       else {
   389         quote = char16_t('\'');
   390       }
   391       AppendToString(quote, aStr);
   392       AppendToString(systemId, aStr);
   393       AppendToString(quote, aStr);
   394     }
   395   }
   396   else if (!systemId.IsEmpty()) {
   397     if (systemId.FindChar(char16_t('"')) == -1) {
   398       quote = char16_t('"');
   399     }
   400     else {
   401       quote = char16_t('\'');
   402     }
   403     AppendToString(NS_LITERAL_STRING(" SYSTEM "), aStr);
   404     AppendToString(quote, aStr);
   405     AppendToString(systemId, aStr);
   406     AppendToString(quote, aStr);
   407   }
   409   if (!internalSubset.IsEmpty()) {
   410     AppendToString(NS_LITERAL_STRING(" ["), aStr);
   411     AppendToString(internalSubset, aStr);
   412     AppendToString(char16_t(']'), aStr);
   413   }
   415   AppendToString(kGreaterThan, aStr);
   416   MaybeFlagNewlineForRootNode(aDocType);
   418   return NS_OK;
   419 }
   421 nsresult
   422 nsXMLContentSerializer::PushNameSpaceDecl(const nsAString& aPrefix,
   423                                           const nsAString& aURI,
   424                                           nsIContent* aOwner)
   425 {
   426   NameSpaceDecl* decl = mNameSpaceStack.AppendElement();
   427   if (!decl) return NS_ERROR_OUT_OF_MEMORY;
   429   decl->mPrefix.Assign(aPrefix);
   430   decl->mURI.Assign(aURI);
   431   // Don't addref - this weak reference will be removed when
   432   // we pop the stack
   433   decl->mOwner = aOwner;
   434   return NS_OK;
   435 }
   437 void
   438 nsXMLContentSerializer::PopNameSpaceDeclsFor(nsIContent* aOwner)
   439 {
   440   int32_t index, count;
   442   count = mNameSpaceStack.Length();
   443   for (index = count - 1; index >= 0; index--) {
   444     if (mNameSpaceStack[index].mOwner != aOwner) {
   445       break;
   446     }
   447     mNameSpaceStack.RemoveElementAt(index);
   448   }
   449 }
   451 bool
   452 nsXMLContentSerializer::ConfirmPrefix(nsAString& aPrefix,
   453                                       const nsAString& aURI,
   454                                       nsIContent* aElement,
   455                                       bool aIsAttribute)
   456 {
   457   if (aPrefix.EqualsLiteral(kXMLNS)) {
   458     return false;
   459   }
   461   if (aURI.EqualsLiteral("http://www.w3.org/XML/1998/namespace")) {
   462     // The prefix must be xml for this namespace. We don't need to declare it,
   463     // so always just set the prefix to xml.
   464     aPrefix.AssignLiteral("xml");
   466     return false;
   467   }
   469   bool mustHavePrefix;
   470   if (aIsAttribute) {
   471     if (aURI.IsEmpty()) {
   472       // Attribute in the null namespace.  This just shouldn't have a prefix.
   473       // And there's no need to push any namespace decls
   474       aPrefix.Truncate();
   475       return false;
   476     }
   478     // Attribute not in the null namespace -- must have a prefix
   479     mustHavePrefix = true;
   480   } else {
   481     // Not an attribute, so doesn't _have_ to have a prefix
   482     mustHavePrefix = false;
   483   }
   485   // Keep track of the closest prefix that's bound to aURI and whether we've
   486   // found such a thing.  closestURIMatch holds the prefix, and uriMatch
   487   // indicates whether we actually have one.
   488   nsAutoString closestURIMatch;
   489   bool uriMatch = false;
   491   // Also keep track of whether we've seen aPrefix already.  If we have, that
   492   // means that it's already bound to a URI different from aURI, so even if we
   493   // later (so in a more outer scope) see it bound to aURI we can't reuse it.
   494   bool haveSeenOurPrefix = false;
   496   int32_t count = mNameSpaceStack.Length();
   497   int32_t index = count - 1;
   498   while (index >= 0) {
   499     NameSpaceDecl& decl = mNameSpaceStack.ElementAt(index);
   500     // Check if we've found a prefix match
   501     if (aPrefix.Equals(decl.mPrefix)) {
   503       // If the URIs match and aPrefix is not bound to any other URI, we can
   504       // use aPrefix
   505       if (!haveSeenOurPrefix && aURI.Equals(decl.mURI)) {
   506         // Just use our uriMatch stuff.  That will deal with an empty aPrefix
   507         // the right way.  We can break out of the loop now, though.
   508         uriMatch = true;
   509         closestURIMatch = aPrefix;
   510         break;
   511       }
   513       haveSeenOurPrefix = true;      
   515       // If they don't, and either:
   516       // 1) We have a prefix (so we'd be redeclaring this prefix to point to a
   517       //    different namespace) or
   518       // 2) We're looking at an existing default namespace decl on aElement (so
   519       //    we can't create a new default namespace decl for this URI)
   520       // then generate a new prefix.  Note that we do NOT generate new prefixes
   521       // if we happen to have aPrefix == decl->mPrefix == "" and mismatching
   522       // URIs when |decl| doesn't have aElement as its owner.  In that case we
   523       // can simply push the new namespace URI as the default namespace for
   524       // aElement.
   525       if (!aPrefix.IsEmpty() || decl.mOwner == aElement) {
   526         NS_ASSERTION(!aURI.IsEmpty(),
   527                      "Not allowed to add a xmlns attribute with an empty "
   528                      "namespace name unless it declares the default "
   529                      "namespace.");
   531         GenerateNewPrefix(aPrefix);
   532         // Now we need to validate our new prefix/uri combination; check it
   533         // against the full namespace stack again.  Note that just restarting
   534         // the while loop is ok, since we haven't changed aURI, so the
   535         // closestURIMatch and uriMatch state is not affected.
   536         index = count - 1;
   537         haveSeenOurPrefix = false;
   538         continue;
   539       }
   540     }
   542     // If we've found a URI match, then record the first one
   543     if (!uriMatch && aURI.Equals(decl.mURI)) {
   544       // Need to check that decl->mPrefix is not declared anywhere closer to
   545       // us.  If it is, we can't use it.
   546       bool prefixOK = true;
   547       int32_t index2;
   548       for (index2 = count-1; index2 > index && prefixOK; --index2) {
   549         prefixOK = (mNameSpaceStack[index2].mPrefix != decl.mPrefix);
   550       }
   552       if (prefixOK) {
   553         uriMatch = true;
   554         closestURIMatch.Assign(decl.mPrefix);
   555       }
   556     }
   558     --index;
   559   }
   561   // At this point the following invariants hold:
   562   // 1) The prefix in closestURIMatch is mapped to aURI in our scope if
   563   //    uriMatch is set.
   564   // 2) There is nothing on the namespace stack that has aPrefix as the prefix
   565   //    and a _different_ URI, except for the case aPrefix.IsEmpty (and
   566   //    possible default namespaces on ancestors)
   568   // So if uriMatch is set it's OK to use the closestURIMatch prefix.  The one
   569   // exception is when closestURIMatch is actually empty (default namespace
   570   // decl) and we must have a prefix.
   571   if (uriMatch && (!mustHavePrefix || !closestURIMatch.IsEmpty())) {
   572     aPrefix.Assign(closestURIMatch);
   573     return false;
   574   }
   576   if (aPrefix.IsEmpty()) {
   577     // At this point, aPrefix is empty (which means we never had a prefix to
   578     // start with).  If we must have a prefix, just generate a new prefix and
   579     // then send it back through the namespace stack checks to make sure it's
   580     // OK.
   581     if (mustHavePrefix) {
   582       GenerateNewPrefix(aPrefix);
   583       return ConfirmPrefix(aPrefix, aURI, aElement, aIsAttribute);
   584     }
   586     // One final special case.  If aPrefix is empty and we never saw an empty
   587     // prefix (default namespace decl) on the namespace stack and we're in the
   588     // null namespace there is no reason to output an |xmlns=""| here.  It just
   589     // makes the output less readable.
   590     if (!haveSeenOurPrefix && aURI.IsEmpty()) {
   591       return false;
   592     }
   593   }
   595   // Now just set aURI as the new default namespace URI.  Indicate that we need
   596   // to create a namespace decl for the final prefix
   597   return true;
   598 }
   600 void
   601 nsXMLContentSerializer::GenerateNewPrefix(nsAString& aPrefix)
   602 {
   603   aPrefix.AssignLiteral("a");
   604   char buf[128];
   605   PR_snprintf(buf, sizeof(buf), "%d", mPrefixIndex++);
   606   AppendASCIItoUTF16(buf, aPrefix);
   607 }
   609 void
   610 nsXMLContentSerializer::SerializeAttr(const nsAString& aPrefix,
   611                                       const nsAString& aName,
   612                                       const nsAString& aValue,
   613                                       nsAString& aStr,
   614                                       bool aDoEscapeEntities)
   615 {
   616   nsAutoString attrString_;
   617   // For innerHTML we can do faster appending without
   618   // temporary strings.
   619   bool rawAppend = mDoRaw && aDoEscapeEntities;
   620   nsAString& attrString = (rawAppend) ? aStr : attrString_;
   622   attrString.Append(char16_t(' '));
   623   if (!aPrefix.IsEmpty()) {
   624     attrString.Append(aPrefix);
   625     attrString.Append(char16_t(':'));
   626   }
   627   attrString.Append(aName);
   629   if (aDoEscapeEntities) {
   630     // if problem characters are turned into character entity references
   631     // then there will be no problem with the value delimiter characters
   632     attrString.AppendLiteral("=\"");
   634     mInAttribute = true;
   635     AppendAndTranslateEntities(aValue, attrString);
   636     mInAttribute = false;
   638     attrString.Append(char16_t('"'));
   639     if (rawAppend) {
   640       return;
   641     }
   642   }
   643   else {
   644     // Depending on whether the attribute value contains quotes or apostrophes we
   645     // need to select the delimiter character and escape characters using
   646     // character entity references, ignoring the value of aDoEscapeEntities.
   647     // See http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2.2 for
   648     // the standard on character entity references in values.  We also have to
   649     // make sure to escape any '&' characters.
   651     bool bIncludesSingle = false;
   652     bool bIncludesDouble = false;
   653     nsAString::const_iterator iCurr, iEnd;
   654     uint32_t uiSize, i;
   655     aValue.BeginReading(iCurr);
   656     aValue.EndReading(iEnd);
   657     for ( ; iCurr != iEnd; iCurr.advance(uiSize) ) {
   658       const char16_t * buf = iCurr.get();
   659       uiSize = iCurr.size_forward();
   660       for ( i = 0; i < uiSize; i++, buf++ ) {
   661         if ( *buf == char16_t('\'') )
   662         {
   663           bIncludesSingle = true;
   664           if ( bIncludesDouble ) break;
   665         }
   666         else if ( *buf == char16_t('"') )
   667         {
   668           bIncludesDouble = true;
   669           if ( bIncludesSingle ) break;
   670         }
   671       }
   672       // if both have been found we don't need to search further
   673       if ( bIncludesDouble && bIncludesSingle ) break;
   674     }
   676     // Delimiter and escaping is according to the following table
   677     //    bIncludesDouble     bIncludesSingle     Delimiter       Escape Double Quote
   678     //    FALSE               FALSE               "               FALSE
   679     //    FALSE               TRUE                "               FALSE
   680     //    TRUE                FALSE               '               FALSE
   681     //    TRUE                TRUE                "               TRUE
   682     char16_t cDelimiter = 
   683         (bIncludesDouble && !bIncludesSingle) ? char16_t('\'') : char16_t('"');
   684     attrString.Append(char16_t('='));
   685     attrString.Append(cDelimiter);
   686     nsAutoString sValue(aValue);
   687     sValue.ReplaceSubstring(NS_LITERAL_STRING("&"),
   688                             NS_LITERAL_STRING("&amp;"));
   689     if (bIncludesDouble && bIncludesSingle) {
   690       sValue.ReplaceSubstring(NS_LITERAL_STRING("\""),
   691                               NS_LITERAL_STRING("&quot;"));
   692     }
   693     attrString.Append(sValue);
   694     attrString.Append(cDelimiter);
   695   }
   696   if (mPreLevel > 0 || mDoRaw) {
   697     AppendToStringConvertLF(attrString, aStr);
   698   }
   699   else if (mDoFormat) {
   700     AppendToStringFormatedWrapped(attrString, aStr);
   701   }
   702   else if (mDoWrap) {
   703     AppendToStringWrapped(attrString, aStr);
   704   }
   705   else {
   706     AppendToStringConvertLF(attrString, aStr);
   707   }
   708 }
   710 uint32_t 
   711 nsXMLContentSerializer::ScanNamespaceDeclarations(nsIContent* aContent,
   712                                                   nsIContent *aOriginalElement,
   713                                                   const nsAString& aTagNamespaceURI)
   714 {
   715   uint32_t index, count;
   716   nsAutoString uriStr, valueStr;
   718   count = aContent->GetAttrCount();
   720   // First scan for namespace declarations, pushing each on the stack
   721   uint32_t skipAttr = count;
   722   for (index = 0; index < count; index++) {
   724     const nsAttrName* name = aContent->GetAttrNameAt(index);
   725     int32_t namespaceID = name->NamespaceID();
   726     nsIAtom *attrName = name->LocalName();
   728     if (namespaceID == kNameSpaceID_XMLNS ||
   729         // Also push on the stack attrs named "xmlns" in the null
   730         // namespace... because once we serialize those out they'll look like
   731         // namespace decls.  :(
   732         // XXXbz what if we have both "xmlns" in the null namespace and "xmlns"
   733         // in the xmlns namespace?
   734         (namespaceID == kNameSpaceID_None &&
   735          attrName == nsGkAtoms::xmlns)) {
   736       aContent->GetAttr(namespaceID, attrName, uriStr);
   738       if (!name->GetPrefix()) {
   739         if (aTagNamespaceURI.IsEmpty() && !uriStr.IsEmpty()) {
   740           // If the element is in no namespace we need to add a xmlns
   741           // attribute to declare that. That xmlns attribute must not have a
   742           // prefix (see http://www.w3.org/TR/REC-xml-names/#dt-prefix), ie it
   743           // must declare the default namespace. We just found an xmlns
   744           // attribute that declares the default namespace to something
   745           // non-empty. We're going to ignore this attribute, for children we
   746           // will detect that we need to add it again and attributes aren't
   747           // affected by the default namespace.
   748           skipAttr = index;
   749         }
   750         else {
   751           // Default NS attribute does not have prefix (and the name is "xmlns")
   752           PushNameSpaceDecl(EmptyString(), uriStr, aOriginalElement);
   753         }
   754       }
   755       else {
   756         PushNameSpaceDecl(nsDependentAtomString(attrName), uriStr,
   757                           aOriginalElement);
   758       }
   759     }
   760   }
   761   return skipAttr;
   762 }
   765 bool
   766 nsXMLContentSerializer::IsJavaScript(nsIContent * aContent, nsIAtom* aAttrNameAtom,
   767                                      int32_t aAttrNamespaceID, const nsAString& aValueString)
   768 {
   769   bool isHtml = aContent->IsHTML();
   770   bool isXul = aContent->IsXUL();
   771   bool isSvg = aContent->IsSVG();
   773   if (aAttrNamespaceID == kNameSpaceID_None &&
   774       (isHtml || isXul || isSvg) &&
   775       (aAttrNameAtom == nsGkAtoms::href ||
   776        aAttrNameAtom == nsGkAtoms::src)) {
   778     static const char kJavaScript[] = "javascript";
   779     int32_t pos = aValueString.FindChar(':');
   780     if (pos < (int32_t)(sizeof kJavaScript - 1))
   781         return false;
   782     nsAutoString scheme(Substring(aValueString, 0, pos));
   783     scheme.StripWhitespace();
   784     if ((scheme.Length() == (sizeof kJavaScript - 1)) &&
   785         scheme.EqualsIgnoreCase(kJavaScript))
   786       return true;
   787     else
   788       return false;
   789   }
   791   return aContent->IsEventAttributeName(aAttrNameAtom);
   792 }
   795 void 
   796 nsXMLContentSerializer::SerializeAttributes(nsIContent* aContent,
   797                                             nsIContent *aOriginalElement,
   798                                             nsAString& aTagPrefix,
   799                                             const nsAString& aTagNamespaceURI,
   800                                             nsIAtom* aTagName,
   801                                             nsAString& aStr,
   802                                             uint32_t aSkipAttr,
   803                                             bool aAddNSAttr)
   804 {
   806   nsAutoString prefixStr, uriStr, valueStr;
   807   nsAutoString xmlnsStr;
   808   xmlnsStr.AssignLiteral(kXMLNS);
   809   uint32_t index, count;
   811   // If we had to add a new namespace declaration, serialize
   812   // and push it on the namespace stack
   813   if (aAddNSAttr) {
   814     if (aTagPrefix.IsEmpty()) {
   815       // Serialize default namespace decl
   816       SerializeAttr(EmptyString(), xmlnsStr, aTagNamespaceURI, aStr, true);
   817     }
   818     else {
   819       // Serialize namespace decl
   820       SerializeAttr(xmlnsStr, aTagPrefix, aTagNamespaceURI, aStr, true);
   821     }
   822     PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement);
   823   }
   825   count = aContent->GetAttrCount();
   827   // Now serialize each of the attributes
   828   // XXX Unfortunately we need a namespace manager to get
   829   // attribute URIs.
   830   for (index = 0; index < count; index++) {
   831     if (aSkipAttr == index) {
   832         continue;
   833     }
   835     const nsAttrName* name = aContent->GetAttrNameAt(index);
   836     int32_t namespaceID = name->NamespaceID();
   837     nsIAtom* attrName = name->LocalName();
   838     nsIAtom* attrPrefix = name->GetPrefix();
   840     // Filter out any attribute starting with [-|_]moz
   841     nsDependentAtomString attrNameStr(attrName);
   842     if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
   843         StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
   844       continue;
   845     }
   847     if (attrPrefix) {
   848       attrPrefix->ToString(prefixStr);
   849     }
   850     else {
   851       prefixStr.Truncate();
   852     }
   854     bool addNSAttr = false;
   855     if (kNameSpaceID_XMLNS != namespaceID) {
   856       nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr);
   857       addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true);
   858     }
   860     aContent->GetAttr(namespaceID, attrName, valueStr);
   862     nsDependentAtomString nameStr(attrName);
   863     bool isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
   865     SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS);
   867     if (addNSAttr) {
   868       NS_ASSERTION(!prefixStr.IsEmpty(),
   869                    "Namespaced attributes must have a prefix");
   870       SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, true);
   871       PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement);
   872     }
   873   }
   874 }
   876 NS_IMETHODIMP 
   877 nsXMLContentSerializer::AppendElementStart(Element* aElement,
   878                                            Element* aOriginalElement,
   879                                            nsAString& aStr)
   880 {
   881   NS_ENSURE_ARG(aElement);
   883   nsIContent* content = aElement;
   885   bool forceFormat = false;
   886   if (!CheckElementStart(content, forceFormat, aStr)) {
   887     return NS_OK;
   888   }
   890   nsAutoString tagPrefix, tagLocalName, tagNamespaceURI;
   891   aElement->NodeInfo()->GetPrefix(tagPrefix);
   892   aElement->NodeInfo()->GetName(tagLocalName);
   893   aElement->NodeInfo()->GetNamespaceURI(tagNamespaceURI);
   895   uint32_t skipAttr = ScanNamespaceDeclarations(content,
   896                           aOriginalElement, tagNamespaceURI);
   898   nsIAtom *name = content->Tag();
   899   bool lineBreakBeforeOpen = LineBreakBeforeOpen(content->GetNameSpaceID(), name);
   901   if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) {
   902     if (mColPos && lineBreakBeforeOpen) {
   903       AppendNewLineToString(aStr);
   904     }
   905     else {
   906       MaybeAddNewlineForRootNode(aStr);
   907     }
   908     if (!mColPos) {
   909       AppendIndentation(aStr);
   910     }
   911     else if (mAddSpace) {
   912       AppendToString(char16_t(' '), aStr);
   913       mAddSpace = false;
   914     }
   915   }
   916   else if (mAddSpace) {
   917     AppendToString(char16_t(' '), aStr);
   918     mAddSpace = false;
   919   }
   920   else {
   921     MaybeAddNewlineForRootNode(aStr);
   922   }
   924   // Always reset to avoid false newlines in case MaybeAddNewlineForRootNode wasn't
   925   // called
   926   mAddNewlineForRootNode = false;
   928   bool addNSAttr;
   929   addNSAttr = ConfirmPrefix(tagPrefix, tagNamespaceURI, aOriginalElement,
   930                             false);
   932   // Serialize the qualified name of the element
   933   AppendToString(kLessThan, aStr);
   934   if (!tagPrefix.IsEmpty()) {
   935     AppendToString(tagPrefix, aStr);
   936     AppendToString(NS_LITERAL_STRING(":"), aStr);
   937   }
   938   AppendToString(tagLocalName, aStr);
   940   MaybeEnterInPreContent(content);
   942   if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) {
   943     IncrIndentation(name);
   944   }
   946   SerializeAttributes(content, aOriginalElement, tagPrefix, tagNamespaceURI,
   947                       name, aStr, skipAttr, addNSAttr);
   949   AppendEndOfElementStart(aOriginalElement, name, content->GetNameSpaceID(),
   950                           aStr);
   952   if ((mDoFormat || forceFormat) && !mPreLevel 
   953     && !mDoRaw && LineBreakAfterOpen(content->GetNameSpaceID(), name)) {
   954     AppendNewLineToString(aStr);
   955   }
   957   AfterElementStart(content, aOriginalElement, aStr);
   959   return NS_OK;
   960 }
   962 void 
   963 nsXMLContentSerializer::AppendEndOfElementStart(nsIContent *aOriginalElement,
   964                                                 nsIAtom * aName,
   965                                                 int32_t aNamespaceID,
   966                                                 nsAString& aStr)
   967 {
   968   // We don't output a separate end tag for empty elements
   969   if (!aOriginalElement->GetChildCount()) {
   970     AppendToString(NS_LITERAL_STRING("/>"), aStr);
   971   }
   972   else {
   973     AppendToString(kGreaterThan, aStr);
   974   }
   975 }
   977 NS_IMETHODIMP 
   978 nsXMLContentSerializer::AppendElementEnd(Element* aElement,
   979                                          nsAString& aStr)
   980 {
   981   NS_ENSURE_ARG(aElement);
   983   nsIContent* content = aElement;
   985   bool forceFormat = false, outputElementEnd;
   986   outputElementEnd = CheckElementEnd(content, forceFormat, aStr);
   988   nsIAtom *name = content->Tag();
   990   if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) {
   991     DecrIndentation(name);
   992   }
   994   if (!outputElementEnd) {
   995     PopNameSpaceDeclsFor(aElement);
   996     MaybeFlagNewlineForRootNode(aElement);
   997     return NS_OK;
   998   }
  1000   nsAutoString tagPrefix, tagLocalName, tagNamespaceURI;
  1002   aElement->NodeInfo()->GetPrefix(tagPrefix);
  1003   aElement->NodeInfo()->GetName(tagLocalName);
  1004   aElement->NodeInfo()->GetNamespaceURI(tagNamespaceURI);
  1006 #ifdef DEBUG
  1007   bool debugNeedToPushNamespace =
  1008 #endif
  1009   ConfirmPrefix(tagPrefix, tagNamespaceURI, aElement, false);
  1010   NS_ASSERTION(!debugNeedToPushNamespace, "Can't push namespaces in closing tag!");
  1012   if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) {
  1014     bool lineBreakBeforeClose = LineBreakBeforeClose(content->GetNameSpaceID(), name);
  1016     if (mColPos && lineBreakBeforeClose) {
  1017       AppendNewLineToString(aStr);
  1019     if (!mColPos) {
  1020       AppendIndentation(aStr);
  1022     else if (mAddSpace) {
  1023       AppendToString(char16_t(' '), aStr);
  1024       mAddSpace = false;
  1027   else if (mAddSpace) {
  1028     AppendToString(char16_t(' '), aStr);
  1029     mAddSpace = false;
  1032   AppendToString(kEndTag, aStr);
  1033   if (!tagPrefix.IsEmpty()) {
  1034     AppendToString(tagPrefix, aStr);
  1035     AppendToString(NS_LITERAL_STRING(":"), aStr);
  1037   AppendToString(tagLocalName, aStr);
  1038   AppendToString(kGreaterThan, aStr);
  1040   PopNameSpaceDeclsFor(aElement);
  1042   MaybeLeaveFromPreContent(content);
  1044   if ((mDoFormat || forceFormat) && !mPreLevel
  1045       && !mDoRaw && LineBreakAfterClose(content->GetNameSpaceID(), name)) {
  1046     AppendNewLineToString(aStr);
  1048   else {
  1049     MaybeFlagNewlineForRootNode(aElement);
  1052   AfterElementEnd(content, aStr);
  1054   return NS_OK;
  1057 NS_IMETHODIMP
  1058 nsXMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument,
  1059                                             nsAString& aStr)
  1061   NS_ENSURE_ARG_POINTER(aDocument);
  1063   nsAutoString version, encoding, standalone;
  1064   aDocument->GetXMLDeclaration(version, encoding, standalone);
  1066   if (version.IsEmpty())
  1067     return NS_OK; // A declaration must have version, or there is no decl
  1069   NS_NAMED_LITERAL_STRING(endQuote, "\"");
  1071   aStr += NS_LITERAL_STRING("<?xml version=\"") + version + endQuote;
  1073   if (!mCharset.IsEmpty()) {
  1074     aStr += NS_LITERAL_STRING(" encoding=\"") +
  1075       NS_ConvertASCIItoUTF16(mCharset) + endQuote;
  1077   // Otherwise just don't output an encoding attr.  Not that we expect
  1078   // mCharset to ever be empty.
  1079 #ifdef DEBUG
  1080   else {
  1081     NS_WARNING("Empty mCharset?  How come?");
  1083 #endif
  1085   if (!standalone.IsEmpty()) {
  1086     aStr += NS_LITERAL_STRING(" standalone=\"") + standalone + endQuote;
  1089   aStr.AppendLiteral("?>");
  1090   mAddNewlineForRootNode = true;
  1092   return NS_OK;
  1095 bool
  1096 nsXMLContentSerializer::CheckElementStart(nsIContent * aContent,
  1097                                           bool & aForceFormat,
  1098                                           nsAString& aStr)
  1100   aForceFormat = false;
  1101   return true;
  1104 bool
  1105 nsXMLContentSerializer::CheckElementEnd(nsIContent * aContent,
  1106                                         bool & aForceFormat,
  1107                                         nsAString& aStr)
  1109   // We don't output a separate end tag for empty element
  1110   aForceFormat = false;
  1111   return aContent->GetChildCount() > 0;
  1114 void 
  1115 nsXMLContentSerializer::AppendToString(const char16_t aChar,
  1116                                        nsAString& aOutputStr)
  1118   if (mBodyOnly && !mInBody) {
  1119     return;
  1121   mColPos += 1;
  1122   aOutputStr.Append(aChar);
  1125 void
  1126 nsXMLContentSerializer::AppendToString(const nsAString& aStr,
  1127                                        nsAString& aOutputStr)
  1129   if (mBodyOnly && !mInBody) {
  1130     return;
  1132   mColPos += aStr.Length();
  1133   aOutputStr.Append(aStr);
  1137 static const uint16_t kGTVal = 62;
  1138 static const char* kEntities[] = {
  1139   "", "", "", "", "", "", "", "", "", "",
  1140   "", "", "", "", "", "", "", "", "", "",
  1141   "", "", "", "", "", "", "", "", "", "",
  1142   "", "", "", "", "", "", "", "", "&amp;", "",
  1143   "", "", "", "", "", "", "", "", "", "",
  1144   "", "", "", "", "", "", "", "", "", "",
  1145   "&lt;", "", "&gt;"
  1146 };
  1148 static const char* kAttrEntities[] = {
  1149   "", "", "", "", "", "", "", "", "", "",
  1150   "", "", "", "", "", "", "", "", "", "",
  1151   "", "", "", "", "", "", "", "", "", "",
  1152   "", "", "", "", "&quot;", "", "", "", "&amp;", "",
  1153   "", "", "", "", "", "", "", "", "", "",
  1154   "", "", "", "", "", "", "", "", "", "",
  1155   "&lt;", "", "&gt;"
  1156 };
  1158 void
  1159 nsXMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr,
  1160                                                    nsAString& aOutputStr)
  1162   nsReadingIterator<char16_t> done_reading;
  1163   aStr.EndReading(done_reading);
  1165   // for each chunk of |aString|...
  1166   uint32_t advanceLength = 0;
  1167   nsReadingIterator<char16_t> iter;
  1169   const char **entityTable = mInAttribute ? kAttrEntities : kEntities;
  1171   for (aStr.BeginReading(iter);
  1172        iter != done_reading;
  1173        iter.advance(int32_t(advanceLength))) {
  1174     uint32_t fragmentLength = iter.size_forward();
  1175     const char16_t* c = iter.get();
  1176     const char16_t* fragmentStart = c;
  1177     const char16_t* fragmentEnd = c + fragmentLength;
  1178     const char* entityText = nullptr;
  1180     advanceLength = 0;
  1181     // for each character in this chunk, check if it
  1182     // needs to be replaced
  1183     for (; c < fragmentEnd; c++, advanceLength++) {
  1184       char16_t val = *c;
  1185       if ((val <= kGTVal) && (entityTable[val][0] != 0)) {
  1186         entityText = entityTable[val];
  1187         break;
  1191     aOutputStr.Append(fragmentStart, advanceLength);
  1192     if (entityText) {
  1193       AppendASCIItoUTF16(entityText, aOutputStr);
  1194       advanceLength++;
  1199 void
  1200 nsXMLContentSerializer::MaybeAddNewlineForRootNode(nsAString& aStr)
  1202   if (mAddNewlineForRootNode) {
  1203     AppendNewLineToString(aStr);
  1207 void
  1208 nsXMLContentSerializer::MaybeFlagNewlineForRootNode(nsINode* aNode)
  1210   nsINode* parent = aNode->GetParentNode();
  1211   if (parent) {
  1212     mAddNewlineForRootNode = parent->IsNodeOfType(nsINode::eDOCUMENT);
  1216 void
  1217 nsXMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode)
  1219   // support of the xml:space attribute
  1220   if (aNode->HasAttr(kNameSpaceID_XML, nsGkAtoms::space)) {
  1221     nsAutoString space;
  1222     aNode->GetAttr(kNameSpaceID_XML, nsGkAtoms::space, space);
  1223     if (space.EqualsLiteral("preserve"))
  1224       ++mPreLevel;
  1228 void
  1229 nsXMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode)
  1231   // support of the xml:space attribute
  1232   if (aNode->HasAttr(kNameSpaceID_XML, nsGkAtoms::space)) {
  1233     nsAutoString space;
  1234     aNode->GetAttr(kNameSpaceID_XML, nsGkAtoms::space, space);
  1235     if (space.EqualsLiteral("preserve"))
  1236       --mPreLevel;
  1240 void
  1241 nsXMLContentSerializer::AppendNewLineToString(nsAString& aStr)
  1243   AppendToString(mLineBreak, aStr);
  1244   mMayIgnoreLineBreakSequence = true;
  1245   mColPos = 0;
  1246   mAddSpace = false;
  1247   mIsIndentationAddedOnCurrentLine = false;
  1250 void
  1251 nsXMLContentSerializer::AppendIndentation(nsAString& aStr)
  1253   mIsIndentationAddedOnCurrentLine = true;
  1254   AppendToString(mIndent, aStr);
  1255   mAddSpace = false;
  1256   mMayIgnoreLineBreakSequence = false;
  1259 void
  1260 nsXMLContentSerializer::IncrIndentation(nsIAtom* aName)
  1262   // we want to keep the source readable
  1263   if (mDoWrap &&
  1264       mIndent.Length() >= uint32_t(mMaxColumn) - MIN_INDENTED_LINE_LENGTH) {
  1265     ++mIndentOverflow;
  1267   else {
  1268     mIndent.AppendLiteral(INDENT_STRING);
  1272 void
  1273 nsXMLContentSerializer::DecrIndentation(nsIAtom* aName)
  1275   if(mIndentOverflow)
  1276     --mIndentOverflow;
  1277   else
  1278     mIndent.Cut(0, INDENT_STRING_LENGTH);
  1281 bool
  1282 nsXMLContentSerializer::LineBreakBeforeOpen(int32_t aNamespaceID, nsIAtom* aName)
  1284   return mAddSpace;
  1287 bool 
  1288 nsXMLContentSerializer::LineBreakAfterOpen(int32_t aNamespaceID, nsIAtom* aName)
  1290   return false;
  1293 bool 
  1294 nsXMLContentSerializer::LineBreakBeforeClose(int32_t aNamespaceID, nsIAtom* aName)
  1296   return mAddSpace;
  1299 bool 
  1300 nsXMLContentSerializer::LineBreakAfterClose(int32_t aNamespaceID, nsIAtom* aName)
  1302   return false;
  1305 void
  1306 nsXMLContentSerializer::AppendToStringConvertLF(const nsAString& aStr,
  1307                                                 nsAString& aOutputStr)
  1309   if (mBodyOnly && !mInBody) {
  1310     return;
  1313   if (mDoRaw) {
  1314     AppendToString(aStr, aOutputStr);
  1316   else {
  1317     // Convert line-endings to mLineBreak
  1318     uint32_t start = 0;
  1319     uint32_t theLen = aStr.Length();
  1320     while (start < theLen) {
  1321       int32_t eol = aStr.FindChar('\n', start);
  1322       if (eol == kNotFound) {
  1323         nsDependentSubstring dataSubstring(aStr, start, theLen - start);
  1324         AppendToString(dataSubstring, aOutputStr);
  1325         start = theLen;
  1326         // if there was a line break before this substring
  1327         // AppendNewLineToString was called, so we should reverse
  1328         // this flag
  1329         mMayIgnoreLineBreakSequence = false;
  1331       else {
  1332         nsDependentSubstring dataSubstring(aStr, start, eol - start);
  1333         AppendToString(dataSubstring, aOutputStr);
  1334         AppendNewLineToString(aOutputStr);
  1335         start = eol + 1;
  1341 void
  1342 nsXMLContentSerializer::AppendFormatedWrapped_WhitespaceSequence(
  1343                         nsASingleFragmentString::const_char_iterator &aPos,
  1344                         const nsASingleFragmentString::const_char_iterator aEnd,
  1345                         const nsASingleFragmentString::const_char_iterator aSequenceStart,
  1346                         bool &aMayIgnoreStartOfLineWhitespaceSequence,
  1347                         nsAString &aOutputStr)
  1349   // Handle the complete sequence of whitespace.
  1350   // Continue to iterate until we find the first non-whitespace char.
  1351   // Updates "aPos" to point to the first unhandled char.
  1352   // Also updates the aMayIgnoreStartOfLineWhitespaceSequence flag,
  1353   // as well as the other "global" state flags.
  1355   bool sawBlankOrTab = false;
  1356   bool leaveLoop = false;
  1358   do {
  1359     switch (*aPos) {
  1360       case ' ':
  1361       case '\t':
  1362         sawBlankOrTab = true;
  1363         // no break
  1364       case '\n':
  1365         ++aPos;
  1366         // do not increase mColPos,
  1367         // because we will reduce the whitespace to a single char
  1368         break;
  1369       default:
  1370         leaveLoop = true;
  1371         break;
  1373   } while (!leaveLoop && aPos < aEnd);
  1375   if (mAddSpace) {
  1376     // if we had previously been asked to add space,
  1377     // our situation has not changed
  1379   else if (!sawBlankOrTab && mMayIgnoreLineBreakSequence) {
  1380     // nothing to do in the case where line breaks have already been added
  1381     // before the call of AppendToStringWrapped
  1382     // and only if we found line break in the sequence
  1383     mMayIgnoreLineBreakSequence = false;
  1385   else if (aMayIgnoreStartOfLineWhitespaceSequence) {
  1386     // nothing to do
  1387     aMayIgnoreStartOfLineWhitespaceSequence = false;
  1389   else {
  1390     if (sawBlankOrTab) {
  1391       if (mDoWrap && mColPos + 1 >= mMaxColumn) {
  1392         // no much sense in delaying, we only have one slot left,
  1393         // let's write a break now
  1394         aOutputStr.Append(mLineBreak);
  1395         mColPos = 0;
  1396         mIsIndentationAddedOnCurrentLine = false;
  1397         mMayIgnoreLineBreakSequence = true;
  1399       else {
  1400         // do not write out yet, we may write out either a space or a linebreak
  1401         // let's delay writing it out until we know more
  1402         mAddSpace = true;
  1403         ++mColPos; // eat a slot of available space
  1406     else {
  1407       // Asian text usually does not contain spaces, therefore we should not
  1408       // transform a linebreak into a space.
  1409       // Since we only saw linebreaks, but no spaces or tabs,
  1410       // let's write a linebreak now.
  1411       AppendNewLineToString(aOutputStr);
  1416 void
  1417 nsXMLContentSerializer::AppendWrapped_NonWhitespaceSequence(
  1418                         nsASingleFragmentString::const_char_iterator &aPos,
  1419                         const nsASingleFragmentString::const_char_iterator aEnd,
  1420                         const nsASingleFragmentString::const_char_iterator aSequenceStart,
  1421                         bool &aMayIgnoreStartOfLineWhitespaceSequence,
  1422                         bool &aSequenceStartAfterAWhiteSpace,
  1423                         nsAString& aOutputStr)
  1425   mMayIgnoreLineBreakSequence = false;
  1426   aMayIgnoreStartOfLineWhitespaceSequence = false;
  1428   // Handle the complete sequence of non-whitespace in this block
  1429   // Iterate until we find the first whitespace char or an aEnd condition
  1430   // Updates "aPos" to point to the first unhandled char.
  1431   // Also updates the aMayIgnoreStartOfLineWhitespaceSequence flag,
  1432   // as well as the other "global" state flags.
  1434   bool thisSequenceStartsAtBeginningOfLine = !mColPos;
  1435   bool onceAgainBecauseWeAddedBreakInFront = false;
  1436   bool foundWhitespaceInLoop;
  1437   uint32_t length, colPos;
  1439   do {
  1441     if (mColPos) {
  1442       colPos = mColPos;
  1444     else {
  1445       if (mDoFormat && !mPreLevel && !onceAgainBecauseWeAddedBreakInFront) {
  1446         colPos = mIndent.Length();
  1448       else
  1449         colPos = 0;
  1451     foundWhitespaceInLoop = false;
  1452     length = 0;
  1453     // we iterate until the next whitespace character
  1454     // or until we reach the maximum of character per line
  1455     // or until the end of the string to add.
  1456     do {
  1457       if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') {
  1458         foundWhitespaceInLoop = true;
  1459         break;
  1462       ++aPos;
  1463       ++length;
  1464     } while ( (!mDoWrap || colPos + length < mMaxColumn) && aPos < aEnd);
  1466     // in the case we don't reached the end of the string, but we reached the maxcolumn,
  1467     // we see if there is a whitespace after the maxcolumn
  1468     // if yes, then we can append directly the string instead of
  1469     // appending a new line etc.
  1470     if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') {
  1471       foundWhitespaceInLoop = true;
  1474     if (aPos == aEnd || foundWhitespaceInLoop) {
  1475       // there is enough room for the complete block we found
  1476       if (mDoFormat && !mColPos) {
  1477         AppendIndentation(aOutputStr);
  1479       else if (mAddSpace) {
  1480         aOutputStr.Append(char16_t(' '));
  1481         mAddSpace = false;
  1484       mColPos += length;
  1485       aOutputStr.Append(aSequenceStart, aPos - aSequenceStart);
  1487       // We have not yet reached the max column, we will continue to
  1488       // fill the current line in the next outer loop iteration
  1489       // (this one in AppendToStringWrapped)
  1490       // make sure we return in this outer loop
  1491       onceAgainBecauseWeAddedBreakInFront = false;
  1493     else { // we reach the max column
  1494       if (!thisSequenceStartsAtBeginningOfLine &&
  1495           (mAddSpace || (!mDoFormat && aSequenceStartAfterAWhiteSpace))) { 
  1496           // when !mDoFormat, mAddSpace is not used, mAddSpace is always false
  1497           // so, in the case where mDoWrap && !mDoFormat, if we want to enter in this condition...
  1499         // We can avoid to wrap. We try to add the whole block 
  1500         // in an empty new line
  1502         AppendNewLineToString(aOutputStr);
  1503         aPos = aSequenceStart;
  1504         thisSequenceStartsAtBeginningOfLine = true;
  1505         onceAgainBecauseWeAddedBreakInFront = true;
  1507       else {
  1508         // we must wrap
  1509         onceAgainBecauseWeAddedBreakInFront = false;
  1510         bool foundWrapPosition = false;
  1511         int32_t wrapPosition;
  1513         nsILineBreaker *lineBreaker = nsContentUtils::LineBreaker();
  1515         wrapPosition = lineBreaker->Prev(aSequenceStart,
  1516                                          (aEnd - aSequenceStart),
  1517                                          (aPos - aSequenceStart) + 1);
  1518         if (wrapPosition != NS_LINEBREAKER_NEED_MORE_TEXT) {
  1519           foundWrapPosition = true;
  1521         else {
  1522           wrapPosition = lineBreaker->Next(aSequenceStart,
  1523                                            (aEnd - aSequenceStart),
  1524                                            (aPos - aSequenceStart));
  1525           if (wrapPosition != NS_LINEBREAKER_NEED_MORE_TEXT) {
  1526             foundWrapPosition = true;
  1530         if (foundWrapPosition) {
  1531           if (!mColPos && mDoFormat) {
  1532             AppendIndentation(aOutputStr);
  1534           else if (mAddSpace) {
  1535             aOutputStr.Append(char16_t(' '));
  1536             mAddSpace = false;
  1538           aOutputStr.Append(aSequenceStart, wrapPosition);
  1540           AppendNewLineToString(aOutputStr);
  1541           aPos = aSequenceStart + wrapPosition;
  1542           aMayIgnoreStartOfLineWhitespaceSequence = true;
  1544         else {
  1545           // try some simple fallback logic
  1546           // go forward up to the next whitespace position,
  1547           // in the worst case this will be all the rest of the data
  1549           // we update the mColPos variable with the length of
  1550           // the part already parsed.
  1551           mColPos += length;
  1553           // now try to find the next whitespace
  1554           do {
  1555             if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') {
  1556               break;
  1559             ++aPos;
  1560             ++mColPos;
  1561           } while (aPos < aEnd);
  1563           if (mAddSpace) {
  1564             aOutputStr.Append(char16_t(' '));
  1565             mAddSpace = false;
  1567           aOutputStr.Append(aSequenceStart, aPos - aSequenceStart);
  1570       aSequenceStartAfterAWhiteSpace = false;
  1572   } while (onceAgainBecauseWeAddedBreakInFront);
  1575 void 
  1576 nsXMLContentSerializer::AppendToStringFormatedWrapped(const nsASingleFragmentString& aStr,
  1577                                                       nsAString& aOutputStr)
  1579   if (mBodyOnly && !mInBody) {
  1580     return;
  1583   nsASingleFragmentString::const_char_iterator pos, end, sequenceStart;
  1585   aStr.BeginReading(pos);
  1586   aStr.EndReading(end);
  1588   bool sequenceStartAfterAWhitespace = false;
  1589   if (pos < end) {
  1590     nsAString::const_char_iterator end2;
  1591     aOutputStr.EndReading(end2);
  1592     --end2;
  1593     if (*end2 == ' ' || *end2 == '\n' || *end2 == '\t') {
  1594       sequenceStartAfterAWhitespace = true;
  1598   // if the current line already has text on it, such as a tag,
  1599   // leading whitespace is significant
  1600   bool mayIgnoreStartOfLineWhitespaceSequence =
  1601     (!mColPos || (mIsIndentationAddedOnCurrentLine &&
  1602                   sequenceStartAfterAWhitespace &&
  1603                   uint32_t(mColPos) == mIndent.Length()));
  1605   while (pos < end) {
  1606     sequenceStart = pos;
  1608     // if beginning of a whitespace sequence
  1609     if (*pos == ' ' || *pos == '\n' || *pos == '\t') {
  1610       AppendFormatedWrapped_WhitespaceSequence(pos, end, sequenceStart,
  1611         mayIgnoreStartOfLineWhitespaceSequence, aOutputStr);
  1613     else { // any other non-whitespace char
  1614       AppendWrapped_NonWhitespaceSequence(pos, end, sequenceStart,
  1615         mayIgnoreStartOfLineWhitespaceSequence, sequenceStartAfterAWhitespace, aOutputStr);
  1620 void
  1621 nsXMLContentSerializer::AppendWrapped_WhitespaceSequence(
  1622                         nsASingleFragmentString::const_char_iterator &aPos,
  1623                         const nsASingleFragmentString::const_char_iterator aEnd,
  1624                         const nsASingleFragmentString::const_char_iterator aSequenceStart,
  1625                         nsAString &aOutputStr)
  1627   // Handle the complete sequence of whitespace.
  1628   // Continue to iterate until we find the first non-whitespace char.
  1629   // Updates "aPos" to point to the first unhandled char.
  1630   mAddSpace = false;
  1631   mIsIndentationAddedOnCurrentLine = false;
  1633   bool leaveLoop = false;
  1634   nsASingleFragmentString::const_char_iterator lastPos = aPos;
  1636   do {
  1637     switch (*aPos) {
  1638       case ' ':
  1639       case '\t':
  1640         // if there are too many spaces on a line, we wrap
  1641         if (mColPos >= mMaxColumn) {
  1642           if (lastPos != aPos) {
  1643             aOutputStr.Append(lastPos, aPos - lastPos);
  1645           AppendToString(mLineBreak, aOutputStr);
  1646           mColPos = 0;
  1647           lastPos = aPos;
  1650         ++mColPos;
  1651         ++aPos;
  1652         break;
  1653       case '\n':
  1654         if (lastPos != aPos) {
  1655           aOutputStr.Append(lastPos, aPos - lastPos);
  1657         AppendToString(mLineBreak, aOutputStr);
  1658         mColPos = 0;
  1659         ++aPos;
  1660         lastPos = aPos;
  1661         break;
  1662       default:
  1663         leaveLoop = true;
  1664         break;
  1666   } while (!leaveLoop && aPos < aEnd);
  1668   if (lastPos != aPos) {
  1669     aOutputStr.Append(lastPos, aPos - lastPos);
  1673 void 
  1674 nsXMLContentSerializer::AppendToStringWrapped(const nsASingleFragmentString& aStr,
  1675                                               nsAString& aOutputStr)
  1677   if (mBodyOnly && !mInBody) {
  1678     return;
  1681   nsASingleFragmentString::const_char_iterator pos, end, sequenceStart;
  1683   aStr.BeginReading(pos);
  1684   aStr.EndReading(end);
  1686   // not used in this case, but needed by AppendWrapped_NonWhitespaceSequence
  1687   bool mayIgnoreStartOfLineWhitespaceSequence = false;
  1688   mMayIgnoreLineBreakSequence = false;
  1690   bool sequenceStartAfterAWhitespace = false;
  1691   if (pos < end && !aOutputStr.IsEmpty()) {
  1692     nsAString::const_char_iterator end2;
  1693     aOutputStr.EndReading(end2);
  1694     --end2;
  1695     if (*end2 == ' ' || *end2 == '\n' || *end2 == '\t') {
  1696       sequenceStartAfterAWhitespace = true;
  1700   while (pos < end) {
  1701     sequenceStart = pos;
  1703     // if beginning of a whitespace sequence
  1704     if (*pos == ' ' || *pos == '\n' || *pos == '\t') {
  1705       sequenceStartAfterAWhitespace = true;
  1706       AppendWrapped_WhitespaceSequence(pos, end, sequenceStart, aOutputStr);
  1708     else { // any other non-whitespace char
  1709       AppendWrapped_NonWhitespaceSequence(pos, end, sequenceStart,
  1710         mayIgnoreStartOfLineWhitespaceSequence, sequenceStartAfterAWhitespace, aOutputStr);

mercurial