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