Bug 1838729 - test(webgpu): accept observed intermittents in `backlog`
[gecko.git] / dom / serializers / nsXMLContentSerializer.h
blobe3c95e7ee43440f00874193c3d3698518a9e1276
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/. */
7 /*
8 * nsIContentSerializer implementation that can be used with an
9 * nsIDocumentEncoder to convert an XML DOM to an XML string that
10 * could be parsed into more or less the original DOM.
13 #ifndef nsXMLContentSerializer_h__
14 #define nsXMLContentSerializer_h__
16 #include "mozilla/Attributes.h"
17 #include "nsIContentSerializer.h"
18 #include "nsISupportsUtils.h"
19 #include "nsCOMPtr.h"
20 #include "nsTArray.h"
21 #include "nsString.h"
23 #define kIndentStr u" "_ns
24 #define kEndTag u"</"_ns
26 class nsAtom;
27 class nsINode;
29 namespace mozilla {
30 class Encoding;
33 class nsXMLContentSerializer : public nsIContentSerializer {
34 public:
35 nsXMLContentSerializer();
37 NS_DECL_ISUPPORTS
39 NS_IMETHOD Init(uint32_t flags, uint32_t aWrapColumn,
40 const mozilla::Encoding* aEncoding, bool aIsCopying,
41 bool aRewriteEncodingDeclaration,
42 bool* aNeedsPreformatScanning, nsAString& aOutput) override;
44 NS_IMETHOD AppendText(nsIContent* aText, int32_t aStartOffset,
45 int32_t aEndOffset) override;
47 NS_IMETHOD AppendCDATASection(nsIContent* aCDATASection, int32_t aStartOffset,
48 int32_t aEndOffset) override;
50 NS_IMETHOD AppendProcessingInstruction(
51 mozilla::dom::ProcessingInstruction* aPI, int32_t aStartOffset,
52 int32_t aEndOffset) override;
54 NS_IMETHOD AppendComment(mozilla::dom::Comment* aComment,
55 int32_t aStartOffset, int32_t aEndOffset) override;
57 NS_IMETHOD AppendDoctype(mozilla::dom::DocumentType* aDoctype) override;
59 NS_IMETHOD AppendElementStart(
60 mozilla::dom::Element* aElement,
61 mozilla::dom::Element* aOriginalElement) override;
63 NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement,
64 mozilla::dom::Element* aOriginalElement) override;
66 NS_IMETHOD FlushAndFinish() override { return NS_OK; }
68 NS_IMETHOD Finish() override;
70 NS_IMETHOD GetOutputLength(uint32_t& aLength) const override;
72 NS_IMETHOD AppendDocumentStart(mozilla::dom::Document* aDocument) override;
74 NS_IMETHOD ScanElementForPreformat(mozilla::dom::Element* aElement) override {
75 return NS_OK;
77 NS_IMETHOD ForgetElementForPreformat(
78 mozilla::dom::Element* aElement) override {
79 return NS_OK;
82 protected:
83 virtual ~nsXMLContentSerializer();
85 /**
86 * Appends a char16_t character and increments the column position
88 [[nodiscard]] bool AppendToString(const char16_t aChar,
89 nsAString& aOutputStr);
91 /**
92 * Appends a nsAString string and increments the column position
94 [[nodiscard]] bool AppendToString(const nsAString& aStr,
95 nsAString& aOutputStr);
97 /**
98 * Appends a string by replacing all line-endings
99 * by mLineBreak, except in the case of raw output.
100 * It increments the column position.
102 [[nodiscard]] bool AppendToStringConvertLF(const nsAString& aStr,
103 nsAString& aOutputStr);
106 * Appends a string by wrapping it when necessary.
107 * It updates the column position.
109 [[nodiscard]] bool AppendToStringWrapped(const nsAString& aStr,
110 nsAString& aOutputStr);
113 * Appends a string by formating and wrapping it when necessary
114 * It updates the column position.
116 [[nodiscard]] bool AppendToStringFormatedWrapped(const nsAString& aStr,
117 nsAString& aOutputStr);
119 // used by AppendToStringWrapped
120 [[nodiscard]] bool AppendWrapped_WhitespaceSequence(
121 nsAString::const_char_iterator& aPos,
122 const nsAString::const_char_iterator aEnd,
123 const nsAString::const_char_iterator aSequenceStart,
124 nsAString& aOutputStr);
126 // used by AppendToStringFormatedWrapped
127 [[nodiscard]] bool AppendFormatedWrapped_WhitespaceSequence(
128 nsAString::const_char_iterator& aPos,
129 const nsAString::const_char_iterator aEnd,
130 const nsAString::const_char_iterator aSequenceStart,
131 bool& aMayIgnoreStartOfLineWhitespaceSequence, nsAString& aOutputStr);
133 // used by AppendToStringWrapped and AppendToStringFormatedWrapped
134 [[nodiscard]] bool AppendWrapped_NonWhitespaceSequence(
135 nsAString::const_char_iterator& aPos,
136 const nsAString::const_char_iterator aEnd,
137 const nsAString::const_char_iterator aSequenceStart,
138 bool& aMayIgnoreStartOfLineWhitespaceSequence,
139 bool& aSequenceStartAfterAWhiteSpace, nsAString& aOutputStr);
142 * add mLineBreak to the string
143 * It updates the column position and other flags.
145 [[nodiscard]] bool AppendNewLineToString(nsAString& aOutputStr);
148 * Appends a string by translating entities
149 * It doesn't increment the column position
151 [[nodiscard]] virtual bool AppendAndTranslateEntities(const nsAString& aStr,
152 nsAString& aOutputStr);
155 * Helper for virtual AppendAndTranslateEntities that does the actualy work.
157 * Do not call this directly. Call it via the template helper below.
159 private:
160 [[nodiscard]] static bool AppendAndTranslateEntities(
161 const nsAString& aStr, nsAString& aOutputStr,
162 const uint8_t aEntityTable[], uint16_t aMaxTableIndex,
163 const char* const aStringTable[]);
165 protected:
167 * Helper for calling AppendAndTranslateEntities in a way that guarantees we
168 * don't mess up our aEntityTable sizing. This is a bit more complicated than
169 * it could be, becaue sometimes we don't want to use all of aEntityTable, so
170 * we have to allow passing the amount to use independently. But we can
171 * statically ensure it's not too big.
173 * The first integer template argument, which callers need to specify
174 * explicitly, is the index of the last entry in aEntityTable that should be
175 * considered for encoding as an entity reference. The second integer
176 * argument will be deduced from the actual table passed in.
178 * aEntityTable contains as values indices into aStringTable. Those represent
179 * the strings that should be used to replace the characters that are used to
180 * index into aEntityTable. aStringTable[0] should be nullptr, and characters
181 * that do not need replacement should map to 0 in aEntityTable.
183 template <uint16_t LargestIndex, uint16_t TableLength>
184 [[nodiscard]] bool AppendAndTranslateEntities(
185 const nsAString& aStr, nsAString& aOutputStr,
186 const uint8_t (&aEntityTable)[TableLength],
187 const char* const aStringTable[]) {
188 static_assert(LargestIndex < TableLength,
189 "Largest allowed index must be smaller than table length");
190 return AppendAndTranslateEntities(aStr, aOutputStr, aEntityTable,
191 LargestIndex, aStringTable);
195 * Max index that can be used with some of our entity tables.
197 static const uint16_t kGTVal = 62;
200 * retrieve the text content of the node and append it to the given string
201 * It doesn't increment the column position
203 nsresult AppendTextData(nsIContent* aNode, int32_t aStartOffset,
204 int32_t aEndOffset, nsAString& aStr,
205 bool aTranslateEntities);
207 virtual nsresult PushNameSpaceDecl(const nsAString& aPrefix,
208 const nsAString& aURI, nsIContent* aOwner);
209 void PopNameSpaceDeclsFor(nsIContent* aOwner);
212 * The problem that ConfirmPrefix fixes is that anyone can insert nodes
213 * through the DOM that have a namespace URI and a random or empty or
214 * previously existing prefix that's totally unrelated to the prefixes
215 * declared at that point through xmlns attributes. So what ConfirmPrefix
216 * does is ensure that we can map aPrefix to the namespace URI aURI (for
217 * example, that the prefix is not already mapped to some other namespace).
218 * aPrefix will be adjusted, if necessary, so the value of the prefix
219 * _after_ this call is what should be serialized.
220 * @param aPrefix the prefix that may need adjusting
221 * @param aURI the namespace URI we want aPrefix to point to
222 * @param aElement the element we're working with (needed for proper default
223 * namespace handling)
224 * @param aIsAttribute true if we're confirming a prefix for an attribute.
225 * @return true if we need to push the (prefix, uri) pair on the namespace
226 * stack (note that this can happen even if the prefix is
227 * empty).
229 bool ConfirmPrefix(nsAString& aPrefix, const nsAString& aURI,
230 nsIContent* aElement, bool aIsAttribute);
232 * GenerateNewPrefix generates a new prefix and writes it to aPrefix
234 void GenerateNewPrefix(nsAString& aPrefix);
236 uint32_t ScanNamespaceDeclarations(mozilla::dom::Element* aContent,
237 mozilla::dom::Element* aOriginalElement,
238 const nsAString& aTagNamespaceURI);
240 [[nodiscard]] virtual bool SerializeAttributes(
241 mozilla::dom::Element* aContent, mozilla::dom::Element* aOriginalElement,
242 nsAString& aTagPrefix, const nsAString& aTagNamespaceURI,
243 nsAtom* aTagName, nsAString& aStr, uint32_t aSkipAttr, bool aAddNSAttr);
245 [[nodiscard]] bool SerializeAttr(const nsAString& aPrefix,
246 const nsAString& aName,
247 const nsAString& aValue, nsAString& aStr,
248 bool aDoEscapeEntities);
250 bool IsJavaScript(nsIContent* aContent, nsAtom* aAttrNameAtom,
251 int32_t aAttrNamespaceID, const nsAString& aValueString);
254 * This method can be redefined to check if the element can be serialized.
255 * It is called when the serialization of the start tag is asked
256 * (AppendElementStart)
257 * In this method you can also force the formating
258 * by setting aForceFormat to true.
259 * @return boolean true if the element can be output
261 virtual bool CheckElementStart(mozilla::dom::Element* aElement,
262 bool& aForceFormat, nsAString& aStr,
263 nsresult& aResult);
266 * This method is responsible for appending the '>' at the end of the start
267 * tag, possibly preceded by '/' and maybe a ' ' before that too.
269 * aElement and aOriginalElement are the same as the corresponding arguments
270 * to AppendElementStart.
272 [[nodiscard]] bool AppendEndOfElementStart(
273 mozilla::dom::Element* aEleemnt, mozilla::dom::Element* aOriginalElement,
274 nsAString& aStr);
277 * This method can be redefine to serialize additional things just after
278 * the serialization of the start tag.
279 * (called at the end of AppendElementStart)
281 [[nodiscard]] virtual bool AfterElementStart(nsIContent* aContent,
282 nsIContent* aOriginalElement,
283 nsAString& aStr) {
284 return true;
288 * This method can be redefined to check if the element can be serialized.
289 * It is called when the serialization of the end tag is asked
290 * (AppendElementEnd)
291 * In this method you can also force the formating
292 * by setting aForceFormat to true.
293 * @return boolean true if the element can be output
295 virtual bool CheckElementEnd(mozilla::dom::Element* aElement,
296 mozilla::dom::Element* aOriginalElement,
297 bool& aForceFormat, nsAString& aStr);
300 * This method can be redefine to serialize additional things just after
301 * the serialization of the end tag.
302 * (called at the end of AppendElementStart)
304 virtual void AfterElementEnd(nsIContent* aContent, nsAString& aStr) {};
307 * Returns true if a line break should be inserted before an element open tag
309 virtual bool LineBreakBeforeOpen(int32_t aNamespaceID, nsAtom* aName);
312 * Returns true if a line break should be inserted after an element open tag
314 virtual bool LineBreakAfterOpen(int32_t aNamespaceID, nsAtom* aName);
317 * Returns true if a line break should be inserted after an element close tag
319 virtual bool LineBreakBeforeClose(int32_t aNamespaceID, nsAtom* aName);
322 * Returns true if a line break should be inserted after an element close tag
324 virtual bool LineBreakAfterClose(int32_t aNamespaceID, nsAtom* aName);
327 * add intendation. Call only in the case of formating and if the current
328 * position is at 0. It updates the column position.
330 [[nodiscard]] bool AppendIndentation(nsAString& aStr);
332 [[nodiscard]] bool IncrIndentation(nsAtom* aName);
333 void DecrIndentation(nsAtom* aName);
335 // Functions to check for newlines that needs to be added between nodes in
336 // the root of a document. See mAddNewlineForRootNode
337 [[nodiscard]] bool MaybeAddNewlineForRootNode(nsAString& aStr);
338 void MaybeFlagNewlineForRootNode(nsINode* aNode);
340 // Functions to check if we enter in or leave from a preformated content
341 virtual void MaybeEnterInPreContent(nsIContent* aNode);
342 virtual void MaybeLeaveFromPreContent(nsIContent* aNode);
344 bool ShouldMaintainPreLevel() const;
345 int32_t PreLevel() const {
346 MOZ_ASSERT(ShouldMaintainPreLevel());
347 return mPreLevel;
349 int32_t& PreLevel() {
350 MOZ_ASSERT(ShouldMaintainPreLevel());
351 return mPreLevel;
354 bool MaybeSerializeIsValue(mozilla::dom::Element* aElement, nsAString& aStr);
356 int32_t mPrefixIndex;
358 struct NameSpaceDecl {
359 nsString mPrefix;
360 nsString mURI;
361 nsIContent* mOwner;
364 nsTArray<NameSpaceDecl> mNameSpaceStack;
366 // nsIDocumentEncoder flags
367 MOZ_INIT_OUTSIDE_CTOR uint32_t mFlags;
369 // characters to use for line break
370 nsString mLineBreak;
372 // The charset that was passed to Init()
373 nsCString mCharset;
375 // current column position on the current line
376 uint32_t mColPos;
378 // true = pretty formating should be done (OutputFormated flag)
379 MOZ_INIT_OUTSIDE_CTOR bool mDoFormat;
381 // true = no formatting,(OutputRaw flag)
382 // no newline convertion and no rewrap long lines even if OutputWrap is set.
383 MOZ_INIT_OUTSIDE_CTOR bool mDoRaw;
385 // true = wrapping should be done (OutputWrap flag)
386 MOZ_INIT_OUTSIDE_CTOR bool mDoWrap;
388 // true = we can break lines (OutputDisallowLineBreaking flag)
389 MOZ_INIT_OUTSIDE_CTOR bool mAllowLineBreaking;
391 // number of maximum column in a line, in the wrap mode
392 MOZ_INIT_OUTSIDE_CTOR uint32_t mMaxColumn;
394 // current indent value
395 nsString mIndent;
397 // this is the indentation level after the indentation reached
398 // the maximum length of indentation
399 int32_t mIndentOverflow;
401 // says if the indentation has been already added on the current line
402 bool mIsIndentationAddedOnCurrentLine;
404 // the string which is currently added is in an attribute
405 bool mInAttribute;
407 // true = a newline character should be added. It's only
408 // useful when serializing root nodes. see MaybeAddNewlineForRootNode and
409 // MaybeFlagNewlineForRootNode
410 bool mAddNewlineForRootNode;
412 // Indicates that a space will be added if and only if content is
413 // continued on the same line while serializing source. Otherwise,
414 // the newline character acts as the whitespace and no space is needed.
415 // used when mDoFormat = true
416 bool mAddSpace;
418 // says that if the next string to add contains a newline character at the
419 // begining, then this newline character should be ignored, because a
420 // such character has already been added into the output string
421 bool mMayIgnoreLineBreakSequence;
423 bool mBodyOnly;
424 int32_t mInBody;
426 // Non-owning.
427 nsAString* mOutput;
429 private:
430 // number of nested elements which have preformated content
431 MOZ_INIT_OUTSIDE_CTOR int32_t mPreLevel;
433 static const uint8_t kEntities[];
434 static const uint8_t kAttrEntities[];
435 static const char* const kEntityStrings[];
438 nsresult NS_NewXMLContentSerializer(nsIContentSerializer** aSerializer);
440 #endif