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