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