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/. */
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.
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"
25 #include "nsUnicharUtils.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
)
48 nsXMLContentSerializer
* it
= new nsXMLContentSerializer();
50 return NS_ERROR_OUT_OF_MEMORY
;
53 return CallQueryInterface(it
, aSerializer
);
56 nsXMLContentSerializer::nsXMLContentSerializer()
60 mIsIndentationAddedOnCurrentLine(false),
62 mAddNewlineForRootNode(false),
64 mMayIgnoreLineBreakSequence(false),
70 nsXMLContentSerializer::~nsXMLContentSerializer()
74 NS_IMPL_ISUPPORTS(nsXMLContentSerializer
, nsIContentSerializer
)
77 nsXMLContentSerializer::Init(uint32_t aFlags
, uint32_t aWrapColumn
,
78 const char* aCharSet
, bool aIsCopying
,
79 bool aRewriteEncodingDeclaration
)
84 mIsIndentationAddedOnCurrentLine
= false;
86 mAddNewlineForRootNode
= false;
88 mMayIgnoreLineBreakSequence
= false;
95 // Set the line break character:
96 if ((mFlags
& nsIDocumentEncoder::OutputCRLineBreak
)
97 && (mFlags
& nsIDocumentEncoder::OutputLFLineBreak
)) { // Windows
98 mLineBreak
.AssignLiteral("\r\n");
100 else if (mFlags
& nsIDocumentEncoder::OutputCRLineBreak
) { // Mac
101 mLineBreak
.Assign('\r');
103 else if (mFlags
& nsIDocumentEncoder::OutputLFLineBreak
) { // Unix/DOM
104 mLineBreak
.Assign('\n');
107 mLineBreak
.AssignLiteral(NS_LINEBREAK
); // Platform/default
110 mDoRaw
= !!(mFlags
& nsIDocumentEncoder::OutputRaw
);
112 mDoFormat
= (mFlags
& nsIDocumentEncoder::OutputFormatted
&& !mDoRaw
);
114 mDoWrap
= (mFlags
& nsIDocumentEncoder::OutputWrap
&& !mDoRaw
);
120 mMaxColumn
= aWrapColumn
;
124 mIsIndentationAddedOnCurrentLine
= false;
129 nsXMLContentSerializer::AppendTextData(nsIContent
* aNode
,
130 int32_t aStartOffset
,
133 bool aTranslateEntities
)
135 nsIContent
* content
= aNode
;
136 const nsTextFragment
* frag
;
137 if (!content
|| !(frag
= content
->GetText())) {
138 return NS_ERROR_FAILURE
;
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!");
149 // XXX Zero is a legal value, maybe non-zero values should be an
155 const char16_t
*strStart
= frag
->Get2b() + aStartOffset
;
156 if (aTranslateEntities
) {
157 AppendAndTranslateEntities(Substring(strStart
, strStart
+ length
), aStr
);
160 aStr
.Append(Substring(strStart
, strStart
+ length
));
164 if (aTranslateEntities
) {
165 AppendAndTranslateEntities(NS_ConvertASCIItoUTF16(frag
->Get1b()+aStartOffset
, length
), aStr
);
168 aStr
.Append(NS_ConvertASCIItoUTF16(frag
->Get1b()+aStartOffset
, length
));
176 nsXMLContentSerializer::AppendText(nsIContent
* aText
,
177 int32_t aStartOffset
,
181 NS_ENSURE_ARG(aText
);
186 rv
= AppendTextData(aText
, aStartOffset
, aEndOffset
, data
, true);
188 return NS_ERROR_FAILURE
;
190 if (mDoRaw
|| PreLevel() > 0) {
191 AppendToStringConvertLF(data
, aStr
);
193 else if (mDoFormat
) {
194 AppendToStringFormatedWrapped(data
, aStr
);
197 AppendToStringWrapped(data
, aStr
);
200 AppendToStringConvertLF(data
, aStr
);
207 nsXMLContentSerializer::AppendCDATASection(nsIContent
* aCDATASection
,
208 int32_t aStartOffset
,
212 NS_ENSURE_ARG(aCDATASection
);
215 NS_NAMED_LITERAL_STRING(cdata
, "<![CDATA[");
217 if (mDoRaw
|| PreLevel() > 0) {
218 AppendToString(cdata
, aStr
);
220 else if (mDoFormat
) {
221 AppendToStringFormatedWrapped(cdata
, aStr
);
224 AppendToStringWrapped(cdata
, aStr
);
227 AppendToString(cdata
, aStr
);
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
);
242 nsXMLContentSerializer::AppendProcessingInstruction(nsIContent
* aPI
,
243 int32_t aStartOffset
,
247 nsCOMPtr
<nsIDOMProcessingInstruction
> pi
= do_QueryInterface(aPI
);
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 (mDoRaw
|| PreLevel() > 0) {
264 AppendToString(start
, aStr
);
266 else if (mDoFormat
) {
268 AppendNewLineToString(aStr
);
270 AppendToStringFormatedWrapped(start
, aStr
);
273 AppendToStringWrapped(start
, aStr
);
276 AppendToString(start
, aStr
);
279 if (!data
.IsEmpty()) {
280 AppendToString(char16_t(' '), aStr
);
281 AppendToStringConvertLF(data
, aStr
);
283 AppendToString(NS_LITERAL_STRING("?>"), aStr
);
285 MaybeFlagNewlineForRootNode(aPI
);
291 nsXMLContentSerializer::AppendComment(nsIContent
* aComment
,
292 int32_t aStartOffset
,
296 nsCOMPtr
<nsIDOMComment
> comment
= do_QueryInterface(aComment
);
297 NS_ENSURE_ARG(comment
);
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
)) {
307 (aEndOffset
== -1) ? dataLength
: std::min(aEndOffset
, dataLength
);
308 length
-= aStartOffset
;
312 data
.Mid(frag
, aStartOffset
, length
);
317 MaybeAddNewlineForRootNode(aStr
);
319 NS_NAMED_LITERAL_STRING(startComment
, "<!--");
321 if (mDoRaw
|| PreLevel() > 0) {
322 AppendToString(startComment
, aStr
);
324 else if (mDoFormat
) {
326 AppendNewLineToString(aStr
);
328 AppendToStringFormatedWrapped(startComment
, aStr
);
331 AppendToStringWrapped(startComment
, aStr
);
334 AppendToString(startComment
, aStr
);
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
);
348 nsXMLContentSerializer::AppendDoctype(nsIContent
* aDocType
,
351 nsCOMPtr
<nsIDOMDocumentType
> docType
= do_QueryInterface(aDocType
);
352 NS_ENSURE_ARG(docType
);
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
);
371 if (!publicId
.IsEmpty()) {
372 AppendToString(NS_LITERAL_STRING(" PUBLIC "), aStr
);
373 if (publicId
.FindChar(char16_t('"')) == -1) {
374 quote
= char16_t('"');
377 quote
= char16_t('\'');
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('"');
389 quote
= char16_t('\'');
391 AppendToString(quote
, aStr
);
392 AppendToString(systemId
, aStr
);
393 AppendToString(quote
, aStr
);
396 else if (!systemId
.IsEmpty()) {
397 if (systemId
.FindChar(char16_t('"')) == -1) {
398 quote
= char16_t('"');
401 quote
= char16_t('\'');
403 AppendToString(NS_LITERAL_STRING(" SYSTEM "), aStr
);
404 AppendToString(quote
, aStr
);
405 AppendToString(systemId
, aStr
);
406 AppendToString(quote
, aStr
);
409 if (!internalSubset
.IsEmpty()) {
410 AppendToString(NS_LITERAL_STRING(" ["), aStr
);
411 AppendToString(internalSubset
, aStr
);
412 AppendToString(char16_t(']'), aStr
);
415 AppendToString(kGreaterThan
, aStr
);
416 MaybeFlagNewlineForRootNode(aDocType
);
422 nsXMLContentSerializer::PushNameSpaceDecl(const nsAString
& aPrefix
,
423 const nsAString
& aURI
,
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
433 decl
->mOwner
= aOwner
;
438 nsXMLContentSerializer::PopNameSpaceDeclsFor(nsIContent
* aOwner
)
440 int32_t index
, count
;
442 count
= mNameSpaceStack
.Length();
443 for (index
= count
- 1; index
>= 0; index
--) {
444 if (mNameSpaceStack
[index
].mOwner
!= aOwner
) {
447 mNameSpaceStack
.RemoveElementAt(index
);
452 nsXMLContentSerializer::ConfirmPrefix(nsAString
& aPrefix
,
453 const nsAString
& aURI
,
454 nsIContent
* aElement
,
457 if (aPrefix
.EqualsLiteral(kXMLNS
)) {
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");
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
478 // Attribute not in the null namespace -- must have a prefix
479 mustHavePrefix
= true;
481 // Not an attribute, so doesn't _have_ to have a prefix
482 mustHavePrefix
= false;
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;
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
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.
509 closestURIMatch
= aPrefix
;
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
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 "
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.
537 haveSeenOurPrefix
= false;
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;
548 for (index2
= count
-1; index2
> index
&& prefixOK
; --index2
) {
549 prefixOK
= (mNameSpaceStack
[index2
].mPrefix
!= decl
.mPrefix
);
554 closestURIMatch
.Assign(decl
.mPrefix
);
561 // At this point the following invariants hold:
562 // 1) The prefix in closestURIMatch is mapped to aURI in our scope if
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
);
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
581 if (mustHavePrefix
) {
582 GenerateNewPrefix(aPrefix
);
583 return ConfirmPrefix(aPrefix
, aURI
, aElement
, aIsAttribute
);
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()) {
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
601 nsXMLContentSerializer::GenerateNewPrefix(nsAString
& aPrefix
)
605 PR_snprintf(buf
, sizeof(buf
), "%d", mPrefixIndex
++);
606 AppendASCIItoUTF16(buf
, aPrefix
);
610 nsXMLContentSerializer::SerializeAttr(const nsAString
& aPrefix
,
611 const nsAString
& aName
,
612 const nsAString
& aValue
,
614 bool aDoEscapeEntities
)
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(':'));
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("=\"");
635 AppendAndTranslateEntities(aValue
, attrString
);
636 mInAttribute
= false;
638 attrString
.Append(char16_t('"'));
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
;
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('\'') )
663 bIncludesSingle
= true;
664 if ( bIncludesDouble
) break;
666 else if ( *buf
== char16_t('"') )
668 bIncludesDouble
= true;
669 if ( bIncludesSingle
) break;
672 // if both have been found we don't need to search further
673 if ( bIncludesDouble
&& bIncludesSingle
) break;
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
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("&"));
689 if (bIncludesDouble
&& bIncludesSingle
) {
690 sValue
.ReplaceSubstring(NS_LITERAL_STRING("\""),
691 NS_LITERAL_STRING("""));
693 attrString
.Append(sValue
);
694 attrString
.Append(cDelimiter
);
696 if (mDoRaw
|| PreLevel() > 0) {
697 AppendToStringConvertLF(attrString
, aStr
);
699 else if (mDoFormat
) {
700 AppendToStringFormatedWrapped(attrString
, aStr
);
703 AppendToStringWrapped(attrString
, aStr
);
706 AppendToStringConvertLF(attrString
, aStr
);
711 nsXMLContentSerializer::ScanNamespaceDeclarations(nsIContent
* aContent
,
712 nsIContent
*aOriginalElement
,
713 const nsAString
& aTagNamespaceURI
)
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.
751 // Default NS attribute does not have prefix (and the name is "xmlns")
752 PushNameSpaceDecl(EmptyString(), uriStr
, aOriginalElement
);
756 PushNameSpaceDecl(nsDependentAtomString(attrName
), uriStr
,
766 nsXMLContentSerializer::IsJavaScript(nsIContent
* aContent
, nsIAtom
* aAttrNameAtom
,
767 int32_t aAttrNamespaceID
, const nsAString
& aValueString
)
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))
782 nsAutoString
scheme(Substring(aValueString
, 0, pos
));
783 scheme
.StripWhitespace();
784 if ((scheme
.Length() == (sizeof kJavaScript
- 1)) &&
785 scheme
.EqualsIgnoreCase(kJavaScript
))
791 return aContent
->IsEventAttributeName(aAttrNameAtom
);
796 nsXMLContentSerializer::SerializeAttributes(nsIContent
* aContent
,
797 nsIContent
*aOriginalElement
,
798 nsAString
& aTagPrefix
,
799 const nsAString
& aTagNamespaceURI
,
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
814 if (aTagPrefix
.IsEmpty()) {
815 // Serialize default namespace decl
816 SerializeAttr(EmptyString(), xmlnsStr
, aTagNamespaceURI
, aStr
, true);
819 // Serialize namespace decl
820 SerializeAttr(xmlnsStr
, aTagPrefix
, aTagNamespaceURI
, aStr
, true);
822 PushNameSpaceDecl(aTagPrefix
, aTagNamespaceURI
, aOriginalElement
);
825 count
= aContent
->GetAttrCount();
827 // Now serialize each of the attributes
828 // XXX Unfortunately we need a namespace manager to get
830 for (index
= 0; index
< count
; index
++) {
831 if (aSkipAttr
== index
) {
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"))) {
848 attrPrefix
->ToString(prefixStr
);
851 prefixStr
.Truncate();
854 bool addNSAttr
= false;
855 if (kNameSpaceID_XMLNS
!= namespaceID
) {
856 nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID
, uriStr
);
857 addNSAttr
= ConfirmPrefix(prefixStr
, uriStr
, aOriginalElement
, true);
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
);
868 NS_ASSERTION(!prefixStr
.IsEmpty(),
869 "Namespaced attributes must have a prefix");
870 SerializeAttr(xmlnsStr
, prefixStr
, uriStr
, aStr
, true);
871 PushNameSpaceDecl(prefixStr
, uriStr
, aOriginalElement
);
877 nsXMLContentSerializer::AppendElementStart(Element
* aElement
,
878 Element
* aOriginalElement
,
881 NS_ENSURE_ARG(aElement
);
883 nsIContent
* content
= aElement
;
885 bool forceFormat
= false;
886 if (!CheckElementStart(content
, forceFormat
, aStr
)) {
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
) && !mDoRaw
&& !PreLevel()) {
902 if (mColPos
&& lineBreakBeforeOpen
) {
903 AppendNewLineToString(aStr
);
906 MaybeAddNewlineForRootNode(aStr
);
909 AppendIndentation(aStr
);
911 else if (mAddSpace
) {
912 AppendToString(char16_t(' '), aStr
);
916 else if (mAddSpace
) {
917 AppendToString(char16_t(' '), aStr
);
921 MaybeAddNewlineForRootNode(aStr
);
924 // Always reset to avoid false newlines in case MaybeAddNewlineForRootNode wasn't
926 mAddNewlineForRootNode
= false;
929 addNSAttr
= ConfirmPrefix(tagPrefix
, tagNamespaceURI
, aOriginalElement
,
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
);
938 AppendToString(tagLocalName
, aStr
);
940 MaybeEnterInPreContent(content
);
942 if ((mDoFormat
|| forceFormat
) && !mDoRaw
&& !PreLevel()) {
943 IncrIndentation(name
);
946 SerializeAttributes(content
, aOriginalElement
, tagPrefix
, tagNamespaceURI
,
947 name
, aStr
, skipAttr
, addNSAttr
);
949 AppendEndOfElementStart(aOriginalElement
, name
, content
->GetNameSpaceID(),
952 if ((mDoFormat
|| forceFormat
) && !mDoRaw
&& !PreLevel()
953 && LineBreakAfterOpen(content
->GetNameSpaceID(), name
)) {
954 AppendNewLineToString(aStr
);
957 AfterElementStart(content
, aOriginalElement
, aStr
);
963 nsXMLContentSerializer::AppendEndOfElementStart(nsIContent
*aOriginalElement
,
965 int32_t aNamespaceID
,
968 // We don't output a separate end tag for empty elements
969 if (!aOriginalElement
->GetChildCount()) {
970 AppendToString(NS_LITERAL_STRING("/>"), aStr
);
973 AppendToString(kGreaterThan
, aStr
);
978 nsXMLContentSerializer::AppendElementEnd(Element
* aElement
,
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
) && !mDoRaw
&& !PreLevel()) {
991 DecrIndentation(name
);
994 if (!outputElementEnd
) {
995 PopNameSpaceDeclsFor(aElement
);
996 MaybeFlagNewlineForRootNode(aElement
);
1000 nsAutoString tagPrefix
, tagLocalName
, tagNamespaceURI
;
1002 aElement
->NodeInfo()->GetPrefix(tagPrefix
);
1003 aElement
->NodeInfo()->GetName(tagLocalName
);
1004 aElement
->NodeInfo()->GetNamespaceURI(tagNamespaceURI
);
1007 bool debugNeedToPushNamespace
=
1009 ConfirmPrefix(tagPrefix
, tagNamespaceURI
, aElement
, false);
1010 NS_ASSERTION(!debugNeedToPushNamespace
, "Can't push namespaces in closing tag!");
1012 if ((mDoFormat
|| forceFormat
) && !mDoRaw
&& !PreLevel()) {
1014 bool lineBreakBeforeClose
= LineBreakBeforeClose(content
->GetNameSpaceID(), name
);
1016 if (mColPos
&& lineBreakBeforeClose
) {
1017 AppendNewLineToString(aStr
);
1020 AppendIndentation(aStr
);
1022 else if (mAddSpace
) {
1023 AppendToString(char16_t(' '), aStr
);
1027 else if (mAddSpace
) {
1028 AppendToString(char16_t(' '), aStr
);
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
) && !mDoRaw
&& !PreLevel()
1045 && LineBreakAfterClose(content
->GetNameSpaceID(), name
)) {
1046 AppendNewLineToString(aStr
);
1049 MaybeFlagNewlineForRootNode(aElement
);
1052 AfterElementEnd(content
, aStr
);
1058 nsXMLContentSerializer::AppendDocumentStart(nsIDocument
*aDocument
,
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.
1081 NS_WARNING("Empty mCharset? How come?");
1085 if (!standalone
.IsEmpty()) {
1086 aStr
+= NS_LITERAL_STRING(" standalone=\"") + standalone
+ endQuote
;
1089 aStr
.AppendLiteral("?>");
1090 mAddNewlineForRootNode
= true;
1096 nsXMLContentSerializer::CheckElementStart(nsIContent
* aContent
,
1097 bool & aForceFormat
,
1100 aForceFormat
= false;
1105 nsXMLContentSerializer::CheckElementEnd(nsIContent
* aContent
,
1106 bool & aForceFormat
,
1109 // We don't output a separate end tag for empty element
1110 aForceFormat
= false;
1111 return aContent
->GetChildCount() > 0;
1115 nsXMLContentSerializer::AppendToString(const char16_t aChar
,
1116 nsAString
& aOutputStr
)
1118 if (mBodyOnly
&& !mInBody
) {
1122 aOutputStr
.Append(aChar
);
1126 nsXMLContentSerializer::AppendToString(const nsAString
& aStr
,
1127 nsAString
& aOutputStr
)
1129 if (mBodyOnly
&& !mInBody
) {
1132 mColPos
+= aStr
.Length();
1133 aOutputStr
.Append(aStr
);
1137 static const uint16_t kGTVal
= 62;
1138 static const char* kEntities
[] = {
1139 "", "", "", "", "", "", "", "", "", "",
1140 "", "", "", "", "", "", "", "", "", "",
1141 "", "", "", "", "", "", "", "", "", "",
1142 "", "", "", "", "", "", "", "", "&", "",
1143 "", "", "", "", "", "", "", "", "", "",
1144 "", "", "", "", "", "", "", "", "", "",
1148 static const char* kAttrEntities
[] = {
1149 "", "", "", "", "", "", "", "", "", "",
1150 "", "", "", "", "", "", "", "", "", "",
1151 "", "", "", "", "", "", "", "", "", "",
1152 "", "", "", "", """, "", "", "", "&", "",
1153 "", "", "", "", "", "", "", "", "", "",
1154 "", "", "", "", "", "", "", "", "", "",
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;
1181 // for each character in this chunk, check if it
1182 // needs to be replaced
1183 for (; c
< fragmentEnd
; c
++, advanceLength
++) {
1185 if ((val
<= kGTVal
) && (entityTable
[val
][0] != 0)) {
1186 entityText
= entityTable
[val
];
1191 aOutputStr
.Append(fragmentStart
, advanceLength
);
1193 AppendASCIItoUTF16(entityText
, aOutputStr
);
1200 nsXMLContentSerializer::MaybeAddNewlineForRootNode(nsAString
& aStr
)
1202 if (mAddNewlineForRootNode
) {
1203 AppendNewLineToString(aStr
);
1208 nsXMLContentSerializer::MaybeFlagNewlineForRootNode(nsINode
* aNode
)
1210 nsINode
* parent
= aNode
->GetParentNode();
1212 mAddNewlineForRootNode
= parent
->IsNodeOfType(nsINode::eDOCUMENT
);
1217 nsXMLContentSerializer::MaybeEnterInPreContent(nsIContent
* aNode
)
1219 // support of the xml:space attribute
1220 if (ShouldMaintainPreLevel() &&
1221 aNode
->HasAttr(kNameSpaceID_XML
, nsGkAtoms::space
)) {
1223 aNode
->GetAttr(kNameSpaceID_XML
, nsGkAtoms::space
, space
);
1224 if (space
.EqualsLiteral("preserve"))
1230 nsXMLContentSerializer::MaybeLeaveFromPreContent(nsIContent
* aNode
)
1232 // support of the xml:space attribute
1233 if (ShouldMaintainPreLevel() &&
1234 aNode
->HasAttr(kNameSpaceID_XML
, nsGkAtoms::space
)) {
1236 aNode
->GetAttr(kNameSpaceID_XML
, nsGkAtoms::space
, space
);
1237 if (space
.EqualsLiteral("preserve"))
1243 nsXMLContentSerializer::AppendNewLineToString(nsAString
& aStr
)
1245 AppendToString(mLineBreak
, aStr
);
1246 mMayIgnoreLineBreakSequence
= true;
1249 mIsIndentationAddedOnCurrentLine
= false;
1253 nsXMLContentSerializer::AppendIndentation(nsAString
& aStr
)
1255 mIsIndentationAddedOnCurrentLine
= true;
1256 AppendToString(mIndent
, aStr
);
1258 mMayIgnoreLineBreakSequence
= false;
1262 nsXMLContentSerializer::IncrIndentation(nsIAtom
* aName
)
1264 // we want to keep the source readable
1266 mIndent
.Length() >= uint32_t(mMaxColumn
) - MIN_INDENTED_LINE_LENGTH
) {
1270 mIndent
.AppendLiteral(INDENT_STRING
);
1275 nsXMLContentSerializer::DecrIndentation(nsIAtom
* aName
)
1280 mIndent
.Cut(0, INDENT_STRING_LENGTH
);
1284 nsXMLContentSerializer::LineBreakBeforeOpen(int32_t aNamespaceID
, nsIAtom
* aName
)
1290 nsXMLContentSerializer::LineBreakAfterOpen(int32_t aNamespaceID
, nsIAtom
* aName
)
1296 nsXMLContentSerializer::LineBreakBeforeClose(int32_t aNamespaceID
, nsIAtom
* aName
)
1302 nsXMLContentSerializer::LineBreakAfterClose(int32_t aNamespaceID
, nsIAtom
* aName
)
1308 nsXMLContentSerializer::AppendToStringConvertLF(const nsAString
& aStr
,
1309 nsAString
& aOutputStr
)
1311 if (mBodyOnly
&& !mInBody
) {
1316 AppendToString(aStr
, aOutputStr
);
1319 // Convert line-endings to mLineBreak
1321 uint32_t theLen
= aStr
.Length();
1322 while (start
< theLen
) {
1323 int32_t eol
= aStr
.FindChar('\n', start
);
1324 if (eol
== kNotFound
) {
1325 nsDependentSubstring
dataSubstring(aStr
, start
, theLen
- start
);
1326 AppendToString(dataSubstring
, aOutputStr
);
1328 // if there was a line break before this substring
1329 // AppendNewLineToString was called, so we should reverse
1331 mMayIgnoreLineBreakSequence
= false;
1334 nsDependentSubstring
dataSubstring(aStr
, start
, eol
- start
);
1335 AppendToString(dataSubstring
, aOutputStr
);
1336 AppendNewLineToString(aOutputStr
);
1344 nsXMLContentSerializer::AppendFormatedWrapped_WhitespaceSequence(
1345 nsASingleFragmentString::const_char_iterator
&aPos
,
1346 const nsASingleFragmentString::const_char_iterator aEnd
,
1347 const nsASingleFragmentString::const_char_iterator aSequenceStart
,
1348 bool &aMayIgnoreStartOfLineWhitespaceSequence
,
1349 nsAString
&aOutputStr
)
1351 // Handle the complete sequence of whitespace.
1352 // Continue to iterate until we find the first non-whitespace char.
1353 // Updates "aPos" to point to the first unhandled char.
1354 // Also updates the aMayIgnoreStartOfLineWhitespaceSequence flag,
1355 // as well as the other "global" state flags.
1357 bool sawBlankOrTab
= false;
1358 bool leaveLoop
= false;
1364 sawBlankOrTab
= true;
1368 // do not increase mColPos,
1369 // because we will reduce the whitespace to a single char
1375 } while (!leaveLoop
&& aPos
< aEnd
);
1378 // if we had previously been asked to add space,
1379 // our situation has not changed
1381 else if (!sawBlankOrTab
&& mMayIgnoreLineBreakSequence
) {
1382 // nothing to do in the case where line breaks have already been added
1383 // before the call of AppendToStringWrapped
1384 // and only if we found line break in the sequence
1385 mMayIgnoreLineBreakSequence
= false;
1387 else if (aMayIgnoreStartOfLineWhitespaceSequence
) {
1389 aMayIgnoreStartOfLineWhitespaceSequence
= false;
1392 if (sawBlankOrTab
) {
1393 if (mDoWrap
&& mColPos
+ 1 >= mMaxColumn
) {
1394 // no much sense in delaying, we only have one slot left,
1395 // let's write a break now
1396 aOutputStr
.Append(mLineBreak
);
1398 mIsIndentationAddedOnCurrentLine
= false;
1399 mMayIgnoreLineBreakSequence
= true;
1402 // do not write out yet, we may write out either a space or a linebreak
1403 // let's delay writing it out until we know more
1405 ++mColPos
; // eat a slot of available space
1409 // Asian text usually does not contain spaces, therefore we should not
1410 // transform a linebreak into a space.
1411 // Since we only saw linebreaks, but no spaces or tabs,
1412 // let's write a linebreak now.
1413 AppendNewLineToString(aOutputStr
);
1419 nsXMLContentSerializer::AppendWrapped_NonWhitespaceSequence(
1420 nsASingleFragmentString::const_char_iterator
&aPos
,
1421 const nsASingleFragmentString::const_char_iterator aEnd
,
1422 const nsASingleFragmentString::const_char_iterator aSequenceStart
,
1423 bool &aMayIgnoreStartOfLineWhitespaceSequence
,
1424 bool &aSequenceStartAfterAWhiteSpace
,
1425 nsAString
& aOutputStr
)
1427 mMayIgnoreLineBreakSequence
= false;
1428 aMayIgnoreStartOfLineWhitespaceSequence
= false;
1430 // Handle the complete sequence of non-whitespace in this block
1431 // Iterate until we find the first whitespace char or an aEnd condition
1432 // Updates "aPos" to point to the first unhandled char.
1433 // Also updates the aMayIgnoreStartOfLineWhitespaceSequence flag,
1434 // as well as the other "global" state flags.
1436 bool thisSequenceStartsAtBeginningOfLine
= !mColPos
;
1437 bool onceAgainBecauseWeAddedBreakInFront
= false;
1438 bool foundWhitespaceInLoop
;
1439 uint32_t length
, colPos
;
1447 if (mDoFormat
&& !mDoRaw
&& !PreLevel() && !onceAgainBecauseWeAddedBreakInFront
) {
1448 colPos
= mIndent
.Length();
1453 foundWhitespaceInLoop
= false;
1455 // we iterate until the next whitespace character
1456 // or until we reach the maximum of character per line
1457 // or until the end of the string to add.
1459 if (*aPos
== ' ' || *aPos
== '\t' || *aPos
== '\n') {
1460 foundWhitespaceInLoop
= true;
1466 } while ( (!mDoWrap
|| colPos
+ length
< mMaxColumn
) && aPos
< aEnd
);
1468 // in the case we don't reached the end of the string, but we reached the maxcolumn,
1469 // we see if there is a whitespace after the maxcolumn
1470 // if yes, then we can append directly the string instead of
1471 // appending a new line etc.
1472 if (*aPos
== ' ' || *aPos
== '\t' || *aPos
== '\n') {
1473 foundWhitespaceInLoop
= true;
1476 if (aPos
== aEnd
|| foundWhitespaceInLoop
) {
1477 // there is enough room for the complete block we found
1478 if (mDoFormat
&& !mColPos
) {
1479 AppendIndentation(aOutputStr
);
1481 else if (mAddSpace
) {
1482 aOutputStr
.Append(char16_t(' '));
1487 aOutputStr
.Append(aSequenceStart
, aPos
- aSequenceStart
);
1489 // We have not yet reached the max column, we will continue to
1490 // fill the current line in the next outer loop iteration
1491 // (this one in AppendToStringWrapped)
1492 // make sure we return in this outer loop
1493 onceAgainBecauseWeAddedBreakInFront
= false;
1495 else { // we reach the max column
1496 if (!thisSequenceStartsAtBeginningOfLine
&&
1497 (mAddSpace
|| (!mDoFormat
&& aSequenceStartAfterAWhiteSpace
))) {
1498 // when !mDoFormat, mAddSpace is not used, mAddSpace is always false
1499 // so, in the case where mDoWrap && !mDoFormat, if we want to enter in this condition...
1501 // We can avoid to wrap. We try to add the whole block
1502 // in an empty new line
1504 AppendNewLineToString(aOutputStr
);
1505 aPos
= aSequenceStart
;
1506 thisSequenceStartsAtBeginningOfLine
= true;
1507 onceAgainBecauseWeAddedBreakInFront
= true;
1511 onceAgainBecauseWeAddedBreakInFront
= false;
1512 bool foundWrapPosition
= false;
1513 int32_t wrapPosition
;
1515 nsILineBreaker
*lineBreaker
= nsContentUtils::LineBreaker();
1517 wrapPosition
= lineBreaker
->Prev(aSequenceStart
,
1518 (aEnd
- aSequenceStart
),
1519 (aPos
- aSequenceStart
) + 1);
1520 if (wrapPosition
!= NS_LINEBREAKER_NEED_MORE_TEXT
) {
1521 foundWrapPosition
= true;
1524 wrapPosition
= lineBreaker
->Next(aSequenceStart
,
1525 (aEnd
- aSequenceStart
),
1526 (aPos
- aSequenceStart
));
1527 if (wrapPosition
!= NS_LINEBREAKER_NEED_MORE_TEXT
) {
1528 foundWrapPosition
= true;
1532 if (foundWrapPosition
) {
1533 if (!mColPos
&& mDoFormat
) {
1534 AppendIndentation(aOutputStr
);
1536 else if (mAddSpace
) {
1537 aOutputStr
.Append(char16_t(' '));
1540 aOutputStr
.Append(aSequenceStart
, wrapPosition
);
1542 AppendNewLineToString(aOutputStr
);
1543 aPos
= aSequenceStart
+ wrapPosition
;
1544 aMayIgnoreStartOfLineWhitespaceSequence
= true;
1547 // try some simple fallback logic
1548 // go forward up to the next whitespace position,
1549 // in the worst case this will be all the rest of the data
1551 // we update the mColPos variable with the length of
1552 // the part already parsed.
1555 // now try to find the next whitespace
1557 if (*aPos
== ' ' || *aPos
== '\t' || *aPos
== '\n') {
1563 } while (aPos
< aEnd
);
1566 aOutputStr
.Append(char16_t(' '));
1569 aOutputStr
.Append(aSequenceStart
, aPos
- aSequenceStart
);
1572 aSequenceStartAfterAWhiteSpace
= false;
1574 } while (onceAgainBecauseWeAddedBreakInFront
);
1578 nsXMLContentSerializer::AppendToStringFormatedWrapped(const nsASingleFragmentString
& aStr
,
1579 nsAString
& aOutputStr
)
1581 if (mBodyOnly
&& !mInBody
) {
1585 nsASingleFragmentString::const_char_iterator pos
, end
, sequenceStart
;
1587 aStr
.BeginReading(pos
);
1588 aStr
.EndReading(end
);
1590 bool sequenceStartAfterAWhitespace
= false;
1592 nsAString::const_char_iterator end2
;
1593 aOutputStr
.EndReading(end2
);
1595 if (*end2
== ' ' || *end2
== '\n' || *end2
== '\t') {
1596 sequenceStartAfterAWhitespace
= true;
1600 // if the current line already has text on it, such as a tag,
1601 // leading whitespace is significant
1602 bool mayIgnoreStartOfLineWhitespaceSequence
=
1603 (!mColPos
|| (mIsIndentationAddedOnCurrentLine
&&
1604 sequenceStartAfterAWhitespace
&&
1605 uint32_t(mColPos
) == mIndent
.Length()));
1608 sequenceStart
= pos
;
1610 // if beginning of a whitespace sequence
1611 if (*pos
== ' ' || *pos
== '\n' || *pos
== '\t') {
1612 AppendFormatedWrapped_WhitespaceSequence(pos
, end
, sequenceStart
,
1613 mayIgnoreStartOfLineWhitespaceSequence
, aOutputStr
);
1615 else { // any other non-whitespace char
1616 AppendWrapped_NonWhitespaceSequence(pos
, end
, sequenceStart
,
1617 mayIgnoreStartOfLineWhitespaceSequence
, sequenceStartAfterAWhitespace
, aOutputStr
);
1623 nsXMLContentSerializer::AppendWrapped_WhitespaceSequence(
1624 nsASingleFragmentString::const_char_iterator
&aPos
,
1625 const nsASingleFragmentString::const_char_iterator aEnd
,
1626 const nsASingleFragmentString::const_char_iterator aSequenceStart
,
1627 nsAString
&aOutputStr
)
1629 // Handle the complete sequence of whitespace.
1630 // Continue to iterate until we find the first non-whitespace char.
1631 // Updates "aPos" to point to the first unhandled char.
1633 mIsIndentationAddedOnCurrentLine
= false;
1635 bool leaveLoop
= false;
1636 nsASingleFragmentString::const_char_iterator lastPos
= aPos
;
1642 // if there are too many spaces on a line, we wrap
1643 if (mColPos
>= mMaxColumn
) {
1644 if (lastPos
!= aPos
) {
1645 aOutputStr
.Append(lastPos
, aPos
- lastPos
);
1647 AppendToString(mLineBreak
, aOutputStr
);
1656 if (lastPos
!= aPos
) {
1657 aOutputStr
.Append(lastPos
, aPos
- lastPos
);
1659 AppendToString(mLineBreak
, aOutputStr
);
1668 } while (!leaveLoop
&& aPos
< aEnd
);
1670 if (lastPos
!= aPos
) {
1671 aOutputStr
.Append(lastPos
, aPos
- lastPos
);
1676 nsXMLContentSerializer::AppendToStringWrapped(const nsASingleFragmentString
& aStr
,
1677 nsAString
& aOutputStr
)
1679 if (mBodyOnly
&& !mInBody
) {
1683 nsASingleFragmentString::const_char_iterator pos
, end
, sequenceStart
;
1685 aStr
.BeginReading(pos
);
1686 aStr
.EndReading(end
);
1688 // not used in this case, but needed by AppendWrapped_NonWhitespaceSequence
1689 bool mayIgnoreStartOfLineWhitespaceSequence
= false;
1690 mMayIgnoreLineBreakSequence
= false;
1692 bool sequenceStartAfterAWhitespace
= false;
1693 if (pos
< end
&& !aOutputStr
.IsEmpty()) {
1694 nsAString::const_char_iterator end2
;
1695 aOutputStr
.EndReading(end2
);
1697 if (*end2
== ' ' || *end2
== '\n' || *end2
== '\t') {
1698 sequenceStartAfterAWhitespace
= true;
1703 sequenceStart
= pos
;
1705 // if beginning of a whitespace sequence
1706 if (*pos
== ' ' || *pos
== '\n' || *pos
== '\t') {
1707 sequenceStartAfterAWhitespace
= true;
1708 AppendWrapped_WhitespaceSequence(pos
, end
, sequenceStart
, aOutputStr
);
1710 else { // any other non-whitespace char
1711 AppendWrapped_NonWhitespaceSequence(pos
, end
, sequenceStart
,
1712 mayIgnoreStartOfLineWhitespaceSequence
, sequenceStartAfterAWhitespace
, aOutputStr
);
1718 nsXMLContentSerializer::ShouldMaintainPreLevel() const
1720 // Only attempt to maintain the pre level for consumers who care about it.
1721 return !mDoRaw
|| (mFlags
& nsIDocumentEncoder::OutputNoFormattingInPre
);