1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
8 * nsIContentSerializer implementation that can be used with an
9 * nsIDocumentEncoder to convert an XML DOM to an XML string that
10 * could be parsed into more or less the original DOM.
13 #ifndef nsXMLContentSerializer_h__
14 #define nsXMLContentSerializer_h__
16 #include "mozilla/Attributes.h"
17 #include "nsIContentSerializer.h"
18 #include "nsISupportsUtils.h"
23 #define kIndentStr NS_LITERAL_STRING(" ")
24 #define kEndTag NS_LITERAL_STRING("</")
33 class nsXMLContentSerializer
: public nsIContentSerializer
{
35 nsXMLContentSerializer();
39 NS_IMETHOD
Init(uint32_t flags
, uint32_t aWrapColumn
,
40 const mozilla::Encoding
* aEncoding
, bool aIsCopying
,
41 bool aRewriteEncodingDeclaration
,
42 bool* aNeedsPreformatScanning
) override
;
44 NS_IMETHOD
AppendText(nsIContent
* aText
, int32_t aStartOffset
,
45 int32_t aEndOffset
, nsAString
& aStr
) override
;
47 NS_IMETHOD
AppendCDATASection(nsIContent
* aCDATASection
, int32_t aStartOffset
,
48 int32_t aEndOffset
, nsAString
& aStr
) override
;
50 NS_IMETHOD
AppendProcessingInstruction(
51 mozilla::dom::ProcessingInstruction
* aPI
, int32_t aStartOffset
,
52 int32_t aEndOffset
, nsAString
& aStr
) override
;
54 NS_IMETHOD
AppendComment(mozilla::dom::Comment
* aComment
,
55 int32_t aStartOffset
, int32_t aEndOffset
,
56 nsAString
& aStr
) override
;
58 NS_IMETHOD
AppendDoctype(mozilla::dom::DocumentType
* aDoctype
,
59 nsAString
& aStr
) override
;
61 NS_IMETHOD
AppendElementStart(mozilla::dom::Element
* aElement
,
62 mozilla::dom::Element
* aOriginalElement
,
63 nsAString
& aStr
) override
;
65 NS_IMETHOD
AppendElementEnd(mozilla::dom::Element
* aElement
,
66 nsAString
& aStr
) override
;
68 NS_IMETHOD
Flush(nsAString
& aStr
) override
{ return NS_OK
; }
70 NS_IMETHOD
AppendDocumentStart(mozilla::dom::Document
* aDocument
,
71 nsAString
& aStr
) override
;
73 NS_IMETHOD
ScanElementForPreformat(mozilla::dom::Element
* aElement
) override
{
76 NS_IMETHOD
ForgetElementForPreformat(
77 mozilla::dom::Element
* aElement
) override
{
82 virtual ~nsXMLContentSerializer();
85 * Appends a char16_t character and increments the column position
88 bool AppendToString(const char16_t aChar
, nsAString
& aOutputStr
);
91 * Appends a nsAString string and increments the column position
94 bool AppendToString(const nsAString
& aStr
, nsAString
& aOutputStr
);
97 * Appends a string by replacing all line-endings
98 * by mLineBreak, except in the case of raw output.
99 * It increments the column position.
102 bool AppendToStringConvertLF(const nsAString
& aStr
, nsAString
& aOutputStr
);
105 * Appends a string by wrapping it when necessary.
106 * It updates the column position.
109 bool AppendToStringWrapped(const nsAString
& aStr
, nsAString
& aOutputStr
);
112 * Appends a string by formating and wrapping it when necessary
113 * It updates the column position.
116 bool AppendToStringFormatedWrapped(const nsAString
& aStr
,
117 nsAString
& aOutputStr
);
119 // used by AppendToStringWrapped
121 bool AppendWrapped_WhitespaceSequence(
122 nsAString::const_char_iterator
& aPos
,
123 const nsAString::const_char_iterator aEnd
,
124 const nsAString::const_char_iterator aSequenceStart
,
125 nsAString
& aOutputStr
);
127 // used by AppendToStringFormatedWrapped
129 bool AppendFormatedWrapped_WhitespaceSequence(
130 nsAString::const_char_iterator
& aPos
,
131 const nsAString::const_char_iterator aEnd
,
132 const nsAString::const_char_iterator aSequenceStart
,
133 bool& aMayIgnoreStartOfLineWhitespaceSequence
, nsAString
& aOutputStr
);
135 // used by AppendToStringWrapped and AppendToStringFormatedWrapped
137 bool AppendWrapped_NonWhitespaceSequence(
138 nsAString::const_char_iterator
& aPos
,
139 const nsAString::const_char_iterator aEnd
,
140 const nsAString::const_char_iterator aSequenceStart
,
141 bool& aMayIgnoreStartOfLineWhitespaceSequence
,
142 bool& aSequenceStartAfterAWhiteSpace
, nsAString
& aOutputStr
);
145 * add mLineBreak to the string
146 * It updates the column position and other flags.
149 bool AppendNewLineToString(nsAString
& aOutputStr
);
152 * Appends a string by translating entities
153 * It doesn't increment the column position
156 virtual bool AppendAndTranslateEntities(const nsAString
& aStr
,
157 nsAString
& aOutputStr
);
160 * Helper for virtual AppendAndTranslateEntities that does the actualy work.
162 * Do not call this directly. Call it via the template helper below.
166 static bool AppendAndTranslateEntities(const nsAString
& aStr
,
167 nsAString
& aOutputStr
,
168 const uint8_t aEntityTable
[],
169 uint16_t aMaxTableIndex
,
170 const char* const aStringTable
[]);
174 * Helper for calling AppendAndTranslateEntities in a way that guarantees we
175 * don't mess up our aEntityTable sizing. This is a bit more complicated than
176 * it could be, becaue sometimes we don't want to use all of aEntityTable, so
177 * we have to allow passing the amount to use independently. But we can
178 * statically ensure it's not too big.
180 * The first integer template argument, which callers need to specify
181 * explicitly, is the index of the last entry in aEntityTable that should be
182 * considered for encoding as an entity reference. The second integer
183 * argument will be deduced from the actual table passed in.
185 * aEntityTable contains as values indices into aStringTable. Those represent
186 * the strings that should be used to replace the characters that are used to
187 * index into aEntityTable. aStringTable[0] should be nullptr, and characters
188 * that do not need replacement should map to 0 in aEntityTable.
190 template <uint16_t LargestIndex
, uint16_t TableLength
>
191 MOZ_MUST_USE
bool AppendAndTranslateEntities(
192 const nsAString
& aStr
, nsAString
& aOutputStr
,
193 const uint8_t (&aEntityTable
)[TableLength
],
194 const char* const aStringTable
[]) {
195 static_assert(LargestIndex
< TableLength
,
196 "Largest allowed index must be smaller than table length");
197 return AppendAndTranslateEntities(aStr
, aOutputStr
, aEntityTable
,
198 LargestIndex
, aStringTable
);
202 * Max index that can be used with some of our entity tables.
204 static const uint16_t kGTVal
= 62;
207 * retrieve the text content of the node and append it to the given string
208 * It doesn't increment the column position
210 nsresult
AppendTextData(nsIContent
* aNode
, int32_t aStartOffset
,
211 int32_t aEndOffset
, nsAString
& aStr
,
212 bool aTranslateEntities
);
214 virtual nsresult
PushNameSpaceDecl(const nsAString
& aPrefix
,
215 const nsAString
& aURI
, nsIContent
* aOwner
);
216 void PopNameSpaceDeclsFor(nsIContent
* aOwner
);
219 * The problem that ConfirmPrefix fixes is that anyone can insert nodes
220 * through the DOM that have a namespace URI and a random or empty or
221 * previously existing prefix that's totally unrelated to the prefixes
222 * declared at that point through xmlns attributes. So what ConfirmPrefix
223 * does is ensure that we can map aPrefix to the namespace URI aURI (for
224 * example, that the prefix is not already mapped to some other namespace).
225 * aPrefix will be adjusted, if necessary, so the value of the prefix
226 * _after_ this call is what should be serialized.
227 * @param aPrefix the prefix that may need adjusting
228 * @param aURI the namespace URI we want aPrefix to point to
229 * @param aElement the element we're working with (needed for proper default
230 * namespace handling)
231 * @param aIsAttribute true if we're confirming a prefix for an attribute.
232 * @return true if we need to push the (prefix, uri) pair on the namespace
233 * stack (note that this can happen even if the prefix is
236 bool ConfirmPrefix(nsAString
& aPrefix
, const nsAString
& aURI
,
237 nsIContent
* aElement
, bool aIsAttribute
);
239 * GenerateNewPrefix generates a new prefix and writes it to aPrefix
241 void GenerateNewPrefix(nsAString
& aPrefix
);
243 uint32_t ScanNamespaceDeclarations(mozilla::dom::Element
* aContent
,
244 mozilla::dom::Element
* aOriginalElement
,
245 const nsAString
& aTagNamespaceURI
);
248 virtual bool SerializeAttributes(mozilla::dom::Element
* aContent
,
249 mozilla::dom::Element
* aOriginalElement
,
250 nsAString
& aTagPrefix
,
251 const nsAString
& aTagNamespaceURI
,
252 nsAtom
* aTagName
, nsAString
& aStr
,
253 uint32_t aSkipAttr
, bool aAddNSAttr
);
256 bool SerializeAttr(const nsAString
& aPrefix
, const nsAString
& aName
,
257 const nsAString
& aValue
, nsAString
& aStr
,
258 bool aDoEscapeEntities
);
260 bool IsJavaScript(nsIContent
* aContent
, nsAtom
* aAttrNameAtom
,
261 int32_t aAttrNamespaceID
, const nsAString
& aValueString
);
264 * This method can be redefined to check if the element can be serialized.
265 * It is called when the serialization of the start tag is asked
266 * (AppendElementStart)
267 * In this method you can also force the formating
268 * by setting aForceFormat to true.
269 * @return boolean true if the element can be output
271 virtual bool CheckElementStart(mozilla::dom::Element
* aElement
,
272 bool& aForceFormat
, nsAString
& aStr
,
276 * This method is responsible for appending the '>' at the end of the start
277 * tag, possibly preceded by '/' and maybe a ' ' before that too.
279 * aElement and aOriginalElement are the same as the corresponding arguments
280 * to AppendElementStart.
283 bool AppendEndOfElementStart(mozilla::dom::Element
* aEleemnt
,
284 mozilla::dom::Element
* aOriginalElement
,
288 * This method can be redefine to serialize additional things just after
289 * after the serialization ot the start tag.
290 * (called at the end of AppendElementStart)
293 virtual bool AfterElementStart(nsIContent
* aContent
,
294 nsIContent
* aOriginalElement
,
300 * This method can be redefined to check if the element can be serialized.
301 * It is called when the serialization of the end tag is asked
303 * In this method you can also force the formating
304 * by setting aForceFormat to true.
305 * @return boolean true if the element can be output
307 virtual bool CheckElementEnd(mozilla::dom::Element
* aElement
,
308 bool& aForceFormat
, nsAString
& aStr
);
311 * This method can be redefine to serialize additional things just after
312 * after the serialization ot the end tag.
313 * (called at the end of AppendElementStart)
315 virtual void AfterElementEnd(nsIContent
* aContent
, nsAString
& aStr
){};
318 * Returns true if a line break should be inserted before an element open tag
320 virtual bool LineBreakBeforeOpen(int32_t aNamespaceID
, nsAtom
* aName
);
323 * Returns true if a line break should be inserted after an element open tag
325 virtual bool LineBreakAfterOpen(int32_t aNamespaceID
, nsAtom
* aName
);
328 * Returns true if a line break should be inserted after an element close tag
330 virtual bool LineBreakBeforeClose(int32_t aNamespaceID
, nsAtom
* aName
);
333 * Returns true if a line break should be inserted after an element close tag
335 virtual bool LineBreakAfterClose(int32_t aNamespaceID
, nsAtom
* aName
);
338 * add intendation. Call only in the case of formating and if the current
339 * position is at 0. It updates the column position.
342 bool AppendIndentation(nsAString
& aStr
);
345 bool IncrIndentation(nsAtom
* aName
);
346 void DecrIndentation(nsAtom
* aName
);
348 // Functions to check for newlines that needs to be added between nodes in
349 // the root of a document. See mAddNewlineForRootNode
351 bool MaybeAddNewlineForRootNode(nsAString
& aStr
);
352 void MaybeFlagNewlineForRootNode(nsINode
* aNode
);
354 // Functions to check if we enter in or leave from a preformated content
355 virtual void MaybeEnterInPreContent(nsIContent
* aNode
);
356 virtual void MaybeLeaveFromPreContent(nsIContent
* aNode
);
358 bool ShouldMaintainPreLevel() const;
359 int32_t PreLevel() const {
360 MOZ_ASSERT(ShouldMaintainPreLevel());
363 int32_t& PreLevel() {
364 MOZ_ASSERT(ShouldMaintainPreLevel());
368 bool MaybeSerializeIsValue(mozilla::dom::Element
* aElement
, nsAString
& aStr
);
370 int32_t mPrefixIndex
;
372 struct NameSpaceDecl
{
378 nsTArray
<NameSpaceDecl
> mNameSpaceStack
;
380 // nsIDocumentEncoder flags
381 MOZ_INIT_OUTSIDE_CTOR
uint32_t mFlags
;
383 // characters to use for line break
386 // The charset that was passed to Init()
389 // current column position on the current line
392 // true = pretty formating should be done (OutputFormated flag)
393 MOZ_INIT_OUTSIDE_CTOR
bool mDoFormat
;
395 // true = no formatting,(OutputRaw flag)
396 // no newline convertion and no rewrap long lines even if OutputWrap is set.
397 MOZ_INIT_OUTSIDE_CTOR
bool mDoRaw
;
399 // true = wrapping should be done (OutputWrap flag)
400 MOZ_INIT_OUTSIDE_CTOR
bool mDoWrap
;
402 // true = we can break lines (OutputDisallowLineBreaking flag)
403 MOZ_INIT_OUTSIDE_CTOR
bool mAllowLineBreaking
;
405 // number of maximum column in a line, in the wrap mode
406 MOZ_INIT_OUTSIDE_CTOR
uint32_t mMaxColumn
;
408 // current indent value
411 // this is the indentation level after the indentation reached
412 // the maximum length of indentation
413 int32_t mIndentOverflow
;
415 // says if the indentation has been already added on the current line
416 bool mIsIndentationAddedOnCurrentLine
;
418 // the string which is currently added is in an attribute
421 // true = a newline character should be added. It's only
422 // useful when serializing root nodes. see MaybeAddNewlineForRootNode and
423 // MaybeFlagNewlineForRootNode
424 bool mAddNewlineForRootNode
;
426 // Indicates that a space will be added if and only if content is
427 // continued on the same line while serializing source. Otherwise,
428 // the newline character acts as the whitespace and no space is needed.
429 // used when mDoFormat = true
432 // says that if the next string to add contains a newline character at the
433 // begining, then this newline character should be ignored, because a
434 // such character has already been added into the output string
435 bool mMayIgnoreLineBreakSequence
;
441 // number of nested elements which have preformated content
442 MOZ_INIT_OUTSIDE_CTOR
int32_t mPreLevel
;
445 nsresult
NS_NewXMLContentSerializer(nsIContentSerializer
** aSerializer
);