/src/mozilla-central/dom/base/nsXMLContentSerializer.cpp
Line | Count | Source (jump to first uncovered line) |
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/. */ |
6 | | |
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. |
11 | | */ |
12 | | |
13 | | #include "nsXMLContentSerializer.h" |
14 | | |
15 | | #include "nsGkAtoms.h" |
16 | | #include "nsIContent.h" |
17 | | #include "nsIContentInlines.h" |
18 | | #include "nsIDocument.h" |
19 | | #include "nsIDocumentEncoder.h" |
20 | | #include "nsElementTable.h" |
21 | | #include "nsNameSpaceManager.h" |
22 | | #include "nsTextFragment.h" |
23 | | #include "nsString.h" |
24 | | #include "mozilla/Sprintf.h" |
25 | | #include "nsUnicharUtils.h" |
26 | | #include "nsCRT.h" |
27 | | #include "nsContentUtils.h" |
28 | | #include "nsAttrName.h" |
29 | | #include "mozilla/dom/Comment.h" |
30 | | #include "mozilla/dom/CustomElementRegistry.h" |
31 | | #include "mozilla/dom/DocumentType.h" |
32 | | #include "mozilla/dom/Element.h" |
33 | | #include "mozilla/dom/ProcessingInstruction.h" |
34 | | #include "mozilla/intl/LineBreaker.h" |
35 | | #include "nsParserConstants.h" |
36 | | #include "mozilla/Encoding.h" |
37 | | |
38 | | using namespace mozilla; |
39 | | using namespace mozilla::dom; |
40 | | |
41 | 0 | #define kXMLNS "xmlns" |
42 | | |
43 | | // to be readable, we assume that an indented line contains |
44 | | // at least this number of characters (arbitrary value here). |
45 | | // This is a limit for the indentation. |
46 | 0 | #define MIN_INDENTED_LINE_LENGTH 15 |
47 | | |
48 | | // the string used to indent. |
49 | 0 | #define INDENT_STRING " " |
50 | 0 | #define INDENT_STRING_LENGTH 2 |
51 | | |
52 | | nsresult |
53 | | NS_NewXMLContentSerializer(nsIContentSerializer** aSerializer) |
54 | 0 | { |
55 | 0 | RefPtr<nsXMLContentSerializer> it = new nsXMLContentSerializer(); |
56 | 0 | it.forget(aSerializer); |
57 | 0 | return NS_OK; |
58 | 0 | } |
59 | | |
60 | | nsXMLContentSerializer::nsXMLContentSerializer() |
61 | | : mPrefixIndex(0), |
62 | | mColPos(0), |
63 | | mIndentOverflow(0), |
64 | | mIsIndentationAddedOnCurrentLine(false), |
65 | | mInAttribute(false), |
66 | | mAddNewlineForRootNode(false), |
67 | | mAddSpace(false), |
68 | | mMayIgnoreLineBreakSequence(false), |
69 | | mBodyOnly(false), |
70 | | mInBody(0) |
71 | 0 | { |
72 | 0 | } |
73 | | |
74 | | nsXMLContentSerializer::~nsXMLContentSerializer() |
75 | 0 | { |
76 | 0 | } |
77 | | |
78 | | NS_IMPL_ISUPPORTS(nsXMLContentSerializer, nsIContentSerializer) |
79 | | |
80 | | NS_IMETHODIMP |
81 | | nsXMLContentSerializer::Init(uint32_t aFlags, |
82 | | uint32_t aWrapColumn, |
83 | | const Encoding* aEncoding, |
84 | | bool aIsCopying, |
85 | | bool aRewriteEncodingDeclaration, |
86 | | bool* aNeedsPreformatScanning) |
87 | 0 | { |
88 | 0 | *aNeedsPreformatScanning = false; |
89 | 0 | mPrefixIndex = 0; |
90 | 0 | mColPos = 0; |
91 | 0 | mIndentOverflow = 0; |
92 | 0 | mIsIndentationAddedOnCurrentLine = false; |
93 | 0 | mInAttribute = false; |
94 | 0 | mAddNewlineForRootNode = false; |
95 | 0 | mAddSpace = false; |
96 | 0 | mMayIgnoreLineBreakSequence = false; |
97 | 0 | mBodyOnly = false; |
98 | 0 | mInBody = 0; |
99 | 0 |
|
100 | 0 | if (aEncoding) { |
101 | 0 | aEncoding->Name(mCharset); |
102 | 0 | } |
103 | 0 | mFlags = aFlags; |
104 | 0 |
|
105 | 0 | // Set the line break character: |
106 | 0 | if ((mFlags & nsIDocumentEncoder::OutputCRLineBreak) |
107 | 0 | && (mFlags & nsIDocumentEncoder::OutputLFLineBreak)) { // Windows |
108 | 0 | mLineBreak.AssignLiteral("\r\n"); |
109 | 0 | } |
110 | 0 | else if (mFlags & nsIDocumentEncoder::OutputCRLineBreak) { // Mac |
111 | 0 | mLineBreak.Assign('\r'); |
112 | 0 | } |
113 | 0 | else if (mFlags & nsIDocumentEncoder::OutputLFLineBreak) { // Unix/DOM |
114 | 0 | mLineBreak.Assign('\n'); |
115 | 0 | } |
116 | 0 | else { |
117 | 0 | mLineBreak.AssignLiteral(NS_LINEBREAK); // Platform/default |
118 | 0 | } |
119 | 0 |
|
120 | 0 | mDoRaw = !!(mFlags & nsIDocumentEncoder::OutputRaw); |
121 | 0 |
|
122 | 0 | mDoFormat = (mFlags & nsIDocumentEncoder::OutputFormatted && !mDoRaw); |
123 | 0 |
|
124 | 0 | mDoWrap = (mFlags & nsIDocumentEncoder::OutputWrap && !mDoRaw); |
125 | 0 |
|
126 | 0 | mAllowLineBreaking = !(mFlags & nsIDocumentEncoder::OutputDisallowLineBreaking); |
127 | 0 |
|
128 | 0 | if (!aWrapColumn) { |
129 | 0 | mMaxColumn = 72; |
130 | 0 | } |
131 | 0 | else { |
132 | 0 | mMaxColumn = aWrapColumn; |
133 | 0 | } |
134 | 0 |
|
135 | 0 | mPreLevel = 0; |
136 | 0 | mIsIndentationAddedOnCurrentLine = false; |
137 | 0 | return NS_OK; |
138 | 0 | } |
139 | | |
140 | | nsresult |
141 | | nsXMLContentSerializer::AppendTextData(nsIContent* aNode, |
142 | | int32_t aStartOffset, |
143 | | int32_t aEndOffset, |
144 | | nsAString& aStr, |
145 | | bool aTranslateEntities) |
146 | 0 | { |
147 | 0 | nsIContent* content = aNode; |
148 | 0 | const nsTextFragment* frag; |
149 | 0 | if (!content || !(frag = content->GetText())) { |
150 | 0 | return NS_ERROR_FAILURE; |
151 | 0 | } |
152 | 0 | |
153 | 0 | int32_t fragLength = frag->GetLength(); |
154 | 0 | int32_t endoffset = (aEndOffset == -1) ? fragLength : std::min(aEndOffset, fragLength); |
155 | 0 | int32_t length = endoffset - aStartOffset; |
156 | 0 |
|
157 | 0 | NS_ASSERTION(aStartOffset >= 0, "Negative start offset for text fragment!"); |
158 | 0 | NS_ASSERTION(aStartOffset <= endoffset, "A start offset is beyond the end of the text fragment!"); |
159 | 0 |
|
160 | 0 | if (length <= 0) { |
161 | 0 | // XXX Zero is a legal value, maybe non-zero values should be an |
162 | 0 | // error. |
163 | 0 | return NS_OK; |
164 | 0 | } |
165 | 0 | |
166 | 0 | if (frag->Is2b()) { |
167 | 0 | const char16_t *strStart = frag->Get2b() + aStartOffset; |
168 | 0 | if (aTranslateEntities) { |
169 | 0 | NS_ENSURE_TRUE(AppendAndTranslateEntities(Substring(strStart, strStart + length), aStr), |
170 | 0 | NS_ERROR_OUT_OF_MEMORY); |
171 | 0 | } |
172 | 0 | else { |
173 | 0 | NS_ENSURE_TRUE(aStr.Append(Substring(strStart, strStart + length), mozilla::fallible), |
174 | 0 | NS_ERROR_OUT_OF_MEMORY); |
175 | 0 | } |
176 | 0 | } |
177 | 0 | else { |
178 | 0 | if (aTranslateEntities) { |
179 | 0 | NS_ENSURE_TRUE(AppendAndTranslateEntities(NS_ConvertASCIItoUTF16(frag->Get1b()+aStartOffset, length), aStr), |
180 | 0 | NS_ERROR_OUT_OF_MEMORY); |
181 | 0 | } |
182 | 0 | else { |
183 | 0 | NS_ENSURE_TRUE(aStr.Append(NS_ConvertASCIItoUTF16(frag->Get1b()+aStartOffset, length), mozilla::fallible), |
184 | 0 | NS_ERROR_OUT_OF_MEMORY); |
185 | 0 | } |
186 | 0 | } |
187 | 0 |
|
188 | 0 | return NS_OK; |
189 | 0 | } |
190 | | |
191 | | NS_IMETHODIMP |
192 | | nsXMLContentSerializer::AppendText(nsIContent* aText, |
193 | | int32_t aStartOffset, |
194 | | int32_t aEndOffset, |
195 | | nsAString& aStr) |
196 | 0 | { |
197 | 0 | NS_ENSURE_ARG(aText); |
198 | 0 |
|
199 | 0 | nsAutoString data; |
200 | 0 | nsresult rv; |
201 | 0 |
|
202 | 0 | rv = AppendTextData(aText, aStartOffset, aEndOffset, data, true); |
203 | 0 | if (NS_FAILED(rv)) |
204 | 0 | return NS_ERROR_FAILURE; |
205 | 0 | |
206 | 0 | if (mDoRaw || PreLevel() > 0) { |
207 | 0 | NS_ENSURE_TRUE(AppendToStringConvertLF(data, aStr), NS_ERROR_OUT_OF_MEMORY); |
208 | 0 | } |
209 | 0 | else if (mDoFormat) { |
210 | 0 | NS_ENSURE_TRUE(AppendToStringFormatedWrapped(data, aStr), NS_ERROR_OUT_OF_MEMORY); |
211 | 0 | } |
212 | 0 | else if (mDoWrap) { |
213 | 0 | NS_ENSURE_TRUE(AppendToStringWrapped(data, aStr), NS_ERROR_OUT_OF_MEMORY); |
214 | 0 | } |
215 | 0 | else { |
216 | 0 | NS_ENSURE_TRUE(AppendToStringConvertLF(data, aStr), NS_ERROR_OUT_OF_MEMORY); |
217 | 0 | } |
218 | 0 |
|
219 | 0 | return NS_OK; |
220 | 0 | } |
221 | | |
222 | | NS_IMETHODIMP |
223 | | nsXMLContentSerializer::AppendCDATASection(nsIContent* aCDATASection, |
224 | | int32_t aStartOffset, |
225 | | int32_t aEndOffset, |
226 | | nsAString& aStr) |
227 | 0 | { |
228 | 0 | NS_ENSURE_ARG(aCDATASection); |
229 | 0 | nsresult rv; |
230 | 0 |
|
231 | 0 | NS_NAMED_LITERAL_STRING(cdata , "<![CDATA["); |
232 | 0 |
|
233 | 0 | if (mDoRaw || PreLevel() > 0) { |
234 | 0 | NS_ENSURE_TRUE(AppendToString(cdata, aStr), NS_ERROR_OUT_OF_MEMORY); |
235 | 0 | } |
236 | 0 | else if (mDoFormat) { |
237 | 0 | NS_ENSURE_TRUE(AppendToStringFormatedWrapped(cdata, aStr), NS_ERROR_OUT_OF_MEMORY); |
238 | 0 | } |
239 | 0 | else if (mDoWrap) { |
240 | 0 | NS_ENSURE_TRUE(AppendToStringWrapped(cdata, aStr), NS_ERROR_OUT_OF_MEMORY); |
241 | 0 | } |
242 | 0 | else { |
243 | 0 | NS_ENSURE_TRUE(AppendToString(cdata, aStr), NS_ERROR_OUT_OF_MEMORY); |
244 | 0 | } |
245 | 0 |
|
246 | 0 | nsAutoString data; |
247 | 0 | rv = AppendTextData(aCDATASection, aStartOffset, aEndOffset, data, false); |
248 | 0 | if (NS_FAILED(rv)) return NS_ERROR_FAILURE; |
249 | 0 | |
250 | 0 | NS_ENSURE_TRUE(AppendToStringConvertLF(data, aStr), NS_ERROR_OUT_OF_MEMORY); |
251 | 0 |
|
252 | 0 | NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING("]]>"), aStr), NS_ERROR_OUT_OF_MEMORY); |
253 | 0 |
|
254 | 0 | return NS_OK; |
255 | 0 | } |
256 | | |
257 | | NS_IMETHODIMP |
258 | | nsXMLContentSerializer::AppendProcessingInstruction(ProcessingInstruction* aPI, |
259 | | int32_t aStartOffset, |
260 | | int32_t aEndOffset, |
261 | | nsAString& aStr) |
262 | 0 | { |
263 | 0 | nsAutoString target, data, start; |
264 | 0 |
|
265 | 0 | NS_ENSURE_TRUE(MaybeAddNewlineForRootNode(aStr), NS_ERROR_OUT_OF_MEMORY); |
266 | 0 |
|
267 | 0 | aPI->GetTarget(target); |
268 | 0 |
|
269 | 0 | aPI->GetData(data); |
270 | 0 |
|
271 | 0 | NS_ENSURE_TRUE(start.AppendLiteral("<?", mozilla::fallible), NS_ERROR_OUT_OF_MEMORY); |
272 | 0 | NS_ENSURE_TRUE(start.Append(target, mozilla::fallible), NS_ERROR_OUT_OF_MEMORY); |
273 | 0 |
|
274 | 0 | if (mDoRaw || PreLevel() > 0) { |
275 | 0 | NS_ENSURE_TRUE(AppendToString(start, aStr), NS_ERROR_OUT_OF_MEMORY); |
276 | 0 | } |
277 | 0 | else if (mDoFormat) { |
278 | 0 | if (mAddSpace) { |
279 | 0 | NS_ENSURE_TRUE(AppendNewLineToString(aStr), NS_ERROR_OUT_OF_MEMORY); |
280 | 0 | } |
281 | 0 | NS_ENSURE_TRUE(AppendToStringFormatedWrapped(start, aStr), NS_ERROR_OUT_OF_MEMORY); |
282 | 0 | } |
283 | 0 | else if (mDoWrap) { |
284 | 0 | NS_ENSURE_TRUE(AppendToStringWrapped(start, aStr), NS_ERROR_OUT_OF_MEMORY); |
285 | 0 | } |
286 | 0 | else { |
287 | 0 | NS_ENSURE_TRUE(AppendToString(start, aStr), NS_ERROR_OUT_OF_MEMORY); |
288 | 0 | } |
289 | 0 |
|
290 | 0 | if (!data.IsEmpty()) { |
291 | 0 | NS_ENSURE_TRUE(AppendToString(char16_t(' '), aStr), NS_ERROR_OUT_OF_MEMORY); |
292 | 0 | NS_ENSURE_TRUE(AppendToStringConvertLF(data, aStr), NS_ERROR_OUT_OF_MEMORY); |
293 | 0 | } |
294 | 0 | NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING("?>"), aStr), NS_ERROR_OUT_OF_MEMORY); |
295 | 0 |
|
296 | 0 | MaybeFlagNewlineForRootNode(aPI); |
297 | 0 |
|
298 | 0 | return NS_OK; |
299 | 0 | } |
300 | | |
301 | | NS_IMETHODIMP |
302 | | nsXMLContentSerializer::AppendComment(Comment* aComment, |
303 | | int32_t aStartOffset, |
304 | | int32_t aEndOffset, |
305 | | nsAString& aStr) |
306 | 0 | { |
307 | 0 | nsAutoString data; |
308 | 0 | aComment->GetData(data); |
309 | 0 |
|
310 | 0 | int32_t dataLength = data.Length(); |
311 | 0 | if (aStartOffset || (aEndOffset != -1 && aEndOffset < dataLength)) { |
312 | 0 | int32_t length = |
313 | 0 | (aEndOffset == -1) ? dataLength : std::min(aEndOffset, dataLength); |
314 | 0 | length -= aStartOffset; |
315 | 0 |
|
316 | 0 | nsAutoString frag; |
317 | 0 | if (length > 0) { |
318 | 0 | data.Mid(frag, aStartOffset, length); |
319 | 0 | } |
320 | 0 | data.Assign(frag); |
321 | 0 | } |
322 | 0 |
|
323 | 0 | NS_ENSURE_TRUE(MaybeAddNewlineForRootNode(aStr), NS_ERROR_OUT_OF_MEMORY); |
324 | 0 |
|
325 | 0 | NS_NAMED_LITERAL_STRING(startComment, "<!--"); |
326 | 0 |
|
327 | 0 | if (mDoRaw || PreLevel() > 0) { |
328 | 0 | NS_ENSURE_TRUE(AppendToString(startComment, aStr), NS_ERROR_OUT_OF_MEMORY); |
329 | 0 | } |
330 | 0 | else if (mDoFormat) { |
331 | 0 | if (mAddSpace) { |
332 | 0 | NS_ENSURE_TRUE(AppendNewLineToString(aStr), NS_ERROR_OUT_OF_MEMORY); |
333 | 0 | } |
334 | 0 | NS_ENSURE_TRUE(AppendToStringFormatedWrapped(startComment, aStr), NS_ERROR_OUT_OF_MEMORY); |
335 | 0 | } |
336 | 0 | else if (mDoWrap) { |
337 | 0 | NS_ENSURE_TRUE(AppendToStringWrapped(startComment, aStr), NS_ERROR_OUT_OF_MEMORY); |
338 | 0 | } |
339 | 0 | else { |
340 | 0 | NS_ENSURE_TRUE(AppendToString(startComment, aStr), NS_ERROR_OUT_OF_MEMORY); |
341 | 0 | } |
342 | 0 |
|
343 | 0 | // Even if mDoformat, we don't format the content because it |
344 | 0 | // could have been preformated by the author |
345 | 0 | NS_ENSURE_TRUE(AppendToStringConvertLF(data, aStr), NS_ERROR_OUT_OF_MEMORY); |
346 | 0 | NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING("-->"), aStr), NS_ERROR_OUT_OF_MEMORY); |
347 | 0 |
|
348 | 0 | MaybeFlagNewlineForRootNode(aComment); |
349 | 0 |
|
350 | 0 | return NS_OK; |
351 | 0 | } |
352 | | |
353 | | NS_IMETHODIMP |
354 | | nsXMLContentSerializer::AppendDoctype(DocumentType* aDocType, |
355 | | nsAString& aStr) |
356 | 0 | { |
357 | 0 | nsAutoString name, publicId, systemId; |
358 | 0 | aDocType->GetName(name); |
359 | 0 | aDocType->GetPublicId(publicId); |
360 | 0 | aDocType->GetSystemId(systemId); |
361 | 0 |
|
362 | 0 | NS_ENSURE_TRUE(MaybeAddNewlineForRootNode(aStr), NS_ERROR_OUT_OF_MEMORY); |
363 | 0 |
|
364 | 0 | NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING("<!DOCTYPE "), aStr), NS_ERROR_OUT_OF_MEMORY); |
365 | 0 | NS_ENSURE_TRUE(AppendToString(name, aStr), NS_ERROR_OUT_OF_MEMORY); |
366 | 0 |
|
367 | 0 | char16_t quote; |
368 | 0 | if (!publicId.IsEmpty()) { |
369 | 0 | NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING(" PUBLIC "), aStr), NS_ERROR_OUT_OF_MEMORY); |
370 | 0 | if (publicId.FindChar(char16_t('"')) == -1) { |
371 | 0 | quote = char16_t('"'); |
372 | 0 | } |
373 | 0 | else { |
374 | 0 | quote = char16_t('\''); |
375 | 0 | } |
376 | 0 | NS_ENSURE_TRUE(AppendToString(quote, aStr), NS_ERROR_OUT_OF_MEMORY); |
377 | 0 | NS_ENSURE_TRUE(AppendToString(publicId, aStr), NS_ERROR_OUT_OF_MEMORY); |
378 | 0 | NS_ENSURE_TRUE(AppendToString(quote, aStr), NS_ERROR_OUT_OF_MEMORY); |
379 | 0 |
|
380 | 0 | if (!systemId.IsEmpty()) { |
381 | 0 | NS_ENSURE_TRUE(AppendToString(char16_t(' '), aStr), NS_ERROR_OUT_OF_MEMORY); |
382 | 0 | if (systemId.FindChar(char16_t('"')) == -1) { |
383 | 0 | quote = char16_t('"'); |
384 | 0 | } |
385 | 0 | else { |
386 | 0 | quote = char16_t('\''); |
387 | 0 | } |
388 | 0 | NS_ENSURE_TRUE(AppendToString(quote, aStr), NS_ERROR_OUT_OF_MEMORY); |
389 | 0 | NS_ENSURE_TRUE(AppendToString(systemId, aStr), NS_ERROR_OUT_OF_MEMORY); |
390 | 0 | NS_ENSURE_TRUE(AppendToString(quote, aStr), NS_ERROR_OUT_OF_MEMORY); |
391 | 0 | } |
392 | 0 | } |
393 | 0 | else if (!systemId.IsEmpty()) { |
394 | 0 | if (systemId.FindChar(char16_t('"')) == -1) { |
395 | 0 | quote = char16_t('"'); |
396 | 0 | } |
397 | 0 | else { |
398 | 0 | quote = char16_t('\''); |
399 | 0 | } |
400 | 0 | NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING(" SYSTEM "), aStr), NS_ERROR_OUT_OF_MEMORY); |
401 | 0 | NS_ENSURE_TRUE(AppendToString(quote, aStr), NS_ERROR_OUT_OF_MEMORY); |
402 | 0 | NS_ENSURE_TRUE(AppendToString(systemId, aStr), NS_ERROR_OUT_OF_MEMORY); |
403 | 0 | NS_ENSURE_TRUE(AppendToString(quote, aStr), NS_ERROR_OUT_OF_MEMORY); |
404 | 0 | } |
405 | 0 |
|
406 | 0 | NS_ENSURE_TRUE(AppendToString(kGreaterThan, aStr), NS_ERROR_OUT_OF_MEMORY); |
407 | 0 | MaybeFlagNewlineForRootNode(aDocType); |
408 | 0 |
|
409 | 0 | return NS_OK; |
410 | 0 | } |
411 | | |
412 | | nsresult |
413 | | nsXMLContentSerializer::PushNameSpaceDecl(const nsAString& aPrefix, |
414 | | const nsAString& aURI, |
415 | | nsIContent* aOwner) |
416 | 0 | { |
417 | 0 | NameSpaceDecl* decl = mNameSpaceStack.AppendElement(); |
418 | 0 | if (!decl) return NS_ERROR_OUT_OF_MEMORY; |
419 | 0 | |
420 | 0 | decl->mPrefix.Assign(aPrefix); |
421 | 0 | decl->mURI.Assign(aURI); |
422 | 0 | // Don't addref - this weak reference will be removed when |
423 | 0 | // we pop the stack |
424 | 0 | decl->mOwner = aOwner; |
425 | 0 | return NS_OK; |
426 | 0 | } |
427 | | |
428 | | void |
429 | | nsXMLContentSerializer::PopNameSpaceDeclsFor(nsIContent* aOwner) |
430 | 0 | { |
431 | 0 | int32_t index, count; |
432 | 0 |
|
433 | 0 | count = mNameSpaceStack.Length(); |
434 | 0 | for (index = count - 1; index >= 0; index--) { |
435 | 0 | if (mNameSpaceStack[index].mOwner != aOwner) { |
436 | 0 | break; |
437 | 0 | } |
438 | 0 | mNameSpaceStack.RemoveElementAt(index); |
439 | 0 | } |
440 | 0 | } |
441 | | |
442 | | bool |
443 | | nsXMLContentSerializer::ConfirmPrefix(nsAString& aPrefix, |
444 | | const nsAString& aURI, |
445 | | nsIContent* aElement, |
446 | | bool aIsAttribute) |
447 | 0 | { |
448 | 0 | if (aPrefix.EqualsLiteral(kXMLNS)) { |
449 | 0 | return false; |
450 | 0 | } |
451 | 0 | |
452 | 0 | if (aURI.EqualsLiteral("http://www.w3.org/XML/1998/namespace")) { |
453 | 0 | // The prefix must be xml for this namespace. We don't need to declare it, |
454 | 0 | // so always just set the prefix to xml. |
455 | 0 | aPrefix.AssignLiteral("xml"); |
456 | 0 |
|
457 | 0 | return false; |
458 | 0 | } |
459 | 0 | |
460 | 0 | bool mustHavePrefix; |
461 | 0 | if (aIsAttribute) { |
462 | 0 | if (aURI.IsEmpty()) { |
463 | 0 | // Attribute in the null namespace. This just shouldn't have a prefix. |
464 | 0 | // And there's no need to push any namespace decls |
465 | 0 | aPrefix.Truncate(); |
466 | 0 | return false; |
467 | 0 | } |
468 | 0 | |
469 | 0 | // Attribute not in the null namespace -- must have a prefix |
470 | 0 | mustHavePrefix = true; |
471 | 0 | } else { |
472 | 0 | // Not an attribute, so doesn't _have_ to have a prefix |
473 | 0 | mustHavePrefix = false; |
474 | 0 | } |
475 | 0 |
|
476 | 0 | // Keep track of the closest prefix that's bound to aURI and whether we've |
477 | 0 | // found such a thing. closestURIMatch holds the prefix, and uriMatch |
478 | 0 | // indicates whether we actually have one. |
479 | 0 | nsAutoString closestURIMatch; |
480 | 0 | bool uriMatch = false; |
481 | 0 |
|
482 | 0 | // Also keep track of whether we've seen aPrefix already. If we have, that |
483 | 0 | // means that it's already bound to a URI different from aURI, so even if we |
484 | 0 | // later (so in a more outer scope) see it bound to aURI we can't reuse it. |
485 | 0 | bool haveSeenOurPrefix = false; |
486 | 0 |
|
487 | 0 | int32_t count = mNameSpaceStack.Length(); |
488 | 0 | int32_t index = count - 1; |
489 | 0 | while (index >= 0) { |
490 | 0 | NameSpaceDecl& decl = mNameSpaceStack.ElementAt(index); |
491 | 0 | // Check if we've found a prefix match |
492 | 0 | if (aPrefix.Equals(decl.mPrefix)) { |
493 | 0 |
|
494 | 0 | // If the URIs match and aPrefix is not bound to any other URI, we can |
495 | 0 | // use aPrefix |
496 | 0 | if (!haveSeenOurPrefix && aURI.Equals(decl.mURI)) { |
497 | 0 | // Just use our uriMatch stuff. That will deal with an empty aPrefix |
498 | 0 | // the right way. We can break out of the loop now, though. |
499 | 0 | uriMatch = true; |
500 | 0 | closestURIMatch = aPrefix; |
501 | 0 | break; |
502 | 0 | } |
503 | 0 | |
504 | 0 | haveSeenOurPrefix = true; |
505 | 0 |
|
506 | 0 | // If they don't, and either: |
507 | 0 | // 1) We have a prefix (so we'd be redeclaring this prefix to point to a |
508 | 0 | // different namespace) or |
509 | 0 | // 2) We're looking at an existing default namespace decl on aElement (so |
510 | 0 | // we can't create a new default namespace decl for this URI) |
511 | 0 | // then generate a new prefix. Note that we do NOT generate new prefixes |
512 | 0 | // if we happen to have aPrefix == decl->mPrefix == "" and mismatching |
513 | 0 | // URIs when |decl| doesn't have aElement as its owner. In that case we |
514 | 0 | // can simply push the new namespace URI as the default namespace for |
515 | 0 | // aElement. |
516 | 0 | if (!aPrefix.IsEmpty() || decl.mOwner == aElement) { |
517 | 0 | NS_ASSERTION(!aURI.IsEmpty(), |
518 | 0 | "Not allowed to add a xmlns attribute with an empty " |
519 | 0 | "namespace name unless it declares the default " |
520 | 0 | "namespace."); |
521 | 0 |
|
522 | 0 | GenerateNewPrefix(aPrefix); |
523 | 0 | // Now we need to validate our new prefix/uri combination; check it |
524 | 0 | // against the full namespace stack again. Note that just restarting |
525 | 0 | // the while loop is ok, since we haven't changed aURI, so the |
526 | 0 | // closestURIMatch and uriMatch state is not affected. |
527 | 0 | index = count - 1; |
528 | 0 | haveSeenOurPrefix = false; |
529 | 0 | continue; |
530 | 0 | } |
531 | 0 | } |
532 | 0 |
|
533 | 0 | // If we've found a URI match, then record the first one |
534 | 0 | if (!uriMatch && aURI.Equals(decl.mURI)) { |
535 | 0 | // Need to check that decl->mPrefix is not declared anywhere closer to |
536 | 0 | // us. If it is, we can't use it. |
537 | 0 | bool prefixOK = true; |
538 | 0 | int32_t index2; |
539 | 0 | for (index2 = count-1; index2 > index && prefixOK; --index2) { |
540 | 0 | prefixOK = (mNameSpaceStack[index2].mPrefix != decl.mPrefix); |
541 | 0 | } |
542 | 0 |
|
543 | 0 | if (prefixOK) { |
544 | 0 | uriMatch = true; |
545 | 0 | closestURIMatch.Assign(decl.mPrefix); |
546 | 0 | } |
547 | 0 | } |
548 | 0 |
|
549 | 0 | --index; |
550 | 0 | } |
551 | 0 |
|
552 | 0 | // At this point the following invariants hold: |
553 | 0 | // 1) The prefix in closestURIMatch is mapped to aURI in our scope if |
554 | 0 | // uriMatch is set. |
555 | 0 | // 2) There is nothing on the namespace stack that has aPrefix as the prefix |
556 | 0 | // and a _different_ URI, except for the case aPrefix.IsEmpty (and |
557 | 0 | // possible default namespaces on ancestors) |
558 | 0 |
|
559 | 0 | // So if uriMatch is set it's OK to use the closestURIMatch prefix. The one |
560 | 0 | // exception is when closestURIMatch is actually empty (default namespace |
561 | 0 | // decl) and we must have a prefix. |
562 | 0 | if (uriMatch && (!mustHavePrefix || !closestURIMatch.IsEmpty())) { |
563 | 0 | aPrefix.Assign(closestURIMatch); |
564 | 0 | return false; |
565 | 0 | } |
566 | 0 | |
567 | 0 | if (aPrefix.IsEmpty()) { |
568 | 0 | // At this point, aPrefix is empty (which means we never had a prefix to |
569 | 0 | // start with). If we must have a prefix, just generate a new prefix and |
570 | 0 | // then send it back through the namespace stack checks to make sure it's |
571 | 0 | // OK. |
572 | 0 | if (mustHavePrefix) { |
573 | 0 | GenerateNewPrefix(aPrefix); |
574 | 0 | return ConfirmPrefix(aPrefix, aURI, aElement, aIsAttribute); |
575 | 0 | } |
576 | 0 | |
577 | 0 | // One final special case. If aPrefix is empty and we never saw an empty |
578 | 0 | // prefix (default namespace decl) on the namespace stack and we're in the |
579 | 0 | // null namespace there is no reason to output an |xmlns=""| here. It just |
580 | 0 | // makes the output less readable. |
581 | 0 | if (!haveSeenOurPrefix && aURI.IsEmpty()) { |
582 | 0 | return false; |
583 | 0 | } |
584 | 0 | } |
585 | 0 | |
586 | 0 | // Now just set aURI as the new default namespace URI. Indicate that we need |
587 | 0 | // to create a namespace decl for the final prefix |
588 | 0 | return true; |
589 | 0 | } |
590 | | |
591 | | void |
592 | | nsXMLContentSerializer::GenerateNewPrefix(nsAString& aPrefix) |
593 | 0 | { |
594 | 0 | aPrefix.Assign('a'); |
595 | 0 | aPrefix.AppendInt(mPrefixIndex++); |
596 | 0 | } |
597 | | |
598 | | bool |
599 | | nsXMLContentSerializer::SerializeAttr(const nsAString& aPrefix, |
600 | | const nsAString& aName, |
601 | | const nsAString& aValue, |
602 | | nsAString& aStr, |
603 | | bool aDoEscapeEntities) |
604 | 0 | { |
605 | 0 | nsAutoString attrString_; |
606 | 0 | // For innerHTML we can do faster appending without |
607 | 0 | // temporary strings. |
608 | 0 | bool rawAppend = mDoRaw && aDoEscapeEntities; |
609 | 0 | nsAString& attrString = (rawAppend) ? aStr : attrString_; |
610 | 0 |
|
611 | 0 | NS_ENSURE_TRUE(attrString.Append(char16_t(' '), mozilla::fallible), false); |
612 | 0 | if (!aPrefix.IsEmpty()) { |
613 | 0 | NS_ENSURE_TRUE(attrString.Append(aPrefix, mozilla::fallible), false); |
614 | 0 | NS_ENSURE_TRUE(attrString.Append(char16_t(':'), mozilla::fallible), false); |
615 | 0 | } |
616 | 0 | NS_ENSURE_TRUE(attrString.Append(aName, mozilla::fallible), false); |
617 | 0 |
|
618 | 0 | if (aDoEscapeEntities) { |
619 | 0 | // if problem characters are turned into character entity references |
620 | 0 | // then there will be no problem with the value delimiter characters |
621 | 0 | NS_ENSURE_TRUE(attrString.AppendLiteral("=\"", mozilla::fallible), false); |
622 | 0 |
|
623 | 0 | mInAttribute = true; |
624 | 0 | bool result = AppendAndTranslateEntities(aValue, attrString); |
625 | 0 | mInAttribute = false; |
626 | 0 | NS_ENSURE_TRUE(result, false); |
627 | 0 |
|
628 | 0 | NS_ENSURE_TRUE(attrString.Append(char16_t('"'), mozilla::fallible), false); |
629 | 0 | if (rawAppend) { |
630 | 0 | return true; |
631 | 0 | } |
632 | 0 | } |
633 | 0 | else { |
634 | 0 | // Depending on whether the attribute value contains quotes or apostrophes we |
635 | 0 | // need to select the delimiter character and escape characters using |
636 | 0 | // character entity references, ignoring the value of aDoEscapeEntities. |
637 | 0 | // See http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2.2 for |
638 | 0 | // the standard on character entity references in values. We also have to |
639 | 0 | // make sure to escape any '&' characters. |
640 | 0 |
|
641 | 0 | bool bIncludesSingle = false; |
642 | 0 | bool bIncludesDouble = false; |
643 | 0 | nsAString::const_iterator iCurr, iEnd; |
644 | 0 | aValue.BeginReading(iCurr); |
645 | 0 | aValue.EndReading(iEnd); |
646 | 0 | for ( ; iCurr != iEnd; ++iCurr) { |
647 | 0 | if (*iCurr == char16_t('\'')) { |
648 | 0 | bIncludesSingle = true; |
649 | 0 | if (bIncludesDouble) { |
650 | 0 | break; |
651 | 0 | } |
652 | 0 | } else if (*iCurr == char16_t('"')) { |
653 | 0 | bIncludesDouble = true; |
654 | 0 | if (bIncludesSingle) { |
655 | 0 | break; |
656 | 0 | } |
657 | 0 | } |
658 | 0 | } |
659 | 0 |
|
660 | 0 | // Delimiter and escaping is according to the following table |
661 | 0 | // bIncludesDouble bIncludesSingle Delimiter Escape Double Quote |
662 | 0 | // FALSE FALSE " FALSE |
663 | 0 | // FALSE TRUE " FALSE |
664 | 0 | // TRUE FALSE ' FALSE |
665 | 0 | // TRUE TRUE " TRUE |
666 | 0 | char16_t cDelimiter = |
667 | 0 | (bIncludesDouble && !bIncludesSingle) ? char16_t('\'') : char16_t('"'); |
668 | 0 | NS_ENSURE_TRUE(attrString.Append(char16_t('='), mozilla::fallible), false); |
669 | 0 | NS_ENSURE_TRUE(attrString.Append(cDelimiter, mozilla::fallible), false); |
670 | 0 | nsAutoString sValue(aValue); |
671 | 0 | NS_ENSURE_TRUE(sValue.ReplaceSubstring(NS_LITERAL_STRING("&"), |
672 | 0 | NS_LITERAL_STRING("&"), mozilla::fallible), false); |
673 | 0 | if (bIncludesDouble && bIncludesSingle) { |
674 | 0 | NS_ENSURE_TRUE(sValue.ReplaceSubstring(NS_LITERAL_STRING("\""), |
675 | 0 | NS_LITERAL_STRING("""), mozilla::fallible), false); |
676 | 0 | } |
677 | 0 | NS_ENSURE_TRUE(attrString.Append(sValue, mozilla::fallible), false); |
678 | 0 | NS_ENSURE_TRUE(attrString.Append(cDelimiter, mozilla::fallible), false); |
679 | 0 | } |
680 | 0 | if (mDoRaw || PreLevel() > 0) { |
681 | 0 | NS_ENSURE_TRUE(AppendToStringConvertLF(attrString, aStr), false); |
682 | 0 | } |
683 | 0 | else if (mDoFormat) { |
684 | 0 | NS_ENSURE_TRUE(AppendToStringFormatedWrapped(attrString, aStr), false); |
685 | 0 | } |
686 | 0 | else if (mDoWrap) { |
687 | 0 | NS_ENSURE_TRUE(AppendToStringWrapped(attrString, aStr), false); |
688 | 0 | } |
689 | 0 | else { |
690 | 0 | NS_ENSURE_TRUE(AppendToStringConvertLF(attrString, aStr), false); |
691 | 0 | } |
692 | 0 |
|
693 | 0 | return true; |
694 | 0 | } |
695 | | |
696 | | uint32_t |
697 | | nsXMLContentSerializer::ScanNamespaceDeclarations(Element* aElement, |
698 | | Element* aOriginalElement, |
699 | | const nsAString& aTagNamespaceURI) |
700 | 0 | { |
701 | 0 | uint32_t index, count; |
702 | 0 | nsAutoString uriStr, valueStr; |
703 | 0 |
|
704 | 0 | count = aElement->GetAttrCount(); |
705 | 0 |
|
706 | 0 | // First scan for namespace declarations, pushing each on the stack |
707 | 0 | uint32_t skipAttr = count; |
708 | 0 | for (index = 0; index < count; index++) { |
709 | 0 |
|
710 | 0 | const BorrowedAttrInfo info = aElement->GetAttrInfoAt(index); |
711 | 0 | const nsAttrName* name = info.mName; |
712 | 0 |
|
713 | 0 | int32_t namespaceID = name->NamespaceID(); |
714 | 0 | nsAtom *attrName = name->LocalName(); |
715 | 0 |
|
716 | 0 | if (namespaceID == kNameSpaceID_XMLNS || |
717 | 0 | // Also push on the stack attrs named "xmlns" in the null |
718 | 0 | // namespace... because once we serialize those out they'll look like |
719 | 0 | // namespace decls. :( |
720 | 0 | // XXXbz what if we have both "xmlns" in the null namespace and "xmlns" |
721 | 0 | // in the xmlns namespace? |
722 | 0 | (namespaceID == kNameSpaceID_None && |
723 | 0 | attrName == nsGkAtoms::xmlns)) { |
724 | 0 | info.mValue->ToString(uriStr); |
725 | 0 |
|
726 | 0 | if (!name->GetPrefix()) { |
727 | 0 | if (aTagNamespaceURI.IsEmpty() && !uriStr.IsEmpty()) { |
728 | 0 | // If the element is in no namespace we need to add a xmlns |
729 | 0 | // attribute to declare that. That xmlns attribute must not have a |
730 | 0 | // prefix (see http://www.w3.org/TR/REC-xml-names/#dt-prefix), ie it |
731 | 0 | // must declare the default namespace. We just found an xmlns |
732 | 0 | // attribute that declares the default namespace to something |
733 | 0 | // non-empty. We're going to ignore this attribute, for children we |
734 | 0 | // will detect that we need to add it again and attributes aren't |
735 | 0 | // affected by the default namespace. |
736 | 0 | skipAttr = index; |
737 | 0 | } |
738 | 0 | else { |
739 | 0 | // Default NS attribute does not have prefix (and the name is "xmlns") |
740 | 0 | PushNameSpaceDecl(EmptyString(), uriStr, aOriginalElement); |
741 | 0 | } |
742 | 0 | } |
743 | 0 | else { |
744 | 0 | PushNameSpaceDecl(nsDependentAtomString(attrName), uriStr, |
745 | 0 | aOriginalElement); |
746 | 0 | } |
747 | 0 | } |
748 | 0 | } |
749 | 0 | return skipAttr; |
750 | 0 | } |
751 | | |
752 | | |
753 | | bool |
754 | | nsXMLContentSerializer::IsJavaScript(nsIContent * aContent, nsAtom* aAttrNameAtom, |
755 | | int32_t aAttrNamespaceID, const nsAString& aValueString) |
756 | 0 | { |
757 | 0 | bool isHtml = aContent->IsHTMLElement(); |
758 | 0 | bool isXul = aContent->IsXULElement(); |
759 | 0 | bool isSvg = aContent->IsSVGElement(); |
760 | 0 |
|
761 | 0 | if (aAttrNamespaceID == kNameSpaceID_None && |
762 | 0 | (isHtml || isXul || isSvg) && |
763 | 0 | (aAttrNameAtom == nsGkAtoms::href || |
764 | 0 | aAttrNameAtom == nsGkAtoms::src)) { |
765 | 0 |
|
766 | 0 | static const char kJavaScript[] = "javascript"; |
767 | 0 | int32_t pos = aValueString.FindChar(':'); |
768 | 0 | if (pos < (int32_t)(sizeof kJavaScript - 1)) |
769 | 0 | return false; |
770 | 0 | nsAutoString scheme(Substring(aValueString, 0, pos)); |
771 | 0 | scheme.StripWhitespace(); |
772 | 0 | if ((scheme.Length() == (sizeof kJavaScript - 1)) && |
773 | 0 | scheme.EqualsIgnoreCase(kJavaScript)) |
774 | 0 | return true; |
775 | 0 | else |
776 | 0 | return false; |
777 | 0 | } |
778 | 0 | |
779 | 0 | return aContent->IsEventAttributeName(aAttrNameAtom); |
780 | 0 | } |
781 | | |
782 | | |
783 | | bool |
784 | | nsXMLContentSerializer::SerializeAttributes(Element* aElement, |
785 | | Element* aOriginalElement, |
786 | | nsAString& aTagPrefix, |
787 | | const nsAString& aTagNamespaceURI, |
788 | | nsAtom* aTagName, |
789 | | nsAString& aStr, |
790 | | uint32_t aSkipAttr, |
791 | | bool aAddNSAttr) |
792 | 0 | { |
793 | 0 | nsAutoString prefixStr, uriStr, valueStr; |
794 | 0 | nsAutoString xmlnsStr; |
795 | 0 | xmlnsStr.AssignLiteral(kXMLNS); |
796 | 0 | uint32_t index, count; |
797 | 0 |
|
798 | 0 | MaybeSerializeIsValue(aElement, aStr); |
799 | 0 |
|
800 | 0 | // If we had to add a new namespace declaration, serialize |
801 | 0 | // and push it on the namespace stack |
802 | 0 | if (aAddNSAttr) { |
803 | 0 | if (aTagPrefix.IsEmpty()) { |
804 | 0 | // Serialize default namespace decl |
805 | 0 | NS_ENSURE_TRUE(SerializeAttr(EmptyString(), xmlnsStr, aTagNamespaceURI, aStr, true), false); |
806 | 0 | } |
807 | 0 | else { |
808 | 0 | // Serialize namespace decl |
809 | 0 | NS_ENSURE_TRUE(SerializeAttr(xmlnsStr, aTagPrefix, aTagNamespaceURI, aStr, true), false); |
810 | 0 | } |
811 | 0 | PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement); |
812 | 0 | } |
813 | 0 |
|
814 | 0 | count = aElement->GetAttrCount(); |
815 | 0 |
|
816 | 0 | // Now serialize each of the attributes |
817 | 0 | // XXX Unfortunately we need a namespace manager to get |
818 | 0 | // attribute URIs. |
819 | 0 | for (index = 0; index < count; index++) { |
820 | 0 | if (aSkipAttr == index) { |
821 | 0 | continue; |
822 | 0 | } |
823 | 0 | |
824 | 0 | const nsAttrName* name = aElement->GetAttrNameAt(index); |
825 | 0 | int32_t namespaceID = name->NamespaceID(); |
826 | 0 | nsAtom* attrName = name->LocalName(); |
827 | 0 | nsAtom* attrPrefix = name->GetPrefix(); |
828 | 0 |
|
829 | 0 | // Filter out any attribute starting with [-|_]moz |
830 | 0 | nsDependentAtomString attrNameStr(attrName); |
831 | 0 | if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) || |
832 | 0 | StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) { |
833 | 0 | continue; |
834 | 0 | } |
835 | 0 | |
836 | 0 | if (attrPrefix) { |
837 | 0 | attrPrefix->ToString(prefixStr); |
838 | 0 | } |
839 | 0 | else { |
840 | 0 | prefixStr.Truncate(); |
841 | 0 | } |
842 | 0 |
|
843 | 0 | bool addNSAttr = false; |
844 | 0 | if (kNameSpaceID_XMLNS != namespaceID) { |
845 | 0 | nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr); |
846 | 0 | addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true); |
847 | 0 | } |
848 | 0 |
|
849 | 0 | aElement->GetAttr(namespaceID, attrName, valueStr); |
850 | 0 |
|
851 | 0 | nsDependentAtomString nameStr(attrName); |
852 | 0 | bool isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr); |
853 | 0 |
|
854 | 0 | NS_ENSURE_TRUE(SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS), false); |
855 | 0 |
|
856 | 0 | if (addNSAttr) { |
857 | 0 | NS_ASSERTION(!prefixStr.IsEmpty(), |
858 | 0 | "Namespaced attributes must have a prefix"); |
859 | 0 | NS_ENSURE_TRUE(SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, true), false); |
860 | 0 | PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement); |
861 | 0 | } |
862 | 0 | } |
863 | 0 |
|
864 | 0 | return true; |
865 | 0 | } |
866 | | |
867 | | NS_IMETHODIMP |
868 | | nsXMLContentSerializer::AppendElementStart(Element* aElement, |
869 | | Element* aOriginalElement, |
870 | | nsAString& aStr) |
871 | 0 | { |
872 | 0 | NS_ENSURE_ARG(aElement); |
873 | 0 |
|
874 | 0 | bool forceFormat = false; |
875 | 0 | nsresult rv = NS_OK; |
876 | 0 | if (!CheckElementStart(aElement, forceFormat, aStr, rv)) { |
877 | 0 | // When we go to AppendElementEnd for this element, we're going to |
878 | 0 | // MaybeLeaveFromPreContent(). So make sure to MaybeEnterInPreContent() |
879 | 0 | // now, so our PreLevel() doesn't get confused. |
880 | 0 | MaybeEnterInPreContent(aElement); |
881 | 0 | return rv; |
882 | 0 | } |
883 | 0 | |
884 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
885 | 0 |
|
886 | 0 | nsAutoString tagPrefix, tagLocalName, tagNamespaceURI; |
887 | 0 | aElement->NodeInfo()->GetPrefix(tagPrefix); |
888 | 0 | aElement->NodeInfo()->GetName(tagLocalName); |
889 | 0 | aElement->NodeInfo()->GetNamespaceURI(tagNamespaceURI); |
890 | 0 |
|
891 | 0 | uint32_t skipAttr = |
892 | 0 | ScanNamespaceDeclarations(aElement, aOriginalElement, tagNamespaceURI); |
893 | 0 |
|
894 | 0 | nsAtom *name = aElement->NodeInfo()->NameAtom(); |
895 | 0 | bool lineBreakBeforeOpen = |
896 | 0 | LineBreakBeforeOpen(aElement->GetNameSpaceID(), name); |
897 | 0 |
|
898 | 0 | if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) { |
899 | 0 | if (mColPos && lineBreakBeforeOpen) { |
900 | 0 | NS_ENSURE_TRUE(AppendNewLineToString(aStr), NS_ERROR_OUT_OF_MEMORY); |
901 | 0 | } |
902 | 0 | else { |
903 | 0 | NS_ENSURE_TRUE(MaybeAddNewlineForRootNode(aStr), NS_ERROR_OUT_OF_MEMORY); |
904 | 0 | } |
905 | 0 | if (!mColPos) { |
906 | 0 | NS_ENSURE_TRUE(AppendIndentation(aStr), NS_ERROR_OUT_OF_MEMORY); |
907 | 0 | } |
908 | 0 | else if (mAddSpace) { |
909 | 0 | NS_ENSURE_TRUE(AppendToString(char16_t(' '), aStr), NS_ERROR_OUT_OF_MEMORY); |
910 | 0 | mAddSpace = false; |
911 | 0 | } |
912 | 0 | } |
913 | 0 | else if (mAddSpace) { |
914 | 0 | NS_ENSURE_TRUE(AppendToString(char16_t(' '), aStr), NS_ERROR_OUT_OF_MEMORY); |
915 | 0 | mAddSpace = false; |
916 | 0 | } |
917 | 0 | else { |
918 | 0 | NS_ENSURE_TRUE(MaybeAddNewlineForRootNode(aStr), NS_ERROR_OUT_OF_MEMORY); |
919 | 0 | } |
920 | 0 |
|
921 | 0 | // Always reset to avoid false newlines in case MaybeAddNewlineForRootNode wasn't |
922 | 0 | // called |
923 | 0 | mAddNewlineForRootNode = false; |
924 | 0 |
|
925 | 0 | bool addNSAttr; |
926 | 0 | addNSAttr = ConfirmPrefix(tagPrefix, tagNamespaceURI, aOriginalElement, |
927 | 0 | false); |
928 | 0 |
|
929 | 0 | // Serialize the qualified name of the element |
930 | 0 | NS_ENSURE_TRUE(AppendToString(kLessThan, aStr), NS_ERROR_OUT_OF_MEMORY); |
931 | 0 | if (!tagPrefix.IsEmpty()) { |
932 | 0 | NS_ENSURE_TRUE(AppendToString(tagPrefix, aStr), NS_ERROR_OUT_OF_MEMORY); |
933 | 0 | NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING(":"), aStr), NS_ERROR_OUT_OF_MEMORY); |
934 | 0 | } |
935 | 0 | NS_ENSURE_TRUE(AppendToString(tagLocalName, aStr), NS_ERROR_OUT_OF_MEMORY); |
936 | 0 |
|
937 | 0 | MaybeEnterInPreContent(aElement); |
938 | 0 |
|
939 | 0 | if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) { |
940 | 0 | NS_ENSURE_TRUE(IncrIndentation(name), NS_ERROR_OUT_OF_MEMORY); |
941 | 0 | } |
942 | 0 |
|
943 | 0 | NS_ENSURE_TRUE(SerializeAttributes(aElement, aOriginalElement, tagPrefix, |
944 | 0 | tagNamespaceURI, name, aStr, skipAttr, |
945 | 0 | addNSAttr), |
946 | 0 | NS_ERROR_OUT_OF_MEMORY); |
947 | 0 |
|
948 | 0 | NS_ENSURE_TRUE(AppendEndOfElementStart(aElement, aOriginalElement, aStr), |
949 | 0 | NS_ERROR_OUT_OF_MEMORY); |
950 | 0 |
|
951 | 0 | if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel() |
952 | 0 | && LineBreakAfterOpen(aElement->GetNameSpaceID(), name)) { |
953 | 0 | NS_ENSURE_TRUE(AppendNewLineToString(aStr), NS_ERROR_OUT_OF_MEMORY); |
954 | 0 | } |
955 | 0 |
|
956 | 0 | NS_ENSURE_TRUE(AfterElementStart(aElement, aOriginalElement, aStr), NS_ERROR_OUT_OF_MEMORY); |
957 | 0 |
|
958 | 0 | return NS_OK; |
959 | 0 | } |
960 | | |
961 | | // aElement is the actual element we're outputting. aOriginalElement is the one |
962 | | // in the original DOM, which is the one we have to test for kids. |
963 | | static bool |
964 | | ElementNeedsSeparateEndTag(Element* aElement, Element* aOriginalElement) |
965 | 0 | { |
966 | 0 | if (aOriginalElement->GetChildCount()) { |
967 | 0 | // We have kids, so we need a separate end tag. This needs to be checked on |
968 | 0 | // aOriginalElement because that's the one that's actually in the DOM and |
969 | 0 | // might have kids. |
970 | 0 | return true; |
971 | 0 | } |
972 | 0 | |
973 | 0 | if (!aElement->IsHTMLElement()) { |
974 | 0 | // Empty non-HTML elements can just skip a separate end tag. |
975 | 0 | return false; |
976 | 0 | } |
977 | 0 | |
978 | 0 | // HTML container tags should have a separate end tag even if empty, per spec. |
979 | 0 | // See |
980 | 0 | // https://w3c.github.io/DOM-Parsing/#dfn-concept-xml-serialization-algorithm |
981 | 0 | nsAtom* localName = aElement->NodeInfo()->NameAtom(); |
982 | 0 | bool isHTMLContainer = |
983 | 0 | nsHTMLElement::IsContainer(nsHTMLTags::CaseSensitiveAtomTagToId(localName)); |
984 | 0 | return isHTMLContainer; |
985 | 0 | } |
986 | | |
987 | | bool |
988 | | nsXMLContentSerializer::AppendEndOfElementStart(Element* aElement, |
989 | | Element* aOriginalElement, |
990 | | nsAString& aStr) |
991 | 0 | { |
992 | 0 | if (ElementNeedsSeparateEndTag(aElement, aOriginalElement)) { |
993 | 0 | return AppendToString(kGreaterThan, aStr); |
994 | 0 | } |
995 | 0 | |
996 | 0 | // We don't need a separate end tag. For HTML elements (which at this point |
997 | 0 | // must be non-containers), append a space before the '/', per spec. See |
998 | 0 | // https://w3c.github.io/DOM-Parsing/#dfn-concept-xml-serialization-algorithm |
999 | 0 | if (aOriginalElement->IsHTMLElement()) { |
1000 | 0 | if (!AppendToString(kSpace, aStr)) { |
1001 | 0 | return false; |
1002 | 0 | } |
1003 | 0 | } |
1004 | 0 | |
1005 | 0 | return AppendToString(NS_LITERAL_STRING("/>"), aStr); |
1006 | 0 | } |
1007 | | |
1008 | | NS_IMETHODIMP |
1009 | | nsXMLContentSerializer::AppendElementEnd(Element* aElement, |
1010 | | nsAString& aStr) |
1011 | 0 | { |
1012 | 0 | NS_ENSURE_ARG(aElement); |
1013 | 0 |
|
1014 | 0 | nsIContent* content = aElement; |
1015 | 0 |
|
1016 | 0 | bool forceFormat = false, outputElementEnd; |
1017 | 0 | outputElementEnd = CheckElementEnd(aElement, forceFormat, aStr); |
1018 | 0 |
|
1019 | 0 | nsAtom *name = content->NodeInfo()->NameAtom(); |
1020 | 0 |
|
1021 | 0 | if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) { |
1022 | 0 | DecrIndentation(name); |
1023 | 0 | } |
1024 | 0 |
|
1025 | 0 | if (!outputElementEnd) { |
1026 | 0 | // Keep this in sync with the cleanup at the end of this method. |
1027 | 0 | PopNameSpaceDeclsFor(aElement); |
1028 | 0 | MaybeLeaveFromPreContent(content); |
1029 | 0 | MaybeFlagNewlineForRootNode(aElement); |
1030 | 0 | AfterElementEnd(content, aStr); |
1031 | 0 | return NS_OK; |
1032 | 0 | } |
1033 | 0 | |
1034 | 0 | nsAutoString tagPrefix, tagLocalName, tagNamespaceURI; |
1035 | 0 |
|
1036 | 0 | aElement->NodeInfo()->GetPrefix(tagPrefix); |
1037 | 0 | aElement->NodeInfo()->GetName(tagLocalName); |
1038 | 0 | aElement->NodeInfo()->GetNamespaceURI(tagNamespaceURI); |
1039 | 0 |
|
1040 | | #ifdef DEBUG |
1041 | | bool debugNeedToPushNamespace = |
1042 | | #endif |
1043 | | ConfirmPrefix(tagPrefix, tagNamespaceURI, aElement, false); |
1044 | 0 | NS_ASSERTION(!debugNeedToPushNamespace, "Can't push namespaces in closing tag!"); |
1045 | 0 |
|
1046 | 0 | if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel()) { |
1047 | 0 |
|
1048 | 0 | bool lineBreakBeforeClose = LineBreakBeforeClose(content->GetNameSpaceID(), name); |
1049 | 0 |
|
1050 | 0 | if (mColPos && lineBreakBeforeClose) { |
1051 | 0 | NS_ENSURE_TRUE(AppendNewLineToString(aStr), NS_ERROR_OUT_OF_MEMORY); |
1052 | 0 | } |
1053 | 0 | if (!mColPos) { |
1054 | 0 | NS_ENSURE_TRUE(AppendIndentation(aStr), NS_ERROR_OUT_OF_MEMORY); |
1055 | 0 | } |
1056 | 0 | else if (mAddSpace) { |
1057 | 0 | NS_ENSURE_TRUE(AppendToString(char16_t(' '), aStr), NS_ERROR_OUT_OF_MEMORY); |
1058 | 0 | mAddSpace = false; |
1059 | 0 | } |
1060 | 0 | } |
1061 | 0 | else if (mAddSpace) { |
1062 | 0 | NS_ENSURE_TRUE(AppendToString(char16_t(' '), aStr), NS_ERROR_OUT_OF_MEMORY); |
1063 | 0 | mAddSpace = false; |
1064 | 0 | } |
1065 | 0 |
|
1066 | 0 | NS_ENSURE_TRUE(AppendToString(kEndTag, aStr), NS_ERROR_OUT_OF_MEMORY); |
1067 | 0 | if (!tagPrefix.IsEmpty()) { |
1068 | 0 | NS_ENSURE_TRUE(AppendToString(tagPrefix, aStr), NS_ERROR_OUT_OF_MEMORY); |
1069 | 0 | NS_ENSURE_TRUE(AppendToString(NS_LITERAL_STRING(":"), aStr), NS_ERROR_OUT_OF_MEMORY); |
1070 | 0 | } |
1071 | 0 | NS_ENSURE_TRUE(AppendToString(tagLocalName, aStr), NS_ERROR_OUT_OF_MEMORY); |
1072 | 0 | NS_ENSURE_TRUE(AppendToString(kGreaterThan, aStr), NS_ERROR_OUT_OF_MEMORY); |
1073 | 0 |
|
1074 | 0 | // Keep what follows in sync with the cleanup in the !outputElementEnd case. |
1075 | 0 | PopNameSpaceDeclsFor(aElement); |
1076 | 0 |
|
1077 | 0 | MaybeLeaveFromPreContent(content); |
1078 | 0 |
|
1079 | 0 | if ((mDoFormat || forceFormat) && !mDoRaw && !PreLevel() |
1080 | 0 | && LineBreakAfterClose(content->GetNameSpaceID(), name)) { |
1081 | 0 | NS_ENSURE_TRUE(AppendNewLineToString(aStr), NS_ERROR_OUT_OF_MEMORY); |
1082 | 0 | } |
1083 | 0 | else { |
1084 | 0 | MaybeFlagNewlineForRootNode(aElement); |
1085 | 0 | } |
1086 | 0 |
|
1087 | 0 | AfterElementEnd(content, aStr); |
1088 | 0 |
|
1089 | 0 | return NS_OK; |
1090 | 0 | } |
1091 | | |
1092 | | NS_IMETHODIMP |
1093 | | nsXMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument, |
1094 | | nsAString& aStr) |
1095 | 0 | { |
1096 | 0 | NS_ENSURE_ARG_POINTER(aDocument); |
1097 | 0 |
|
1098 | 0 | nsAutoString version, encoding, standalone; |
1099 | 0 | aDocument->GetXMLDeclaration(version, encoding, standalone); |
1100 | 0 |
|
1101 | 0 | if (version.IsEmpty()) |
1102 | 0 | return NS_OK; // A declaration must have version, or there is no decl |
1103 | 0 | |
1104 | 0 | NS_NAMED_LITERAL_STRING(endQuote, "\""); |
1105 | 0 |
|
1106 | 0 | aStr += NS_LITERAL_STRING("<?xml version=\"") + version + endQuote; |
1107 | 0 |
|
1108 | 0 | if (!mCharset.IsEmpty()) { |
1109 | 0 | aStr += NS_LITERAL_STRING(" encoding=\"") + |
1110 | 0 | NS_ConvertASCIItoUTF16(mCharset) + endQuote; |
1111 | 0 | } |
1112 | 0 | // Otherwise just don't output an encoding attr. Not that we expect |
1113 | 0 | // mCharset to ever be empty. |
1114 | | #ifdef DEBUG |
1115 | | else { |
1116 | | NS_WARNING("Empty mCharset? How come?"); |
1117 | | } |
1118 | | #endif |
1119 | |
|
1120 | 0 | if (!standalone.IsEmpty()) { |
1121 | 0 | aStr += NS_LITERAL_STRING(" standalone=\"") + standalone + endQuote; |
1122 | 0 | } |
1123 | 0 |
|
1124 | 0 | NS_ENSURE_TRUE(aStr.AppendLiteral("?>", mozilla::fallible), NS_ERROR_OUT_OF_MEMORY); |
1125 | 0 | mAddNewlineForRootNode = true; |
1126 | 0 |
|
1127 | 0 | return NS_OK; |
1128 | 0 | } |
1129 | | |
1130 | | bool |
1131 | | nsXMLContentSerializer::CheckElementStart(Element*, |
1132 | | bool& aForceFormat, |
1133 | | nsAString& aStr, |
1134 | | nsresult& aResult) |
1135 | 0 | { |
1136 | 0 | aResult = NS_OK; |
1137 | 0 | aForceFormat = false; |
1138 | 0 | return true; |
1139 | 0 | } |
1140 | | |
1141 | | bool |
1142 | | nsXMLContentSerializer::CheckElementEnd(Element* aElement, |
1143 | | bool& aForceFormat, |
1144 | | nsAString& aStr) |
1145 | 0 | { |
1146 | 0 | // We don't output a separate end tag for empty element |
1147 | 0 | aForceFormat = false; |
1148 | 0 |
|
1149 | 0 | // XXXbz this is a bit messed up, but by now we don't have our fixed-up |
1150 | 0 | // version of aElement anymore. Let's hope fixup never changes the localName |
1151 | 0 | // or namespace... |
1152 | 0 | return ElementNeedsSeparateEndTag(aElement, aElement); |
1153 | 0 | } |
1154 | | |
1155 | | bool |
1156 | | nsXMLContentSerializer::AppendToString(const char16_t aChar, |
1157 | | nsAString& aOutputStr) |
1158 | 0 | { |
1159 | 0 | if (mBodyOnly && !mInBody) { |
1160 | 0 | return true; |
1161 | 0 | } |
1162 | 0 | mColPos += 1; |
1163 | 0 | return aOutputStr.Append(aChar, mozilla::fallible); |
1164 | 0 | } |
1165 | | |
1166 | | bool |
1167 | | nsXMLContentSerializer::AppendToString(const nsAString& aStr, |
1168 | | nsAString& aOutputStr) |
1169 | 0 | { |
1170 | 0 | if (mBodyOnly && !mInBody) { |
1171 | 0 | return true; |
1172 | 0 | } |
1173 | 0 | mColPos += aStr.Length(); |
1174 | 0 | return aOutputStr.Append(aStr, mozilla::fallible); |
1175 | 0 | } |
1176 | | |
1177 | | |
1178 | | static const uint16_t kGTVal = 62; |
1179 | | |
1180 | | #define _ 0 |
1181 | | |
1182 | | // This table indexes into kEntityStrings[]. |
1183 | | static const uint8_t kEntities[] = { |
1184 | | _, _, _, _, _, _, _, _, _, _, |
1185 | | _, _, _, _, _, _, _, _, _, _, |
1186 | | _, _, _, _, _, _, _, _, _, _, |
1187 | | _, _, _, _, _, _, _, _, 2, _, |
1188 | | _, _, _, _, _, _, _, _, _, _, |
1189 | | _, _, _, _, _, _, _, _, _, _, |
1190 | | 3, _, 4 |
1191 | | }; |
1192 | | |
1193 | | // This table indexes into kEntityStrings[]. |
1194 | | static const uint8_t kAttrEntities[] = { |
1195 | | _, _, _, _, _, _, _, _, _, 5, |
1196 | | 6, _, _, 7, _, _, _, _, _, _, |
1197 | | _, _, _, _, _, _, _, _, _, _, |
1198 | | _, _, _, _, 1, _, _, _, 2, _, |
1199 | | _, _, _, _, _, _, _, _, _, _, |
1200 | | _, _, _, _, _, _, _, _, _, _, |
1201 | | 3, _, 4 |
1202 | | }; |
1203 | | |
1204 | | #undef _ |
1205 | | |
1206 | | static const char* const kEntityStrings[] = { |
1207 | | /* 0 */ nullptr, |
1208 | | /* 1 */ """, |
1209 | | /* 2 */ "&", |
1210 | | /* 3 */ "<", |
1211 | | /* 4 */ ">", |
1212 | | /* 5 */ "	", |
1213 | | /* 6 */ "
", |
1214 | | /* 7 */ "
", |
1215 | | }; |
1216 | | |
1217 | | bool |
1218 | | nsXMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr, |
1219 | | nsAString& aOutputStr) |
1220 | 0 | { |
1221 | 0 | nsReadingIterator<char16_t> done_reading; |
1222 | 0 | aStr.EndReading(done_reading); |
1223 | 0 |
|
1224 | 0 | // for each chunk of |aString|... |
1225 | 0 | uint32_t advanceLength = 0; |
1226 | 0 | nsReadingIterator<char16_t> iter; |
1227 | 0 |
|
1228 | 0 | const uint8_t* entityTable = mInAttribute ? kAttrEntities : kEntities; |
1229 | 0 |
|
1230 | 0 | for (aStr.BeginReading(iter); |
1231 | 0 | iter != done_reading; |
1232 | 0 | iter.advance(int32_t(advanceLength))) { |
1233 | 0 | uint32_t fragmentLength = done_reading - iter; |
1234 | 0 | const char16_t* c = iter.get(); |
1235 | 0 | const char16_t* fragmentStart = c; |
1236 | 0 | const char16_t* fragmentEnd = c + fragmentLength; |
1237 | 0 | const char* entityText = nullptr; |
1238 | 0 |
|
1239 | 0 | advanceLength = 0; |
1240 | 0 | // for each character in this chunk, check if it |
1241 | 0 | // needs to be replaced |
1242 | 0 | for (; c < fragmentEnd; c++, advanceLength++) { |
1243 | 0 | char16_t val = *c; |
1244 | 0 | if ((val <= kGTVal) && entityTable[val]) { |
1245 | 0 | entityText = kEntityStrings[entityTable[val]]; |
1246 | 0 | break; |
1247 | 0 | } |
1248 | 0 | } |
1249 | 0 |
|
1250 | 0 | NS_ENSURE_TRUE(aOutputStr.Append(fragmentStart, advanceLength, mozilla::fallible), false); |
1251 | 0 | if (entityText) { |
1252 | 0 | NS_ENSURE_TRUE(AppendASCIItoUTF16(mozilla::MakeStringSpan(entityText), |
1253 | 0 | aOutputStr, |
1254 | 0 | mozilla::fallible), |
1255 | 0 | false); |
1256 | 0 | advanceLength++; |
1257 | 0 | } |
1258 | 0 | } |
1259 | 0 |
|
1260 | 0 | return true; |
1261 | 0 | } |
1262 | | |
1263 | | bool |
1264 | | nsXMLContentSerializer::MaybeAddNewlineForRootNode(nsAString& aStr) |
1265 | 0 | { |
1266 | 0 | if (mAddNewlineForRootNode) { |
1267 | 0 | return AppendNewLineToString(aStr); |
1268 | 0 | } |
1269 | 0 | |
1270 | 0 | return true; |
1271 | 0 | } |
1272 | | |
1273 | | void |
1274 | | nsXMLContentSerializer::MaybeFlagNewlineForRootNode(nsINode* aNode) |
1275 | 0 | { |
1276 | 0 | nsINode* parent = aNode->GetParentNode(); |
1277 | 0 | if (parent) { |
1278 | 0 | mAddNewlineForRootNode = parent->IsDocument(); |
1279 | 0 | } |
1280 | 0 | } |
1281 | | |
1282 | | void |
1283 | | nsXMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode) |
1284 | 0 | { |
1285 | 0 | // support of the xml:space attribute |
1286 | 0 | nsAutoString space; |
1287 | 0 | if (ShouldMaintainPreLevel() && |
1288 | 0 | aNode->IsElement() && |
1289 | 0 | aNode->AsElement()->GetAttr(kNameSpaceID_XML, nsGkAtoms::space, space) && |
1290 | 0 | space.EqualsLiteral("preserve")) { |
1291 | 0 | ++PreLevel(); |
1292 | 0 | } |
1293 | 0 | } |
1294 | | |
1295 | | void |
1296 | | nsXMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode) |
1297 | 0 | { |
1298 | 0 | // support of the xml:space attribute |
1299 | 0 | nsAutoString space; |
1300 | 0 | if (ShouldMaintainPreLevel() && |
1301 | 0 | aNode->IsElement() && |
1302 | 0 | aNode->AsElement()->GetAttr(kNameSpaceID_XML, nsGkAtoms::space, space) && |
1303 | 0 | space.EqualsLiteral("preserve")) { |
1304 | 0 | --PreLevel(); |
1305 | 0 | } |
1306 | 0 | } |
1307 | | |
1308 | | bool |
1309 | | nsXMLContentSerializer::AppendNewLineToString(nsAString& aStr) |
1310 | 0 | { |
1311 | 0 | bool result = AppendToString(mLineBreak, aStr); |
1312 | 0 | mMayIgnoreLineBreakSequence = true; |
1313 | 0 | mColPos = 0; |
1314 | 0 | mAddSpace = false; |
1315 | 0 | mIsIndentationAddedOnCurrentLine = false; |
1316 | 0 | return result; |
1317 | 0 | } |
1318 | | |
1319 | | bool |
1320 | | nsXMLContentSerializer::AppendIndentation(nsAString& aStr) |
1321 | 0 | { |
1322 | 0 | mIsIndentationAddedOnCurrentLine = true; |
1323 | 0 | bool result = AppendToString(mIndent, aStr); |
1324 | 0 | mAddSpace = false; |
1325 | 0 | mMayIgnoreLineBreakSequence = false; |
1326 | 0 | return result; |
1327 | 0 | } |
1328 | | |
1329 | | bool |
1330 | | nsXMLContentSerializer::IncrIndentation(nsAtom* aName) |
1331 | 0 | { |
1332 | 0 | // we want to keep the source readable |
1333 | 0 | if (mDoWrap && |
1334 | 0 | mIndent.Length() >= uint32_t(mMaxColumn) - MIN_INDENTED_LINE_LENGTH) { |
1335 | 0 | ++mIndentOverflow; |
1336 | 0 | } |
1337 | 0 | else { |
1338 | 0 | return mIndent.AppendLiteral(INDENT_STRING, mozilla::fallible); |
1339 | 0 | } |
1340 | 0 |
|
1341 | 0 | return true; |
1342 | 0 | } |
1343 | | |
1344 | | void |
1345 | | nsXMLContentSerializer::DecrIndentation(nsAtom* aName) |
1346 | 0 | { |
1347 | 0 | if(mIndentOverflow) |
1348 | 0 | --mIndentOverflow; |
1349 | 0 | else |
1350 | 0 | mIndent.Cut(0, INDENT_STRING_LENGTH); |
1351 | 0 | } |
1352 | | |
1353 | | bool |
1354 | | nsXMLContentSerializer::LineBreakBeforeOpen(int32_t aNamespaceID, nsAtom* aName) |
1355 | 0 | { |
1356 | 0 | return mAddSpace; |
1357 | 0 | } |
1358 | | |
1359 | | bool |
1360 | | nsXMLContentSerializer::LineBreakAfterOpen(int32_t aNamespaceID, nsAtom* aName) |
1361 | 0 | { |
1362 | 0 | return false; |
1363 | 0 | } |
1364 | | |
1365 | | bool |
1366 | | nsXMLContentSerializer::LineBreakBeforeClose(int32_t aNamespaceID, nsAtom* aName) |
1367 | 0 | { |
1368 | 0 | return mAddSpace; |
1369 | 0 | } |
1370 | | |
1371 | | bool |
1372 | | nsXMLContentSerializer::LineBreakAfterClose(int32_t aNamespaceID, nsAtom* aName) |
1373 | 0 | { |
1374 | 0 | return false; |
1375 | 0 | } |
1376 | | |
1377 | | bool |
1378 | | nsXMLContentSerializer::AppendToStringConvertLF(const nsAString& aStr, |
1379 | | nsAString& aOutputStr) |
1380 | 0 | { |
1381 | 0 | if (mBodyOnly && !mInBody) { |
1382 | 0 | return true; |
1383 | 0 | } |
1384 | 0 | |
1385 | 0 | if (mDoRaw) { |
1386 | 0 | NS_ENSURE_TRUE(AppendToString(aStr, aOutputStr), false); |
1387 | 0 | } |
1388 | 0 | else { |
1389 | 0 | // Convert line-endings to mLineBreak |
1390 | 0 | uint32_t start = 0; |
1391 | 0 | uint32_t theLen = aStr.Length(); |
1392 | 0 | while (start < theLen) { |
1393 | 0 | int32_t eol = aStr.FindChar('\n', start); |
1394 | 0 | if (eol == kNotFound) { |
1395 | 0 | nsDependentSubstring dataSubstring(aStr, start, theLen - start); |
1396 | 0 | NS_ENSURE_TRUE(AppendToString(dataSubstring, aOutputStr), false); |
1397 | 0 | start = theLen; |
1398 | 0 | // if there was a line break before this substring |
1399 | 0 | // AppendNewLineToString was called, so we should reverse |
1400 | 0 | // this flag |
1401 | 0 | mMayIgnoreLineBreakSequence = false; |
1402 | 0 | } |
1403 | 0 | else { |
1404 | 0 | nsDependentSubstring dataSubstring(aStr, start, eol - start); |
1405 | 0 | NS_ENSURE_TRUE(AppendToString(dataSubstring, aOutputStr), false); |
1406 | 0 | NS_ENSURE_TRUE(AppendNewLineToString(aOutputStr), false); |
1407 | 0 | start = eol + 1; |
1408 | 0 | } |
1409 | 0 | } |
1410 | 0 | } |
1411 | 0 |
|
1412 | 0 | return true; |
1413 | 0 | } |
1414 | | |
1415 | | bool |
1416 | | nsXMLContentSerializer::AppendFormatedWrapped_WhitespaceSequence( |
1417 | | nsAString::const_char_iterator &aPos, |
1418 | | const nsAString::const_char_iterator aEnd, |
1419 | | const nsAString::const_char_iterator aSequenceStart, |
1420 | | bool &aMayIgnoreStartOfLineWhitespaceSequence, |
1421 | | nsAString &aOutputStr) |
1422 | 0 | { |
1423 | 0 | // Handle the complete sequence of whitespace. |
1424 | 0 | // Continue to iterate until we find the first non-whitespace char. |
1425 | 0 | // Updates "aPos" to point to the first unhandled char. |
1426 | 0 | // Also updates the aMayIgnoreStartOfLineWhitespaceSequence flag, |
1427 | 0 | // as well as the other "global" state flags. |
1428 | 0 |
|
1429 | 0 | bool sawBlankOrTab = false; |
1430 | 0 | bool leaveLoop = false; |
1431 | 0 |
|
1432 | 0 | do { |
1433 | 0 | switch (*aPos) { |
1434 | 0 | case ' ': |
1435 | 0 | case '\t': |
1436 | 0 | sawBlankOrTab = true; |
1437 | 0 | MOZ_FALLTHROUGH; |
1438 | 0 | case '\n': |
1439 | 0 | ++aPos; |
1440 | 0 | // do not increase mColPos, |
1441 | 0 | // because we will reduce the whitespace to a single char |
1442 | 0 | break; |
1443 | 0 | default: |
1444 | 0 | leaveLoop = true; |
1445 | 0 | break; |
1446 | 0 | } |
1447 | 0 | } while (!leaveLoop && aPos < aEnd); |
1448 | 0 |
|
1449 | 0 | if (mAddSpace) { |
1450 | 0 | // if we had previously been asked to add space, |
1451 | 0 | // our situation has not changed |
1452 | 0 | } |
1453 | 0 | else if (!sawBlankOrTab && mMayIgnoreLineBreakSequence) { |
1454 | 0 | // nothing to do in the case where line breaks have already been added |
1455 | 0 | // before the call of AppendToStringWrapped |
1456 | 0 | // and only if we found line break in the sequence |
1457 | 0 | mMayIgnoreLineBreakSequence = false; |
1458 | 0 | } |
1459 | 0 | else if (aMayIgnoreStartOfLineWhitespaceSequence) { |
1460 | 0 | // nothing to do |
1461 | 0 | aMayIgnoreStartOfLineWhitespaceSequence = false; |
1462 | 0 | } |
1463 | 0 | else { |
1464 | 0 | if (sawBlankOrTab) { |
1465 | 0 | if (mDoWrap && mColPos + 1 >= mMaxColumn) { |
1466 | 0 | // no much sense in delaying, we only have one slot left, |
1467 | 0 | // let's write a break now |
1468 | 0 | bool result = aOutputStr.Append(mLineBreak, mozilla::fallible); |
1469 | 0 | mColPos = 0; |
1470 | 0 | mIsIndentationAddedOnCurrentLine = false; |
1471 | 0 | mMayIgnoreLineBreakSequence = true; |
1472 | 0 | NS_ENSURE_TRUE(result, false); |
1473 | 0 | } |
1474 | 0 | else { |
1475 | 0 | // do not write out yet, we may write out either a space or a linebreak |
1476 | 0 | // let's delay writing it out until we know more |
1477 | 0 | mAddSpace = true; |
1478 | 0 | ++mColPos; // eat a slot of available space |
1479 | 0 | } |
1480 | 0 | } |
1481 | 0 | else { |
1482 | 0 | // Asian text usually does not contain spaces, therefore we should not |
1483 | 0 | // transform a linebreak into a space. |
1484 | 0 | // Since we only saw linebreaks, but no spaces or tabs, |
1485 | 0 | // let's write a linebreak now. |
1486 | 0 | NS_ENSURE_TRUE(AppendNewLineToString(aOutputStr), false); |
1487 | 0 | } |
1488 | 0 | } |
1489 | 0 |
|
1490 | 0 | return true; |
1491 | 0 | } |
1492 | | |
1493 | | bool |
1494 | | nsXMLContentSerializer::AppendWrapped_NonWhitespaceSequence( |
1495 | | nsAString::const_char_iterator &aPos, |
1496 | | const nsAString::const_char_iterator aEnd, |
1497 | | const nsAString::const_char_iterator aSequenceStart, |
1498 | | bool &aMayIgnoreStartOfLineWhitespaceSequence, |
1499 | | bool &aSequenceStartAfterAWhiteSpace, |
1500 | | nsAString& aOutputStr) |
1501 | 0 | { |
1502 | 0 | mMayIgnoreLineBreakSequence = false; |
1503 | 0 | aMayIgnoreStartOfLineWhitespaceSequence = false; |
1504 | 0 |
|
1505 | 0 | // Handle the complete sequence of non-whitespace in this block |
1506 | 0 | // Iterate until we find the first whitespace char or an aEnd condition |
1507 | 0 | // Updates "aPos" to point to the first unhandled char. |
1508 | 0 | // Also updates the aMayIgnoreStartOfLineWhitespaceSequence flag, |
1509 | 0 | // as well as the other "global" state flags. |
1510 | 0 |
|
1511 | 0 | bool thisSequenceStartsAtBeginningOfLine = !mColPos; |
1512 | 0 | bool onceAgainBecauseWeAddedBreakInFront = false; |
1513 | 0 | bool foundWhitespaceInLoop; |
1514 | 0 | uint32_t length, colPos; |
1515 | 0 |
|
1516 | 0 | do { |
1517 | 0 |
|
1518 | 0 | if (mColPos) { |
1519 | 0 | colPos = mColPos; |
1520 | 0 | } |
1521 | 0 | else { |
1522 | 0 | if (mDoFormat && !mDoRaw && !PreLevel() && !onceAgainBecauseWeAddedBreakInFront) { |
1523 | 0 | colPos = mIndent.Length(); |
1524 | 0 | } |
1525 | 0 | else |
1526 | 0 | colPos = 0; |
1527 | 0 | } |
1528 | 0 | foundWhitespaceInLoop = false; |
1529 | 0 | length = 0; |
1530 | 0 | // we iterate until the next whitespace character |
1531 | 0 | // or until we reach the maximum of character per line |
1532 | 0 | // or until the end of the string to add. |
1533 | 0 | do { |
1534 | 0 | if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') { |
1535 | 0 | foundWhitespaceInLoop = true; |
1536 | 0 | break; |
1537 | 0 | } |
1538 | 0 | |
1539 | 0 | ++aPos; |
1540 | 0 | ++length; |
1541 | 0 | } while ( (!mDoWrap || colPos + length < mMaxColumn) && aPos < aEnd); |
1542 | 0 |
|
1543 | 0 | // in the case we don't reached the end of the string, but we reached the maxcolumn, |
1544 | 0 | // we see if there is a whitespace after the maxcolumn |
1545 | 0 | // if yes, then we can append directly the string instead of |
1546 | 0 | // appending a new line etc. |
1547 | 0 | if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') { |
1548 | 0 | foundWhitespaceInLoop = true; |
1549 | 0 | } |
1550 | 0 |
|
1551 | 0 | if (aPos == aEnd || foundWhitespaceInLoop) { |
1552 | 0 | // there is enough room for the complete block we found |
1553 | 0 | if (mDoFormat && !mColPos) { |
1554 | 0 | NS_ENSURE_TRUE(AppendIndentation(aOutputStr), false); |
1555 | 0 | } |
1556 | 0 | else if (mAddSpace) { |
1557 | 0 | bool result = aOutputStr.Append(char16_t(' '), mozilla::fallible); |
1558 | 0 | mAddSpace = false; |
1559 | 0 | NS_ENSURE_TRUE(result, false); |
1560 | 0 | } |
1561 | 0 |
|
1562 | 0 | mColPos += length; |
1563 | 0 | NS_ENSURE_TRUE(aOutputStr.Append(aSequenceStart, aPos - aSequenceStart, mozilla::fallible), false); |
1564 | 0 |
|
1565 | 0 | // We have not yet reached the max column, we will continue to |
1566 | 0 | // fill the current line in the next outer loop iteration |
1567 | 0 | // (this one in AppendToStringWrapped) |
1568 | 0 | // make sure we return in this outer loop |
1569 | 0 | onceAgainBecauseWeAddedBreakInFront = false; |
1570 | 0 | } |
1571 | 0 | else { // we reach the max column |
1572 | 0 | if (!thisSequenceStartsAtBeginningOfLine && |
1573 | 0 | (mAddSpace || (!mDoFormat && aSequenceStartAfterAWhiteSpace))) { |
1574 | 0 | // when !mDoFormat, mAddSpace is not used, mAddSpace is always false |
1575 | 0 | // so, in the case where mDoWrap && !mDoFormat, if we want to enter in this condition... |
1576 | 0 |
|
1577 | 0 | // We can avoid to wrap. We try to add the whole block |
1578 | 0 | // in an empty new line |
1579 | 0 |
|
1580 | 0 | NS_ENSURE_TRUE(AppendNewLineToString(aOutputStr), false); |
1581 | 0 | aPos = aSequenceStart; |
1582 | 0 | thisSequenceStartsAtBeginningOfLine = true; |
1583 | 0 | onceAgainBecauseWeAddedBreakInFront = true; |
1584 | 0 | } |
1585 | 0 | else { |
1586 | 0 | // we must wrap |
1587 | 0 | onceAgainBecauseWeAddedBreakInFront = false; |
1588 | 0 | bool foundWrapPosition = false; |
1589 | 0 | int32_t wrapPosition = 0; |
1590 | 0 |
|
1591 | 0 | if (mAllowLineBreaking) { |
1592 | 0 | mozilla::intl::LineBreaker* lineBreaker = |
1593 | 0 | nsContentUtils::LineBreaker(); |
1594 | 0 |
|
1595 | 0 | wrapPosition = lineBreaker->Prev(aSequenceStart, |
1596 | 0 | (aEnd - aSequenceStart), |
1597 | 0 | (aPos - aSequenceStart) + 1); |
1598 | 0 | if (wrapPosition != NS_LINEBREAKER_NEED_MORE_TEXT) { |
1599 | 0 | foundWrapPosition = true; |
1600 | 0 | } |
1601 | 0 | else { |
1602 | 0 | wrapPosition = lineBreaker->Next(aSequenceStart, |
1603 | 0 | (aEnd - aSequenceStart), |
1604 | 0 | (aPos - aSequenceStart)); |
1605 | 0 | if (wrapPosition != NS_LINEBREAKER_NEED_MORE_TEXT) { |
1606 | 0 | foundWrapPosition = true; |
1607 | 0 | } |
1608 | 0 | } |
1609 | 0 | } |
1610 | 0 |
|
1611 | 0 | if (foundWrapPosition) { |
1612 | 0 | if (!mColPos && mDoFormat) { |
1613 | 0 | NS_ENSURE_TRUE(AppendIndentation(aOutputStr), false); |
1614 | 0 | } |
1615 | 0 | else if (mAddSpace) { |
1616 | 0 | bool result = aOutputStr.Append(char16_t(' '), mozilla::fallible); |
1617 | 0 | mAddSpace = false; |
1618 | 0 | NS_ENSURE_TRUE(result, false); |
1619 | 0 | } |
1620 | 0 | NS_ENSURE_TRUE(aOutputStr.Append(aSequenceStart, wrapPosition, mozilla::fallible), false); |
1621 | 0 |
|
1622 | 0 | NS_ENSURE_TRUE(AppendNewLineToString(aOutputStr), false); |
1623 | 0 | aPos = aSequenceStart + wrapPosition; |
1624 | 0 | aMayIgnoreStartOfLineWhitespaceSequence = true; |
1625 | 0 | } |
1626 | 0 | else { |
1627 | 0 | // try some simple fallback logic |
1628 | 0 | // go forward up to the next whitespace position, |
1629 | 0 | // in the worst case this will be all the rest of the data |
1630 | 0 |
|
1631 | 0 | // we update the mColPos variable with the length of |
1632 | 0 | // the part already parsed. |
1633 | 0 | mColPos += length; |
1634 | 0 |
|
1635 | 0 | // now try to find the next whitespace |
1636 | 0 | do { |
1637 | 0 | if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') { |
1638 | 0 | break; |
1639 | 0 | } |
1640 | 0 | |
1641 | 0 | ++aPos; |
1642 | 0 | ++mColPos; |
1643 | 0 | } while (aPos < aEnd); |
1644 | 0 |
|
1645 | 0 | if (mAddSpace) { |
1646 | 0 | bool result = aOutputStr.Append(char16_t(' '), mozilla::fallible); |
1647 | 0 | mAddSpace = false; |
1648 | 0 | NS_ENSURE_TRUE(result, false); |
1649 | 0 | } |
1650 | 0 | NS_ENSURE_TRUE(aOutputStr.Append(aSequenceStart, aPos - aSequenceStart, mozilla::fallible), false); |
1651 | 0 | } |
1652 | 0 | } |
1653 | 0 | aSequenceStartAfterAWhiteSpace = false; |
1654 | 0 | } |
1655 | 0 | } while (onceAgainBecauseWeAddedBreakInFront); |
1656 | 0 |
|
1657 | 0 | return true; |
1658 | 0 | } |
1659 | | |
1660 | | bool |
1661 | | nsXMLContentSerializer::AppendToStringFormatedWrapped(const nsAString& aStr, |
1662 | | nsAString& aOutputStr) |
1663 | 0 | { |
1664 | 0 | if (mBodyOnly && !mInBody) { |
1665 | 0 | return true; |
1666 | 0 | } |
1667 | 0 | |
1668 | 0 | nsAString::const_char_iterator pos, end, sequenceStart; |
1669 | 0 |
|
1670 | 0 | aStr.BeginReading(pos); |
1671 | 0 | aStr.EndReading(end); |
1672 | 0 |
|
1673 | 0 | bool sequenceStartAfterAWhitespace = false; |
1674 | 0 | if (pos < end) { |
1675 | 0 | nsAString::const_char_iterator end2; |
1676 | 0 | aOutputStr.EndReading(end2); |
1677 | 0 | --end2; |
1678 | 0 | if (*end2 == ' ' || *end2 == '\n' || *end2 == '\t') { |
1679 | 0 | sequenceStartAfterAWhitespace = true; |
1680 | 0 | } |
1681 | 0 | } |
1682 | 0 |
|
1683 | 0 | // if the current line already has text on it, such as a tag, |
1684 | 0 | // leading whitespace is significant |
1685 | 0 | bool mayIgnoreStartOfLineWhitespaceSequence = |
1686 | 0 | (!mColPos || (mIsIndentationAddedOnCurrentLine && |
1687 | 0 | sequenceStartAfterAWhitespace && |
1688 | 0 | uint32_t(mColPos) == mIndent.Length())); |
1689 | 0 |
|
1690 | 0 | while (pos < end) { |
1691 | 0 | sequenceStart = pos; |
1692 | 0 |
|
1693 | 0 | // if beginning of a whitespace sequence |
1694 | 0 | if (*pos == ' ' || *pos == '\n' || *pos == '\t') { |
1695 | 0 | NS_ENSURE_TRUE(AppendFormatedWrapped_WhitespaceSequence(pos, end, sequenceStart, |
1696 | 0 | mayIgnoreStartOfLineWhitespaceSequence, aOutputStr), false); |
1697 | 0 | } |
1698 | 0 | else { // any other non-whitespace char |
1699 | 0 | NS_ENSURE_TRUE(AppendWrapped_NonWhitespaceSequence(pos, end, sequenceStart, |
1700 | 0 | mayIgnoreStartOfLineWhitespaceSequence, sequenceStartAfterAWhitespace, |
1701 | 0 | aOutputStr), false); |
1702 | 0 | } |
1703 | 0 | } |
1704 | 0 |
|
1705 | 0 | return true; |
1706 | 0 | } |
1707 | | |
1708 | | bool |
1709 | | nsXMLContentSerializer::AppendWrapped_WhitespaceSequence( |
1710 | | nsAString::const_char_iterator &aPos, |
1711 | | const nsAString::const_char_iterator aEnd, |
1712 | | const nsAString::const_char_iterator aSequenceStart, |
1713 | | nsAString &aOutputStr) |
1714 | 0 | { |
1715 | 0 | // Handle the complete sequence of whitespace. |
1716 | 0 | // Continue to iterate until we find the first non-whitespace char. |
1717 | 0 | // Updates "aPos" to point to the first unhandled char. |
1718 | 0 | mAddSpace = false; |
1719 | 0 | mIsIndentationAddedOnCurrentLine = false; |
1720 | 0 |
|
1721 | 0 | bool leaveLoop = false; |
1722 | 0 | nsAString::const_char_iterator lastPos = aPos; |
1723 | 0 |
|
1724 | 0 | do { |
1725 | 0 | switch (*aPos) { |
1726 | 0 | case ' ': |
1727 | 0 | case '\t': |
1728 | 0 | // if there are too many spaces on a line, we wrap |
1729 | 0 | if (mColPos >= mMaxColumn) { |
1730 | 0 | if (lastPos != aPos) { |
1731 | 0 | NS_ENSURE_TRUE(aOutputStr.Append(lastPos, aPos - lastPos, mozilla::fallible), false); |
1732 | 0 | } |
1733 | 0 | NS_ENSURE_TRUE(AppendToString(mLineBreak, aOutputStr), false); |
1734 | 0 | mColPos = 0; |
1735 | 0 | lastPos = aPos; |
1736 | 0 | } |
1737 | 0 |
|
1738 | 0 | ++mColPos; |
1739 | 0 | ++aPos; |
1740 | 0 | break; |
1741 | 0 | case '\n': |
1742 | 0 | if (lastPos != aPos) { |
1743 | 0 | NS_ENSURE_TRUE(aOutputStr.Append(lastPos, aPos - lastPos, mozilla::fallible), false); |
1744 | 0 | } |
1745 | 0 | NS_ENSURE_TRUE(AppendToString(mLineBreak, aOutputStr), false); |
1746 | 0 | mColPos = 0; |
1747 | 0 | ++aPos; |
1748 | 0 | lastPos = aPos; |
1749 | 0 | break; |
1750 | 0 | default: |
1751 | 0 | leaveLoop = true; |
1752 | 0 | break; |
1753 | 0 | } |
1754 | 0 | } while (!leaveLoop && aPos < aEnd); |
1755 | 0 |
|
1756 | 0 | if (lastPos != aPos) { |
1757 | 0 | NS_ENSURE_TRUE(aOutputStr.Append(lastPos, aPos - lastPos, mozilla::fallible), false); |
1758 | 0 | } |
1759 | 0 |
|
1760 | 0 | return true; |
1761 | 0 | } |
1762 | | |
1763 | | bool |
1764 | | nsXMLContentSerializer::AppendToStringWrapped(const nsAString& aStr, |
1765 | | nsAString& aOutputStr) |
1766 | 0 | { |
1767 | 0 | if (mBodyOnly && !mInBody) { |
1768 | 0 | return true; |
1769 | 0 | } |
1770 | 0 | |
1771 | 0 | nsAString::const_char_iterator pos, end, sequenceStart; |
1772 | 0 |
|
1773 | 0 | aStr.BeginReading(pos); |
1774 | 0 | aStr.EndReading(end); |
1775 | 0 |
|
1776 | 0 | // not used in this case, but needed by AppendWrapped_NonWhitespaceSequence |
1777 | 0 | bool mayIgnoreStartOfLineWhitespaceSequence = false; |
1778 | 0 | mMayIgnoreLineBreakSequence = false; |
1779 | 0 |
|
1780 | 0 | bool sequenceStartAfterAWhitespace = false; |
1781 | 0 | if (pos < end && !aOutputStr.IsEmpty()) { |
1782 | 0 | nsAString::const_char_iterator end2; |
1783 | 0 | aOutputStr.EndReading(end2); |
1784 | 0 | --end2; |
1785 | 0 | if (*end2 == ' ' || *end2 == '\n' || *end2 == '\t') { |
1786 | 0 | sequenceStartAfterAWhitespace = true; |
1787 | 0 | } |
1788 | 0 | } |
1789 | 0 |
|
1790 | 0 | while (pos < end) { |
1791 | 0 | sequenceStart = pos; |
1792 | 0 |
|
1793 | 0 | // if beginning of a whitespace sequence |
1794 | 0 | if (*pos == ' ' || *pos == '\n' || *pos == '\t') { |
1795 | 0 | sequenceStartAfterAWhitespace = true; |
1796 | 0 | NS_ENSURE_TRUE(AppendWrapped_WhitespaceSequence(pos, end, |
1797 | 0 | sequenceStart, aOutputStr), false); |
1798 | 0 | } |
1799 | 0 | else { // any other non-whitespace char |
1800 | 0 | NS_ENSURE_TRUE(AppendWrapped_NonWhitespaceSequence(pos, end, sequenceStart, |
1801 | 0 | mayIgnoreStartOfLineWhitespaceSequence, |
1802 | 0 | sequenceStartAfterAWhitespace, aOutputStr), false); |
1803 | 0 | } |
1804 | 0 | } |
1805 | 0 |
|
1806 | 0 | return true; |
1807 | 0 | } |
1808 | | |
1809 | | bool |
1810 | | nsXMLContentSerializer::ShouldMaintainPreLevel() const |
1811 | 0 | { |
1812 | 0 | // Only attempt to maintain the pre level for consumers who care about it. |
1813 | 0 | return !mDoRaw || (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre); |
1814 | 0 | } |
1815 | | |
1816 | | bool |
1817 | | nsXMLContentSerializer::MaybeSerializeIsValue(Element* aElement, |
1818 | | nsAString& aStr) |
1819 | 0 | { |
1820 | 0 | CustomElementData* ceData = aElement->GetCustomElementData(); |
1821 | 0 | if (ceData) { |
1822 | 0 | nsAtom* isAttr = ceData->GetIs(aElement); |
1823 | 0 | if (isAttr && !aElement->HasAttr(kNameSpaceID_None, nsGkAtoms::is)) { |
1824 | 0 | NS_ENSURE_TRUE(aStr.AppendLiteral(" is=\"", mozilla::fallible), false); |
1825 | 0 | NS_ENSURE_TRUE(aStr.Append(nsDependentAtomString(isAttr), |
1826 | 0 | mozilla::fallible), |
1827 | 0 | false); |
1828 | 0 | NS_ENSURE_TRUE(aStr.AppendLiteral("\"", mozilla::fallible), false); |
1829 | 0 | } |
1830 | 0 | } |
1831 | 0 |
|
1832 | 0 | return true; |
1833 | 0 | } |