1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsXMLContentSerializer.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1713 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +/* 1.10 + * nsIContentSerializer implementation that can be used with an 1.11 + * nsIDocumentEncoder to convert an XML DOM to an XML string that 1.12 + * could be parsed into more or less the original DOM. 1.13 + */ 1.14 + 1.15 +#include "nsXMLContentSerializer.h" 1.16 + 1.17 +#include "nsGkAtoms.h" 1.18 +#include "nsIDOMProcessingInstruction.h" 1.19 +#include "nsIDOMComment.h" 1.20 +#include "nsIDOMDocumentType.h" 1.21 +#include "nsIContent.h" 1.22 +#include "nsIDocument.h" 1.23 +#include "nsIDocumentEncoder.h" 1.24 +#include "nsNameSpaceManager.h" 1.25 +#include "nsTextFragment.h" 1.26 +#include "nsString.h" 1.27 +#include "prprf.h" 1.28 +#include "nsUnicharUtils.h" 1.29 +#include "nsCRT.h" 1.30 +#include "nsContentUtils.h" 1.31 +#include "nsAttrName.h" 1.32 +#include "nsILineBreaker.h" 1.33 +#include "mozilla/dom/Element.h" 1.34 +#include "nsParserConstants.h" 1.35 + 1.36 +using namespace mozilla::dom; 1.37 + 1.38 +#define kXMLNS "xmlns" 1.39 + 1.40 +// to be readable, we assume that an indented line contains 1.41 +// at least this number of characters (arbitrary value here). 1.42 +// This is a limit for the indentation. 1.43 +#define MIN_INDENTED_LINE_LENGTH 15 1.44 + 1.45 +// the string used to indent. 1.46 +#define INDENT_STRING " " 1.47 +#define INDENT_STRING_LENGTH 2 1.48 + 1.49 +nsresult NS_NewXMLContentSerializer(nsIContentSerializer** aSerializer) 1.50 +{ 1.51 + nsXMLContentSerializer* it = new nsXMLContentSerializer(); 1.52 + if (!it) { 1.53 + return NS_ERROR_OUT_OF_MEMORY; 1.54 + } 1.55 + 1.56 + return CallQueryInterface(it, aSerializer); 1.57 +} 1.58 + 1.59 +nsXMLContentSerializer::nsXMLContentSerializer() 1.60 + : mPrefixIndex(0), 1.61 + mColPos(0), 1.62 + mIndentOverflow(0), 1.63 + mIsIndentationAddedOnCurrentLine(false), 1.64 + mInAttribute(false), 1.65 + mAddNewlineForRootNode(false), 1.66 + mAddSpace(false), 1.67 + mMayIgnoreLineBreakSequence(false), 1.68 + mBodyOnly(false), 1.69 + mInBody(0) 1.70 +{ 1.71 +} 1.72 + 1.73 +nsXMLContentSerializer::~nsXMLContentSerializer() 1.74 +{ 1.75 +} 1.76 + 1.77 +NS_IMPL_ISUPPORTS(nsXMLContentSerializer, nsIContentSerializer) 1.78 + 1.79 +NS_IMETHODIMP 1.80 +nsXMLContentSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn, 1.81 + const char* aCharSet, bool aIsCopying, 1.82 + bool aRewriteEncodingDeclaration) 1.83 +{ 1.84 + mPrefixIndex = 0; 1.85 + mColPos = 0; 1.86 + mIndentOverflow = 0; 1.87 + mIsIndentationAddedOnCurrentLine = false; 1.88 + mInAttribute = false; 1.89 + mAddNewlineForRootNode = false; 1.90 + mAddSpace = false; 1.91 + mMayIgnoreLineBreakSequence = false; 1.92 + mBodyOnly = false; 1.93 + mInBody = 0; 1.94 + 1.95 + mCharset = aCharSet; 1.96 + mFlags = aFlags; 1.97 + 1.98 + // Set the line break character: 1.99 + if ((mFlags & nsIDocumentEncoder::OutputCRLineBreak) 1.100 + && (mFlags & nsIDocumentEncoder::OutputLFLineBreak)) { // Windows 1.101 + mLineBreak.AssignLiteral("\r\n"); 1.102 + } 1.103 + else if (mFlags & nsIDocumentEncoder::OutputCRLineBreak) { // Mac 1.104 + mLineBreak.AssignLiteral("\r"); 1.105 + } 1.106 + else if (mFlags & nsIDocumentEncoder::OutputLFLineBreak) { // Unix/DOM 1.107 + mLineBreak.AssignLiteral("\n"); 1.108 + } 1.109 + else { 1.110 + mLineBreak.AssignLiteral(NS_LINEBREAK); // Platform/default 1.111 + } 1.112 + 1.113 + mDoRaw = !!(mFlags & nsIDocumentEncoder::OutputRaw); 1.114 + 1.115 + mDoFormat = (mFlags & nsIDocumentEncoder::OutputFormatted && !mDoRaw); 1.116 + 1.117 + mDoWrap = (mFlags & nsIDocumentEncoder::OutputWrap && !mDoRaw); 1.118 + 1.119 + if (!aWrapColumn) { 1.120 + mMaxColumn = 72; 1.121 + } 1.122 + else { 1.123 + mMaxColumn = aWrapColumn; 1.124 + } 1.125 + 1.126 + mPreLevel = 0; 1.127 + mIsIndentationAddedOnCurrentLine = false; 1.128 + return NS_OK; 1.129 +} 1.130 + 1.131 +nsresult 1.132 +nsXMLContentSerializer::AppendTextData(nsIContent* aNode, 1.133 + int32_t aStartOffset, 1.134 + int32_t aEndOffset, 1.135 + nsAString& aStr, 1.136 + bool aTranslateEntities) 1.137 +{ 1.138 + nsIContent* content = aNode; 1.139 + const nsTextFragment* frag; 1.140 + if (!content || !(frag = content->GetText())) { 1.141 + return NS_ERROR_FAILURE; 1.142 + } 1.143 + 1.144 + int32_t fragLength = frag->GetLength(); 1.145 + int32_t endoffset = (aEndOffset == -1) ? fragLength : std::min(aEndOffset, fragLength); 1.146 + int32_t length = endoffset - aStartOffset; 1.147 + 1.148 + NS_ASSERTION(aStartOffset >= 0, "Negative start offset for text fragment!"); 1.149 + NS_ASSERTION(aStartOffset <= endoffset, "A start offset is beyond the end of the text fragment!"); 1.150 + 1.151 + if (length <= 0) { 1.152 + // XXX Zero is a legal value, maybe non-zero values should be an 1.153 + // error. 1.154 + return NS_OK; 1.155 + } 1.156 + 1.157 + if (frag->Is2b()) { 1.158 + const char16_t *strStart = frag->Get2b() + aStartOffset; 1.159 + if (aTranslateEntities) { 1.160 + AppendAndTranslateEntities(Substring(strStart, strStart + length), aStr); 1.161 + } 1.162 + else { 1.163 + aStr.Append(Substring(strStart, strStart + length)); 1.164 + } 1.165 + } 1.166 + else { 1.167 + if (aTranslateEntities) { 1.168 + AppendAndTranslateEntities(NS_ConvertASCIItoUTF16(frag->Get1b()+aStartOffset, length), aStr); 1.169 + } 1.170 + else { 1.171 + aStr.Append(NS_ConvertASCIItoUTF16(frag->Get1b()+aStartOffset, length)); 1.172 + } 1.173 + } 1.174 + 1.175 + return NS_OK; 1.176 +} 1.177 + 1.178 +NS_IMETHODIMP 1.179 +nsXMLContentSerializer::AppendText(nsIContent* aText, 1.180 + int32_t aStartOffset, 1.181 + int32_t aEndOffset, 1.182 + nsAString& aStr) 1.183 +{ 1.184 + NS_ENSURE_ARG(aText); 1.185 + 1.186 + nsAutoString data; 1.187 + nsresult rv; 1.188 + 1.189 + rv = AppendTextData(aText, aStartOffset, aEndOffset, data, true); 1.190 + if (NS_FAILED(rv)) 1.191 + return NS_ERROR_FAILURE; 1.192 + 1.193 + if (mPreLevel > 0 || mDoRaw) { 1.194 + AppendToStringConvertLF(data, aStr); 1.195 + } 1.196 + else if (mDoFormat) { 1.197 + AppendToStringFormatedWrapped(data, aStr); 1.198 + } 1.199 + else if (mDoWrap) { 1.200 + AppendToStringWrapped(data, aStr); 1.201 + } 1.202 + else { 1.203 + AppendToStringConvertLF(data, aStr); 1.204 + } 1.205 + 1.206 + return NS_OK; 1.207 +} 1.208 + 1.209 +NS_IMETHODIMP 1.210 +nsXMLContentSerializer::AppendCDATASection(nsIContent* aCDATASection, 1.211 + int32_t aStartOffset, 1.212 + int32_t aEndOffset, 1.213 + nsAString& aStr) 1.214 +{ 1.215 + NS_ENSURE_ARG(aCDATASection); 1.216 + nsresult rv; 1.217 + 1.218 + NS_NAMED_LITERAL_STRING(cdata , "<![CDATA["); 1.219 + 1.220 + if (mPreLevel > 0 || mDoRaw) { 1.221 + AppendToString(cdata, aStr); 1.222 + } 1.223 + else if (mDoFormat) { 1.224 + AppendToStringFormatedWrapped(cdata, aStr); 1.225 + } 1.226 + else if (mDoWrap) { 1.227 + AppendToStringWrapped(cdata, aStr); 1.228 + } 1.229 + else { 1.230 + AppendToString(cdata, aStr); 1.231 + } 1.232 + 1.233 + nsAutoString data; 1.234 + rv = AppendTextData(aCDATASection, aStartOffset, aEndOffset, data, false); 1.235 + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; 1.236 + 1.237 + AppendToStringConvertLF(data, aStr); 1.238 + 1.239 + AppendToString(NS_LITERAL_STRING("]]>"), aStr); 1.240 + 1.241 + return NS_OK; 1.242 +} 1.243 + 1.244 +NS_IMETHODIMP 1.245 +nsXMLContentSerializer::AppendProcessingInstruction(nsIContent* aPI, 1.246 + int32_t aStartOffset, 1.247 + int32_t aEndOffset, 1.248 + nsAString& aStr) 1.249 +{ 1.250 + nsCOMPtr<nsIDOMProcessingInstruction> pi = do_QueryInterface(aPI); 1.251 + NS_ENSURE_ARG(pi); 1.252 + nsresult rv; 1.253 + nsAutoString target, data, start; 1.254 + 1.255 + MaybeAddNewlineForRootNode(aStr); 1.256 + 1.257 + rv = pi->GetTarget(target); 1.258 + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; 1.259 + 1.260 + rv = pi->GetData(data); 1.261 + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; 1.262 + 1.263 + start.AppendLiteral("<?"); 1.264 + start.Append(target); 1.265 + 1.266 + if (mPreLevel > 0 || mDoRaw) { 1.267 + AppendToString(start, aStr); 1.268 + } 1.269 + else if (mDoFormat) { 1.270 + if (mAddSpace) { 1.271 + AppendNewLineToString(aStr); 1.272 + } 1.273 + AppendToStringFormatedWrapped(start, aStr); 1.274 + } 1.275 + else if (mDoWrap) { 1.276 + AppendToStringWrapped(start, aStr); 1.277 + } 1.278 + else { 1.279 + AppendToString(start, aStr); 1.280 + } 1.281 + 1.282 + if (!data.IsEmpty()) { 1.283 + AppendToString(char16_t(' '), aStr); 1.284 + AppendToStringConvertLF(data, aStr); 1.285 + } 1.286 + AppendToString(NS_LITERAL_STRING("?>"), aStr); 1.287 + 1.288 + MaybeFlagNewlineForRootNode(aPI); 1.289 + 1.290 + return NS_OK; 1.291 +} 1.292 + 1.293 +NS_IMETHODIMP 1.294 +nsXMLContentSerializer::AppendComment(nsIContent* aComment, 1.295 + int32_t aStartOffset, 1.296 + int32_t aEndOffset, 1.297 + nsAString& aStr) 1.298 +{ 1.299 + nsCOMPtr<nsIDOMComment> comment = do_QueryInterface(aComment); 1.300 + NS_ENSURE_ARG(comment); 1.301 + nsresult rv; 1.302 + nsAutoString data; 1.303 + 1.304 + rv = comment->GetData(data); 1.305 + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; 1.306 + 1.307 + int32_t dataLength = data.Length(); 1.308 + if (aStartOffset || (aEndOffset != -1 && aEndOffset < dataLength)) { 1.309 + int32_t length = 1.310 + (aEndOffset == -1) ? dataLength : std::min(aEndOffset, dataLength); 1.311 + length -= aStartOffset; 1.312 + 1.313 + nsAutoString frag; 1.314 + if (length > 0) { 1.315 + data.Mid(frag, aStartOffset, length); 1.316 + } 1.317 + data.Assign(frag); 1.318 + } 1.319 + 1.320 + MaybeAddNewlineForRootNode(aStr); 1.321 + 1.322 + NS_NAMED_LITERAL_STRING(startComment, "<!--"); 1.323 + 1.324 + if (mPreLevel > 0 || mDoRaw) { 1.325 + AppendToString(startComment, aStr); 1.326 + } 1.327 + else if (mDoFormat) { 1.328 + if (mAddSpace) { 1.329 + AppendNewLineToString(aStr); 1.330 + } 1.331 + AppendToStringFormatedWrapped(startComment, aStr); 1.332 + } 1.333 + else if (mDoWrap) { 1.334 + AppendToStringWrapped(startComment, aStr); 1.335 + } 1.336 + else { 1.337 + AppendToString(startComment, aStr); 1.338 + } 1.339 + 1.340 + // Even if mDoformat, we don't format the content because it 1.341 + // could have been preformated by the author 1.342 + AppendToStringConvertLF(data, aStr); 1.343 + AppendToString(NS_LITERAL_STRING("-->"), aStr); 1.344 + 1.345 + MaybeFlagNewlineForRootNode(aComment); 1.346 + 1.347 + return NS_OK; 1.348 +} 1.349 + 1.350 +NS_IMETHODIMP 1.351 +nsXMLContentSerializer::AppendDoctype(nsIContent* aDocType, 1.352 + nsAString& aStr) 1.353 +{ 1.354 + nsCOMPtr<nsIDOMDocumentType> docType = do_QueryInterface(aDocType); 1.355 + NS_ENSURE_ARG(docType); 1.356 + nsresult rv; 1.357 + nsAutoString name, publicId, systemId, internalSubset; 1.358 + 1.359 + rv = docType->GetName(name); 1.360 + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; 1.361 + rv = docType->GetPublicId(publicId); 1.362 + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; 1.363 + rv = docType->GetSystemId(systemId); 1.364 + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; 1.365 + rv = docType->GetInternalSubset(internalSubset); 1.366 + if (NS_FAILED(rv)) return NS_ERROR_FAILURE; 1.367 + 1.368 + MaybeAddNewlineForRootNode(aStr); 1.369 + 1.370 + AppendToString(NS_LITERAL_STRING("<!DOCTYPE "), aStr); 1.371 + AppendToString(name, aStr); 1.372 + 1.373 + char16_t quote; 1.374 + if (!publicId.IsEmpty()) { 1.375 + AppendToString(NS_LITERAL_STRING(" PUBLIC "), aStr); 1.376 + if (publicId.FindChar(char16_t('"')) == -1) { 1.377 + quote = char16_t('"'); 1.378 + } 1.379 + else { 1.380 + quote = char16_t('\''); 1.381 + } 1.382 + AppendToString(quote, aStr); 1.383 + AppendToString(publicId, aStr); 1.384 + AppendToString(quote, aStr); 1.385 + 1.386 + if (!systemId.IsEmpty()) { 1.387 + AppendToString(char16_t(' '), aStr); 1.388 + if (systemId.FindChar(char16_t('"')) == -1) { 1.389 + quote = char16_t('"'); 1.390 + } 1.391 + else { 1.392 + quote = char16_t('\''); 1.393 + } 1.394 + AppendToString(quote, aStr); 1.395 + AppendToString(systemId, aStr); 1.396 + AppendToString(quote, aStr); 1.397 + } 1.398 + } 1.399 + else if (!systemId.IsEmpty()) { 1.400 + if (systemId.FindChar(char16_t('"')) == -1) { 1.401 + quote = char16_t('"'); 1.402 + } 1.403 + else { 1.404 + quote = char16_t('\''); 1.405 + } 1.406 + AppendToString(NS_LITERAL_STRING(" SYSTEM "), aStr); 1.407 + AppendToString(quote, aStr); 1.408 + AppendToString(systemId, aStr); 1.409 + AppendToString(quote, aStr); 1.410 + } 1.411 + 1.412 + if (!internalSubset.IsEmpty()) { 1.413 + AppendToString(NS_LITERAL_STRING(" ["), aStr); 1.414 + AppendToString(internalSubset, aStr); 1.415 + AppendToString(char16_t(']'), aStr); 1.416 + } 1.417 + 1.418 + AppendToString(kGreaterThan, aStr); 1.419 + MaybeFlagNewlineForRootNode(aDocType); 1.420 + 1.421 + return NS_OK; 1.422 +} 1.423 + 1.424 +nsresult 1.425 +nsXMLContentSerializer::PushNameSpaceDecl(const nsAString& aPrefix, 1.426 + const nsAString& aURI, 1.427 + nsIContent* aOwner) 1.428 +{ 1.429 + NameSpaceDecl* decl = mNameSpaceStack.AppendElement(); 1.430 + if (!decl) return NS_ERROR_OUT_OF_MEMORY; 1.431 + 1.432 + decl->mPrefix.Assign(aPrefix); 1.433 + decl->mURI.Assign(aURI); 1.434 + // Don't addref - this weak reference will be removed when 1.435 + // we pop the stack 1.436 + decl->mOwner = aOwner; 1.437 + return NS_OK; 1.438 +} 1.439 + 1.440 +void 1.441 +nsXMLContentSerializer::PopNameSpaceDeclsFor(nsIContent* aOwner) 1.442 +{ 1.443 + int32_t index, count; 1.444 + 1.445 + count = mNameSpaceStack.Length(); 1.446 + for (index = count - 1; index >= 0; index--) { 1.447 + if (mNameSpaceStack[index].mOwner != aOwner) { 1.448 + break; 1.449 + } 1.450 + mNameSpaceStack.RemoveElementAt(index); 1.451 + } 1.452 +} 1.453 + 1.454 +bool 1.455 +nsXMLContentSerializer::ConfirmPrefix(nsAString& aPrefix, 1.456 + const nsAString& aURI, 1.457 + nsIContent* aElement, 1.458 + bool aIsAttribute) 1.459 +{ 1.460 + if (aPrefix.EqualsLiteral(kXMLNS)) { 1.461 + return false; 1.462 + } 1.463 + 1.464 + if (aURI.EqualsLiteral("http://www.w3.org/XML/1998/namespace")) { 1.465 + // The prefix must be xml for this namespace. We don't need to declare it, 1.466 + // so always just set the prefix to xml. 1.467 + aPrefix.AssignLiteral("xml"); 1.468 + 1.469 + return false; 1.470 + } 1.471 + 1.472 + bool mustHavePrefix; 1.473 + if (aIsAttribute) { 1.474 + if (aURI.IsEmpty()) { 1.475 + // Attribute in the null namespace. This just shouldn't have a prefix. 1.476 + // And there's no need to push any namespace decls 1.477 + aPrefix.Truncate(); 1.478 + return false; 1.479 + } 1.480 + 1.481 + // Attribute not in the null namespace -- must have a prefix 1.482 + mustHavePrefix = true; 1.483 + } else { 1.484 + // Not an attribute, so doesn't _have_ to have a prefix 1.485 + mustHavePrefix = false; 1.486 + } 1.487 + 1.488 + // Keep track of the closest prefix that's bound to aURI and whether we've 1.489 + // found such a thing. closestURIMatch holds the prefix, and uriMatch 1.490 + // indicates whether we actually have one. 1.491 + nsAutoString closestURIMatch; 1.492 + bool uriMatch = false; 1.493 + 1.494 + // Also keep track of whether we've seen aPrefix already. If we have, that 1.495 + // means that it's already bound to a URI different from aURI, so even if we 1.496 + // later (so in a more outer scope) see it bound to aURI we can't reuse it. 1.497 + bool haveSeenOurPrefix = false; 1.498 + 1.499 + int32_t count = mNameSpaceStack.Length(); 1.500 + int32_t index = count - 1; 1.501 + while (index >= 0) { 1.502 + NameSpaceDecl& decl = mNameSpaceStack.ElementAt(index); 1.503 + // Check if we've found a prefix match 1.504 + if (aPrefix.Equals(decl.mPrefix)) { 1.505 + 1.506 + // If the URIs match and aPrefix is not bound to any other URI, we can 1.507 + // use aPrefix 1.508 + if (!haveSeenOurPrefix && aURI.Equals(decl.mURI)) { 1.509 + // Just use our uriMatch stuff. That will deal with an empty aPrefix 1.510 + // the right way. We can break out of the loop now, though. 1.511 + uriMatch = true; 1.512 + closestURIMatch = aPrefix; 1.513 + break; 1.514 + } 1.515 + 1.516 + haveSeenOurPrefix = true; 1.517 + 1.518 + // If they don't, and either: 1.519 + // 1) We have a prefix (so we'd be redeclaring this prefix to point to a 1.520 + // different namespace) or 1.521 + // 2) We're looking at an existing default namespace decl on aElement (so 1.522 + // we can't create a new default namespace decl for this URI) 1.523 + // then generate a new prefix. Note that we do NOT generate new prefixes 1.524 + // if we happen to have aPrefix == decl->mPrefix == "" and mismatching 1.525 + // URIs when |decl| doesn't have aElement as its owner. In that case we 1.526 + // can simply push the new namespace URI as the default namespace for 1.527 + // aElement. 1.528 + if (!aPrefix.IsEmpty() || decl.mOwner == aElement) { 1.529 + NS_ASSERTION(!aURI.IsEmpty(), 1.530 + "Not allowed to add a xmlns attribute with an empty " 1.531 + "namespace name unless it declares the default " 1.532 + "namespace."); 1.533 + 1.534 + GenerateNewPrefix(aPrefix); 1.535 + // Now we need to validate our new prefix/uri combination; check it 1.536 + // against the full namespace stack again. Note that just restarting 1.537 + // the while loop is ok, since we haven't changed aURI, so the 1.538 + // closestURIMatch and uriMatch state is not affected. 1.539 + index = count - 1; 1.540 + haveSeenOurPrefix = false; 1.541 + continue; 1.542 + } 1.543 + } 1.544 + 1.545 + // If we've found a URI match, then record the first one 1.546 + if (!uriMatch && aURI.Equals(decl.mURI)) { 1.547 + // Need to check that decl->mPrefix is not declared anywhere closer to 1.548 + // us. If it is, we can't use it. 1.549 + bool prefixOK = true; 1.550 + int32_t index2; 1.551 + for (index2 = count-1; index2 > index && prefixOK; --index2) { 1.552 + prefixOK = (mNameSpaceStack[index2].mPrefix != decl.mPrefix); 1.553 + } 1.554 + 1.555 + if (prefixOK) { 1.556 + uriMatch = true; 1.557 + closestURIMatch.Assign(decl.mPrefix); 1.558 + } 1.559 + } 1.560 + 1.561 + --index; 1.562 + } 1.563 + 1.564 + // At this point the following invariants hold: 1.565 + // 1) The prefix in closestURIMatch is mapped to aURI in our scope if 1.566 + // uriMatch is set. 1.567 + // 2) There is nothing on the namespace stack that has aPrefix as the prefix 1.568 + // and a _different_ URI, except for the case aPrefix.IsEmpty (and 1.569 + // possible default namespaces on ancestors) 1.570 + 1.571 + // So if uriMatch is set it's OK to use the closestURIMatch prefix. The one 1.572 + // exception is when closestURIMatch is actually empty (default namespace 1.573 + // decl) and we must have a prefix. 1.574 + if (uriMatch && (!mustHavePrefix || !closestURIMatch.IsEmpty())) { 1.575 + aPrefix.Assign(closestURIMatch); 1.576 + return false; 1.577 + } 1.578 + 1.579 + if (aPrefix.IsEmpty()) { 1.580 + // At this point, aPrefix is empty (which means we never had a prefix to 1.581 + // start with). If we must have a prefix, just generate a new prefix and 1.582 + // then send it back through the namespace stack checks to make sure it's 1.583 + // OK. 1.584 + if (mustHavePrefix) { 1.585 + GenerateNewPrefix(aPrefix); 1.586 + return ConfirmPrefix(aPrefix, aURI, aElement, aIsAttribute); 1.587 + } 1.588 + 1.589 + // One final special case. If aPrefix is empty and we never saw an empty 1.590 + // prefix (default namespace decl) on the namespace stack and we're in the 1.591 + // null namespace there is no reason to output an |xmlns=""| here. It just 1.592 + // makes the output less readable. 1.593 + if (!haveSeenOurPrefix && aURI.IsEmpty()) { 1.594 + return false; 1.595 + } 1.596 + } 1.597 + 1.598 + // Now just set aURI as the new default namespace URI. Indicate that we need 1.599 + // to create a namespace decl for the final prefix 1.600 + return true; 1.601 +} 1.602 + 1.603 +void 1.604 +nsXMLContentSerializer::GenerateNewPrefix(nsAString& aPrefix) 1.605 +{ 1.606 + aPrefix.AssignLiteral("a"); 1.607 + char buf[128]; 1.608 + PR_snprintf(buf, sizeof(buf), "%d", mPrefixIndex++); 1.609 + AppendASCIItoUTF16(buf, aPrefix); 1.610 +} 1.611 + 1.612 +void 1.613 +nsXMLContentSerializer::SerializeAttr(const nsAString& aPrefix, 1.614 + const nsAString& aName, 1.615 + const nsAString& aValue, 1.616 + nsAString& aStr, 1.617 + bool aDoEscapeEntities) 1.618 +{ 1.619 + nsAutoString attrString_; 1.620 + // For innerHTML we can do faster appending without 1.621 + // temporary strings. 1.622 + bool rawAppend = mDoRaw && aDoEscapeEntities; 1.623 + nsAString& attrString = (rawAppend) ? aStr : attrString_; 1.624 + 1.625 + attrString.Append(char16_t(' ')); 1.626 + if (!aPrefix.IsEmpty()) { 1.627 + attrString.Append(aPrefix); 1.628 + attrString.Append(char16_t(':')); 1.629 + } 1.630 + attrString.Append(aName); 1.631 + 1.632 + if (aDoEscapeEntities) { 1.633 + // if problem characters are turned into character entity references 1.634 + // then there will be no problem with the value delimiter characters 1.635 + attrString.AppendLiteral("=\""); 1.636 + 1.637 + mInAttribute = true; 1.638 + AppendAndTranslateEntities(aValue, attrString); 1.639 + mInAttribute = false; 1.640 + 1.641 + attrString.Append(char16_t('"')); 1.642 + if (rawAppend) { 1.643 + return; 1.644 + } 1.645 + } 1.646 + else { 1.647 + // Depending on whether the attribute value contains quotes or apostrophes we 1.648 + // need to select the delimiter character and escape characters using 1.649 + // character entity references, ignoring the value of aDoEscapeEntities. 1.650 + // See http://www.w3.org/TR/REC-html40/appendix/notes.html#h-B.3.2.2 for 1.651 + // the standard on character entity references in values. We also have to 1.652 + // make sure to escape any '&' characters. 1.653 + 1.654 + bool bIncludesSingle = false; 1.655 + bool bIncludesDouble = false; 1.656 + nsAString::const_iterator iCurr, iEnd; 1.657 + uint32_t uiSize, i; 1.658 + aValue.BeginReading(iCurr); 1.659 + aValue.EndReading(iEnd); 1.660 + for ( ; iCurr != iEnd; iCurr.advance(uiSize) ) { 1.661 + const char16_t * buf = iCurr.get(); 1.662 + uiSize = iCurr.size_forward(); 1.663 + for ( i = 0; i < uiSize; i++, buf++ ) { 1.664 + if ( *buf == char16_t('\'') ) 1.665 + { 1.666 + bIncludesSingle = true; 1.667 + if ( bIncludesDouble ) break; 1.668 + } 1.669 + else if ( *buf == char16_t('"') ) 1.670 + { 1.671 + bIncludesDouble = true; 1.672 + if ( bIncludesSingle ) break; 1.673 + } 1.674 + } 1.675 + // if both have been found we don't need to search further 1.676 + if ( bIncludesDouble && bIncludesSingle ) break; 1.677 + } 1.678 + 1.679 + // Delimiter and escaping is according to the following table 1.680 + // bIncludesDouble bIncludesSingle Delimiter Escape Double Quote 1.681 + // FALSE FALSE " FALSE 1.682 + // FALSE TRUE " FALSE 1.683 + // TRUE FALSE ' FALSE 1.684 + // TRUE TRUE " TRUE 1.685 + char16_t cDelimiter = 1.686 + (bIncludesDouble && !bIncludesSingle) ? char16_t('\'') : char16_t('"'); 1.687 + attrString.Append(char16_t('=')); 1.688 + attrString.Append(cDelimiter); 1.689 + nsAutoString sValue(aValue); 1.690 + sValue.ReplaceSubstring(NS_LITERAL_STRING("&"), 1.691 + NS_LITERAL_STRING("&")); 1.692 + if (bIncludesDouble && bIncludesSingle) { 1.693 + sValue.ReplaceSubstring(NS_LITERAL_STRING("\""), 1.694 + NS_LITERAL_STRING(""")); 1.695 + } 1.696 + attrString.Append(sValue); 1.697 + attrString.Append(cDelimiter); 1.698 + } 1.699 + if (mPreLevel > 0 || mDoRaw) { 1.700 + AppendToStringConvertLF(attrString, aStr); 1.701 + } 1.702 + else if (mDoFormat) { 1.703 + AppendToStringFormatedWrapped(attrString, aStr); 1.704 + } 1.705 + else if (mDoWrap) { 1.706 + AppendToStringWrapped(attrString, aStr); 1.707 + } 1.708 + else { 1.709 + AppendToStringConvertLF(attrString, aStr); 1.710 + } 1.711 +} 1.712 + 1.713 +uint32_t 1.714 +nsXMLContentSerializer::ScanNamespaceDeclarations(nsIContent* aContent, 1.715 + nsIContent *aOriginalElement, 1.716 + const nsAString& aTagNamespaceURI) 1.717 +{ 1.718 + uint32_t index, count; 1.719 + nsAutoString uriStr, valueStr; 1.720 + 1.721 + count = aContent->GetAttrCount(); 1.722 + 1.723 + // First scan for namespace declarations, pushing each on the stack 1.724 + uint32_t skipAttr = count; 1.725 + for (index = 0; index < count; index++) { 1.726 + 1.727 + const nsAttrName* name = aContent->GetAttrNameAt(index); 1.728 + int32_t namespaceID = name->NamespaceID(); 1.729 + nsIAtom *attrName = name->LocalName(); 1.730 + 1.731 + if (namespaceID == kNameSpaceID_XMLNS || 1.732 + // Also push on the stack attrs named "xmlns" in the null 1.733 + // namespace... because once we serialize those out they'll look like 1.734 + // namespace decls. :( 1.735 + // XXXbz what if we have both "xmlns" in the null namespace and "xmlns" 1.736 + // in the xmlns namespace? 1.737 + (namespaceID == kNameSpaceID_None && 1.738 + attrName == nsGkAtoms::xmlns)) { 1.739 + aContent->GetAttr(namespaceID, attrName, uriStr); 1.740 + 1.741 + if (!name->GetPrefix()) { 1.742 + if (aTagNamespaceURI.IsEmpty() && !uriStr.IsEmpty()) { 1.743 + // If the element is in no namespace we need to add a xmlns 1.744 + // attribute to declare that. That xmlns attribute must not have a 1.745 + // prefix (see http://www.w3.org/TR/REC-xml-names/#dt-prefix), ie it 1.746 + // must declare the default namespace. We just found an xmlns 1.747 + // attribute that declares the default namespace to something 1.748 + // non-empty. We're going to ignore this attribute, for children we 1.749 + // will detect that we need to add it again and attributes aren't 1.750 + // affected by the default namespace. 1.751 + skipAttr = index; 1.752 + } 1.753 + else { 1.754 + // Default NS attribute does not have prefix (and the name is "xmlns") 1.755 + PushNameSpaceDecl(EmptyString(), uriStr, aOriginalElement); 1.756 + } 1.757 + } 1.758 + else { 1.759 + PushNameSpaceDecl(nsDependentAtomString(attrName), uriStr, 1.760 + aOriginalElement); 1.761 + } 1.762 + } 1.763 + } 1.764 + return skipAttr; 1.765 +} 1.766 + 1.767 + 1.768 +bool 1.769 +nsXMLContentSerializer::IsJavaScript(nsIContent * aContent, nsIAtom* aAttrNameAtom, 1.770 + int32_t aAttrNamespaceID, const nsAString& aValueString) 1.771 +{ 1.772 + bool isHtml = aContent->IsHTML(); 1.773 + bool isXul = aContent->IsXUL(); 1.774 + bool isSvg = aContent->IsSVG(); 1.775 + 1.776 + if (aAttrNamespaceID == kNameSpaceID_None && 1.777 + (isHtml || isXul || isSvg) && 1.778 + (aAttrNameAtom == nsGkAtoms::href || 1.779 + aAttrNameAtom == nsGkAtoms::src)) { 1.780 + 1.781 + static const char kJavaScript[] = "javascript"; 1.782 + int32_t pos = aValueString.FindChar(':'); 1.783 + if (pos < (int32_t)(sizeof kJavaScript - 1)) 1.784 + return false; 1.785 + nsAutoString scheme(Substring(aValueString, 0, pos)); 1.786 + scheme.StripWhitespace(); 1.787 + if ((scheme.Length() == (sizeof kJavaScript - 1)) && 1.788 + scheme.EqualsIgnoreCase(kJavaScript)) 1.789 + return true; 1.790 + else 1.791 + return false; 1.792 + } 1.793 + 1.794 + return aContent->IsEventAttributeName(aAttrNameAtom); 1.795 +} 1.796 + 1.797 + 1.798 +void 1.799 +nsXMLContentSerializer::SerializeAttributes(nsIContent* aContent, 1.800 + nsIContent *aOriginalElement, 1.801 + nsAString& aTagPrefix, 1.802 + const nsAString& aTagNamespaceURI, 1.803 + nsIAtom* aTagName, 1.804 + nsAString& aStr, 1.805 + uint32_t aSkipAttr, 1.806 + bool aAddNSAttr) 1.807 +{ 1.808 + 1.809 + nsAutoString prefixStr, uriStr, valueStr; 1.810 + nsAutoString xmlnsStr; 1.811 + xmlnsStr.AssignLiteral(kXMLNS); 1.812 + uint32_t index, count; 1.813 + 1.814 + // If we had to add a new namespace declaration, serialize 1.815 + // and push it on the namespace stack 1.816 + if (aAddNSAttr) { 1.817 + if (aTagPrefix.IsEmpty()) { 1.818 + // Serialize default namespace decl 1.819 + SerializeAttr(EmptyString(), xmlnsStr, aTagNamespaceURI, aStr, true); 1.820 + } 1.821 + else { 1.822 + // Serialize namespace decl 1.823 + SerializeAttr(xmlnsStr, aTagPrefix, aTagNamespaceURI, aStr, true); 1.824 + } 1.825 + PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement); 1.826 + } 1.827 + 1.828 + count = aContent->GetAttrCount(); 1.829 + 1.830 + // Now serialize each of the attributes 1.831 + // XXX Unfortunately we need a namespace manager to get 1.832 + // attribute URIs. 1.833 + for (index = 0; index < count; index++) { 1.834 + if (aSkipAttr == index) { 1.835 + continue; 1.836 + } 1.837 + 1.838 + const nsAttrName* name = aContent->GetAttrNameAt(index); 1.839 + int32_t namespaceID = name->NamespaceID(); 1.840 + nsIAtom* attrName = name->LocalName(); 1.841 + nsIAtom* attrPrefix = name->GetPrefix(); 1.842 + 1.843 + // Filter out any attribute starting with [-|_]moz 1.844 + nsDependentAtomString attrNameStr(attrName); 1.845 + if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) || 1.846 + StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) { 1.847 + continue; 1.848 + } 1.849 + 1.850 + if (attrPrefix) { 1.851 + attrPrefix->ToString(prefixStr); 1.852 + } 1.853 + else { 1.854 + prefixStr.Truncate(); 1.855 + } 1.856 + 1.857 + bool addNSAttr = false; 1.858 + if (kNameSpaceID_XMLNS != namespaceID) { 1.859 + nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr); 1.860 + addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true); 1.861 + } 1.862 + 1.863 + aContent->GetAttr(namespaceID, attrName, valueStr); 1.864 + 1.865 + nsDependentAtomString nameStr(attrName); 1.866 + bool isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr); 1.867 + 1.868 + SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS); 1.869 + 1.870 + if (addNSAttr) { 1.871 + NS_ASSERTION(!prefixStr.IsEmpty(), 1.872 + "Namespaced attributes must have a prefix"); 1.873 + SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, true); 1.874 + PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement); 1.875 + } 1.876 + } 1.877 +} 1.878 + 1.879 +NS_IMETHODIMP 1.880 +nsXMLContentSerializer::AppendElementStart(Element* aElement, 1.881 + Element* aOriginalElement, 1.882 + nsAString& aStr) 1.883 +{ 1.884 + NS_ENSURE_ARG(aElement); 1.885 + 1.886 + nsIContent* content = aElement; 1.887 + 1.888 + bool forceFormat = false; 1.889 + if (!CheckElementStart(content, forceFormat, aStr)) { 1.890 + return NS_OK; 1.891 + } 1.892 + 1.893 + nsAutoString tagPrefix, tagLocalName, tagNamespaceURI; 1.894 + aElement->NodeInfo()->GetPrefix(tagPrefix); 1.895 + aElement->NodeInfo()->GetName(tagLocalName); 1.896 + aElement->NodeInfo()->GetNamespaceURI(tagNamespaceURI); 1.897 + 1.898 + uint32_t skipAttr = ScanNamespaceDeclarations(content, 1.899 + aOriginalElement, tagNamespaceURI); 1.900 + 1.901 + nsIAtom *name = content->Tag(); 1.902 + bool lineBreakBeforeOpen = LineBreakBeforeOpen(content->GetNameSpaceID(), name); 1.903 + 1.904 + if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) { 1.905 + if (mColPos && lineBreakBeforeOpen) { 1.906 + AppendNewLineToString(aStr); 1.907 + } 1.908 + else { 1.909 + MaybeAddNewlineForRootNode(aStr); 1.910 + } 1.911 + if (!mColPos) { 1.912 + AppendIndentation(aStr); 1.913 + } 1.914 + else if (mAddSpace) { 1.915 + AppendToString(char16_t(' '), aStr); 1.916 + mAddSpace = false; 1.917 + } 1.918 + } 1.919 + else if (mAddSpace) { 1.920 + AppendToString(char16_t(' '), aStr); 1.921 + mAddSpace = false; 1.922 + } 1.923 + else { 1.924 + MaybeAddNewlineForRootNode(aStr); 1.925 + } 1.926 + 1.927 + // Always reset to avoid false newlines in case MaybeAddNewlineForRootNode wasn't 1.928 + // called 1.929 + mAddNewlineForRootNode = false; 1.930 + 1.931 + bool addNSAttr; 1.932 + addNSAttr = ConfirmPrefix(tagPrefix, tagNamespaceURI, aOriginalElement, 1.933 + false); 1.934 + 1.935 + // Serialize the qualified name of the element 1.936 + AppendToString(kLessThan, aStr); 1.937 + if (!tagPrefix.IsEmpty()) { 1.938 + AppendToString(tagPrefix, aStr); 1.939 + AppendToString(NS_LITERAL_STRING(":"), aStr); 1.940 + } 1.941 + AppendToString(tagLocalName, aStr); 1.942 + 1.943 + MaybeEnterInPreContent(content); 1.944 + 1.945 + if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) { 1.946 + IncrIndentation(name); 1.947 + } 1.948 + 1.949 + SerializeAttributes(content, aOriginalElement, tagPrefix, tagNamespaceURI, 1.950 + name, aStr, skipAttr, addNSAttr); 1.951 + 1.952 + AppendEndOfElementStart(aOriginalElement, name, content->GetNameSpaceID(), 1.953 + aStr); 1.954 + 1.955 + if ((mDoFormat || forceFormat) && !mPreLevel 1.956 + && !mDoRaw && LineBreakAfterOpen(content->GetNameSpaceID(), name)) { 1.957 + AppendNewLineToString(aStr); 1.958 + } 1.959 + 1.960 + AfterElementStart(content, aOriginalElement, aStr); 1.961 + 1.962 + return NS_OK; 1.963 +} 1.964 + 1.965 +void 1.966 +nsXMLContentSerializer::AppendEndOfElementStart(nsIContent *aOriginalElement, 1.967 + nsIAtom * aName, 1.968 + int32_t aNamespaceID, 1.969 + nsAString& aStr) 1.970 +{ 1.971 + // We don't output a separate end tag for empty elements 1.972 + if (!aOriginalElement->GetChildCount()) { 1.973 + AppendToString(NS_LITERAL_STRING("/>"), aStr); 1.974 + } 1.975 + else { 1.976 + AppendToString(kGreaterThan, aStr); 1.977 + } 1.978 +} 1.979 + 1.980 +NS_IMETHODIMP 1.981 +nsXMLContentSerializer::AppendElementEnd(Element* aElement, 1.982 + nsAString& aStr) 1.983 +{ 1.984 + NS_ENSURE_ARG(aElement); 1.985 + 1.986 + nsIContent* content = aElement; 1.987 + 1.988 + bool forceFormat = false, outputElementEnd; 1.989 + outputElementEnd = CheckElementEnd(content, forceFormat, aStr); 1.990 + 1.991 + nsIAtom *name = content->Tag(); 1.992 + 1.993 + if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) { 1.994 + DecrIndentation(name); 1.995 + } 1.996 + 1.997 + if (!outputElementEnd) { 1.998 + PopNameSpaceDeclsFor(aElement); 1.999 + MaybeFlagNewlineForRootNode(aElement); 1.1000 + return NS_OK; 1.1001 + } 1.1002 + 1.1003 + nsAutoString tagPrefix, tagLocalName, tagNamespaceURI; 1.1004 + 1.1005 + aElement->NodeInfo()->GetPrefix(tagPrefix); 1.1006 + aElement->NodeInfo()->GetName(tagLocalName); 1.1007 + aElement->NodeInfo()->GetNamespaceURI(tagNamespaceURI); 1.1008 + 1.1009 +#ifdef DEBUG 1.1010 + bool debugNeedToPushNamespace = 1.1011 +#endif 1.1012 + ConfirmPrefix(tagPrefix, tagNamespaceURI, aElement, false); 1.1013 + NS_ASSERTION(!debugNeedToPushNamespace, "Can't push namespaces in closing tag!"); 1.1014 + 1.1015 + if ((mDoFormat || forceFormat) && !mPreLevel && !mDoRaw) { 1.1016 + 1.1017 + bool lineBreakBeforeClose = LineBreakBeforeClose(content->GetNameSpaceID(), name); 1.1018 + 1.1019 + if (mColPos && lineBreakBeforeClose) { 1.1020 + AppendNewLineToString(aStr); 1.1021 + } 1.1022 + if (!mColPos) { 1.1023 + AppendIndentation(aStr); 1.1024 + } 1.1025 + else if (mAddSpace) { 1.1026 + AppendToString(char16_t(' '), aStr); 1.1027 + mAddSpace = false; 1.1028 + } 1.1029 + } 1.1030 + else if (mAddSpace) { 1.1031 + AppendToString(char16_t(' '), aStr); 1.1032 + mAddSpace = false; 1.1033 + } 1.1034 + 1.1035 + AppendToString(kEndTag, aStr); 1.1036 + if (!tagPrefix.IsEmpty()) { 1.1037 + AppendToString(tagPrefix, aStr); 1.1038 + AppendToString(NS_LITERAL_STRING(":"), aStr); 1.1039 + } 1.1040 + AppendToString(tagLocalName, aStr); 1.1041 + AppendToString(kGreaterThan, aStr); 1.1042 + 1.1043 + PopNameSpaceDeclsFor(aElement); 1.1044 + 1.1045 + MaybeLeaveFromPreContent(content); 1.1046 + 1.1047 + if ((mDoFormat || forceFormat) && !mPreLevel 1.1048 + && !mDoRaw && LineBreakAfterClose(content->GetNameSpaceID(), name)) { 1.1049 + AppendNewLineToString(aStr); 1.1050 + } 1.1051 + else { 1.1052 + MaybeFlagNewlineForRootNode(aElement); 1.1053 + } 1.1054 + 1.1055 + AfterElementEnd(content, aStr); 1.1056 + 1.1057 + return NS_OK; 1.1058 +} 1.1059 + 1.1060 +NS_IMETHODIMP 1.1061 +nsXMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument, 1.1062 + nsAString& aStr) 1.1063 +{ 1.1064 + NS_ENSURE_ARG_POINTER(aDocument); 1.1065 + 1.1066 + nsAutoString version, encoding, standalone; 1.1067 + aDocument->GetXMLDeclaration(version, encoding, standalone); 1.1068 + 1.1069 + if (version.IsEmpty()) 1.1070 + return NS_OK; // A declaration must have version, or there is no decl 1.1071 + 1.1072 + NS_NAMED_LITERAL_STRING(endQuote, "\""); 1.1073 + 1.1074 + aStr += NS_LITERAL_STRING("<?xml version=\"") + version + endQuote; 1.1075 + 1.1076 + if (!mCharset.IsEmpty()) { 1.1077 + aStr += NS_LITERAL_STRING(" encoding=\"") + 1.1078 + NS_ConvertASCIItoUTF16(mCharset) + endQuote; 1.1079 + } 1.1080 + // Otherwise just don't output an encoding attr. Not that we expect 1.1081 + // mCharset to ever be empty. 1.1082 +#ifdef DEBUG 1.1083 + else { 1.1084 + NS_WARNING("Empty mCharset? How come?"); 1.1085 + } 1.1086 +#endif 1.1087 + 1.1088 + if (!standalone.IsEmpty()) { 1.1089 + aStr += NS_LITERAL_STRING(" standalone=\"") + standalone + endQuote; 1.1090 + } 1.1091 + 1.1092 + aStr.AppendLiteral("?>"); 1.1093 + mAddNewlineForRootNode = true; 1.1094 + 1.1095 + return NS_OK; 1.1096 +} 1.1097 + 1.1098 +bool 1.1099 +nsXMLContentSerializer::CheckElementStart(nsIContent * aContent, 1.1100 + bool & aForceFormat, 1.1101 + nsAString& aStr) 1.1102 +{ 1.1103 + aForceFormat = false; 1.1104 + return true; 1.1105 +} 1.1106 + 1.1107 +bool 1.1108 +nsXMLContentSerializer::CheckElementEnd(nsIContent * aContent, 1.1109 + bool & aForceFormat, 1.1110 + nsAString& aStr) 1.1111 +{ 1.1112 + // We don't output a separate end tag for empty element 1.1113 + aForceFormat = false; 1.1114 + return aContent->GetChildCount() > 0; 1.1115 +} 1.1116 + 1.1117 +void 1.1118 +nsXMLContentSerializer::AppendToString(const char16_t aChar, 1.1119 + nsAString& aOutputStr) 1.1120 +{ 1.1121 + if (mBodyOnly && !mInBody) { 1.1122 + return; 1.1123 + } 1.1124 + mColPos += 1; 1.1125 + aOutputStr.Append(aChar); 1.1126 +} 1.1127 + 1.1128 +void 1.1129 +nsXMLContentSerializer::AppendToString(const nsAString& aStr, 1.1130 + nsAString& aOutputStr) 1.1131 +{ 1.1132 + if (mBodyOnly && !mInBody) { 1.1133 + return; 1.1134 + } 1.1135 + mColPos += aStr.Length(); 1.1136 + aOutputStr.Append(aStr); 1.1137 +} 1.1138 + 1.1139 + 1.1140 +static const uint16_t kGTVal = 62; 1.1141 +static const char* kEntities[] = { 1.1142 + "", "", "", "", "", "", "", "", "", "", 1.1143 + "", "", "", "", "", "", "", "", "", "", 1.1144 + "", "", "", "", "", "", "", "", "", "", 1.1145 + "", "", "", "", "", "", "", "", "&", "", 1.1146 + "", "", "", "", "", "", "", "", "", "", 1.1147 + "", "", "", "", "", "", "", "", "", "", 1.1148 + "<", "", ">" 1.1149 +}; 1.1150 + 1.1151 +static const char* kAttrEntities[] = { 1.1152 + "", "", "", "", "", "", "", "", "", "", 1.1153 + "", "", "", "", "", "", "", "", "", "", 1.1154 + "", "", "", "", "", "", "", "", "", "", 1.1155 + "", "", "", "", """, "", "", "", "&", "", 1.1156 + "", "", "", "", "", "", "", "", "", "", 1.1157 + "", "", "", "", "", "", "", "", "", "", 1.1158 + "<", "", ">" 1.1159 +}; 1.1160 + 1.1161 +void 1.1162 +nsXMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr, 1.1163 + nsAString& aOutputStr) 1.1164 +{ 1.1165 + nsReadingIterator<char16_t> done_reading; 1.1166 + aStr.EndReading(done_reading); 1.1167 + 1.1168 + // for each chunk of |aString|... 1.1169 + uint32_t advanceLength = 0; 1.1170 + nsReadingIterator<char16_t> iter; 1.1171 + 1.1172 + const char **entityTable = mInAttribute ? kAttrEntities : kEntities; 1.1173 + 1.1174 + for (aStr.BeginReading(iter); 1.1175 + iter != done_reading; 1.1176 + iter.advance(int32_t(advanceLength))) { 1.1177 + uint32_t fragmentLength = iter.size_forward(); 1.1178 + const char16_t* c = iter.get(); 1.1179 + const char16_t* fragmentStart = c; 1.1180 + const char16_t* fragmentEnd = c + fragmentLength; 1.1181 + const char* entityText = nullptr; 1.1182 + 1.1183 + advanceLength = 0; 1.1184 + // for each character in this chunk, check if it 1.1185 + // needs to be replaced 1.1186 + for (; c < fragmentEnd; c++, advanceLength++) { 1.1187 + char16_t val = *c; 1.1188 + if ((val <= kGTVal) && (entityTable[val][0] != 0)) { 1.1189 + entityText = entityTable[val]; 1.1190 + break; 1.1191 + } 1.1192 + } 1.1193 + 1.1194 + aOutputStr.Append(fragmentStart, advanceLength); 1.1195 + if (entityText) { 1.1196 + AppendASCIItoUTF16(entityText, aOutputStr); 1.1197 + advanceLength++; 1.1198 + } 1.1199 + } 1.1200 +} 1.1201 + 1.1202 +void 1.1203 +nsXMLContentSerializer::MaybeAddNewlineForRootNode(nsAString& aStr) 1.1204 +{ 1.1205 + if (mAddNewlineForRootNode) { 1.1206 + AppendNewLineToString(aStr); 1.1207 + } 1.1208 +} 1.1209 + 1.1210 +void 1.1211 +nsXMLContentSerializer::MaybeFlagNewlineForRootNode(nsINode* aNode) 1.1212 +{ 1.1213 + nsINode* parent = aNode->GetParentNode(); 1.1214 + if (parent) { 1.1215 + mAddNewlineForRootNode = parent->IsNodeOfType(nsINode::eDOCUMENT); 1.1216 + } 1.1217 +} 1.1218 + 1.1219 +void 1.1220 +nsXMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode) 1.1221 +{ 1.1222 + // support of the xml:space attribute 1.1223 + if (aNode->HasAttr(kNameSpaceID_XML, nsGkAtoms::space)) { 1.1224 + nsAutoString space; 1.1225 + aNode->GetAttr(kNameSpaceID_XML, nsGkAtoms::space, space); 1.1226 + if (space.EqualsLiteral("preserve")) 1.1227 + ++mPreLevel; 1.1228 + } 1.1229 +} 1.1230 + 1.1231 +void 1.1232 +nsXMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode) 1.1233 +{ 1.1234 + // support of the xml:space attribute 1.1235 + if (aNode->HasAttr(kNameSpaceID_XML, nsGkAtoms::space)) { 1.1236 + nsAutoString space; 1.1237 + aNode->GetAttr(kNameSpaceID_XML, nsGkAtoms::space, space); 1.1238 + if (space.EqualsLiteral("preserve")) 1.1239 + --mPreLevel; 1.1240 + } 1.1241 +} 1.1242 + 1.1243 +void 1.1244 +nsXMLContentSerializer::AppendNewLineToString(nsAString& aStr) 1.1245 +{ 1.1246 + AppendToString(mLineBreak, aStr); 1.1247 + mMayIgnoreLineBreakSequence = true; 1.1248 + mColPos = 0; 1.1249 + mAddSpace = false; 1.1250 + mIsIndentationAddedOnCurrentLine = false; 1.1251 +} 1.1252 + 1.1253 +void 1.1254 +nsXMLContentSerializer::AppendIndentation(nsAString& aStr) 1.1255 +{ 1.1256 + mIsIndentationAddedOnCurrentLine = true; 1.1257 + AppendToString(mIndent, aStr); 1.1258 + mAddSpace = false; 1.1259 + mMayIgnoreLineBreakSequence = false; 1.1260 +} 1.1261 + 1.1262 +void 1.1263 +nsXMLContentSerializer::IncrIndentation(nsIAtom* aName) 1.1264 +{ 1.1265 + // we want to keep the source readable 1.1266 + if (mDoWrap && 1.1267 + mIndent.Length() >= uint32_t(mMaxColumn) - MIN_INDENTED_LINE_LENGTH) { 1.1268 + ++mIndentOverflow; 1.1269 + } 1.1270 + else { 1.1271 + mIndent.AppendLiteral(INDENT_STRING); 1.1272 + } 1.1273 +} 1.1274 + 1.1275 +void 1.1276 +nsXMLContentSerializer::DecrIndentation(nsIAtom* aName) 1.1277 +{ 1.1278 + if(mIndentOverflow) 1.1279 + --mIndentOverflow; 1.1280 + else 1.1281 + mIndent.Cut(0, INDENT_STRING_LENGTH); 1.1282 +} 1.1283 + 1.1284 +bool 1.1285 +nsXMLContentSerializer::LineBreakBeforeOpen(int32_t aNamespaceID, nsIAtom* aName) 1.1286 +{ 1.1287 + return mAddSpace; 1.1288 +} 1.1289 + 1.1290 +bool 1.1291 +nsXMLContentSerializer::LineBreakAfterOpen(int32_t aNamespaceID, nsIAtom* aName) 1.1292 +{ 1.1293 + return false; 1.1294 +} 1.1295 + 1.1296 +bool 1.1297 +nsXMLContentSerializer::LineBreakBeforeClose(int32_t aNamespaceID, nsIAtom* aName) 1.1298 +{ 1.1299 + return mAddSpace; 1.1300 +} 1.1301 + 1.1302 +bool 1.1303 +nsXMLContentSerializer::LineBreakAfterClose(int32_t aNamespaceID, nsIAtom* aName) 1.1304 +{ 1.1305 + return false; 1.1306 +} 1.1307 + 1.1308 +void 1.1309 +nsXMLContentSerializer::AppendToStringConvertLF(const nsAString& aStr, 1.1310 + nsAString& aOutputStr) 1.1311 +{ 1.1312 + if (mBodyOnly && !mInBody) { 1.1313 + return; 1.1314 + } 1.1315 + 1.1316 + if (mDoRaw) { 1.1317 + AppendToString(aStr, aOutputStr); 1.1318 + } 1.1319 + else { 1.1320 + // Convert line-endings to mLineBreak 1.1321 + uint32_t start = 0; 1.1322 + uint32_t theLen = aStr.Length(); 1.1323 + while (start < theLen) { 1.1324 + int32_t eol = aStr.FindChar('\n', start); 1.1325 + if (eol == kNotFound) { 1.1326 + nsDependentSubstring dataSubstring(aStr, start, theLen - start); 1.1327 + AppendToString(dataSubstring, aOutputStr); 1.1328 + start = theLen; 1.1329 + // if there was a line break before this substring 1.1330 + // AppendNewLineToString was called, so we should reverse 1.1331 + // this flag 1.1332 + mMayIgnoreLineBreakSequence = false; 1.1333 + } 1.1334 + else { 1.1335 + nsDependentSubstring dataSubstring(aStr, start, eol - start); 1.1336 + AppendToString(dataSubstring, aOutputStr); 1.1337 + AppendNewLineToString(aOutputStr); 1.1338 + start = eol + 1; 1.1339 + } 1.1340 + } 1.1341 + } 1.1342 +} 1.1343 + 1.1344 +void 1.1345 +nsXMLContentSerializer::AppendFormatedWrapped_WhitespaceSequence( 1.1346 + nsASingleFragmentString::const_char_iterator &aPos, 1.1347 + const nsASingleFragmentString::const_char_iterator aEnd, 1.1348 + const nsASingleFragmentString::const_char_iterator aSequenceStart, 1.1349 + bool &aMayIgnoreStartOfLineWhitespaceSequence, 1.1350 + nsAString &aOutputStr) 1.1351 +{ 1.1352 + // Handle the complete sequence of whitespace. 1.1353 + // Continue to iterate until we find the first non-whitespace char. 1.1354 + // Updates "aPos" to point to the first unhandled char. 1.1355 + // Also updates the aMayIgnoreStartOfLineWhitespaceSequence flag, 1.1356 + // as well as the other "global" state flags. 1.1357 + 1.1358 + bool sawBlankOrTab = false; 1.1359 + bool leaveLoop = false; 1.1360 + 1.1361 + do { 1.1362 + switch (*aPos) { 1.1363 + case ' ': 1.1364 + case '\t': 1.1365 + sawBlankOrTab = true; 1.1366 + // no break 1.1367 + case '\n': 1.1368 + ++aPos; 1.1369 + // do not increase mColPos, 1.1370 + // because we will reduce the whitespace to a single char 1.1371 + break; 1.1372 + default: 1.1373 + leaveLoop = true; 1.1374 + break; 1.1375 + } 1.1376 + } while (!leaveLoop && aPos < aEnd); 1.1377 + 1.1378 + if (mAddSpace) { 1.1379 + // if we had previously been asked to add space, 1.1380 + // our situation has not changed 1.1381 + } 1.1382 + else if (!sawBlankOrTab && mMayIgnoreLineBreakSequence) { 1.1383 + // nothing to do in the case where line breaks have already been added 1.1384 + // before the call of AppendToStringWrapped 1.1385 + // and only if we found line break in the sequence 1.1386 + mMayIgnoreLineBreakSequence = false; 1.1387 + } 1.1388 + else if (aMayIgnoreStartOfLineWhitespaceSequence) { 1.1389 + // nothing to do 1.1390 + aMayIgnoreStartOfLineWhitespaceSequence = false; 1.1391 + } 1.1392 + else { 1.1393 + if (sawBlankOrTab) { 1.1394 + if (mDoWrap && mColPos + 1 >= mMaxColumn) { 1.1395 + // no much sense in delaying, we only have one slot left, 1.1396 + // let's write a break now 1.1397 + aOutputStr.Append(mLineBreak); 1.1398 + mColPos = 0; 1.1399 + mIsIndentationAddedOnCurrentLine = false; 1.1400 + mMayIgnoreLineBreakSequence = true; 1.1401 + } 1.1402 + else { 1.1403 + // do not write out yet, we may write out either a space or a linebreak 1.1404 + // let's delay writing it out until we know more 1.1405 + mAddSpace = true; 1.1406 + ++mColPos; // eat a slot of available space 1.1407 + } 1.1408 + } 1.1409 + else { 1.1410 + // Asian text usually does not contain spaces, therefore we should not 1.1411 + // transform a linebreak into a space. 1.1412 + // Since we only saw linebreaks, but no spaces or tabs, 1.1413 + // let's write a linebreak now. 1.1414 + AppendNewLineToString(aOutputStr); 1.1415 + } 1.1416 + } 1.1417 +} 1.1418 + 1.1419 +void 1.1420 +nsXMLContentSerializer::AppendWrapped_NonWhitespaceSequence( 1.1421 + nsASingleFragmentString::const_char_iterator &aPos, 1.1422 + const nsASingleFragmentString::const_char_iterator aEnd, 1.1423 + const nsASingleFragmentString::const_char_iterator aSequenceStart, 1.1424 + bool &aMayIgnoreStartOfLineWhitespaceSequence, 1.1425 + bool &aSequenceStartAfterAWhiteSpace, 1.1426 + nsAString& aOutputStr) 1.1427 +{ 1.1428 + mMayIgnoreLineBreakSequence = false; 1.1429 + aMayIgnoreStartOfLineWhitespaceSequence = false; 1.1430 + 1.1431 + // Handle the complete sequence of non-whitespace in this block 1.1432 + // Iterate until we find the first whitespace char or an aEnd condition 1.1433 + // Updates "aPos" to point to the first unhandled char. 1.1434 + // Also updates the aMayIgnoreStartOfLineWhitespaceSequence flag, 1.1435 + // as well as the other "global" state flags. 1.1436 + 1.1437 + bool thisSequenceStartsAtBeginningOfLine = !mColPos; 1.1438 + bool onceAgainBecauseWeAddedBreakInFront = false; 1.1439 + bool foundWhitespaceInLoop; 1.1440 + uint32_t length, colPos; 1.1441 + 1.1442 + do { 1.1443 + 1.1444 + if (mColPos) { 1.1445 + colPos = mColPos; 1.1446 + } 1.1447 + else { 1.1448 + if (mDoFormat && !mPreLevel && !onceAgainBecauseWeAddedBreakInFront) { 1.1449 + colPos = mIndent.Length(); 1.1450 + } 1.1451 + else 1.1452 + colPos = 0; 1.1453 + } 1.1454 + foundWhitespaceInLoop = false; 1.1455 + length = 0; 1.1456 + // we iterate until the next whitespace character 1.1457 + // or until we reach the maximum of character per line 1.1458 + // or until the end of the string to add. 1.1459 + do { 1.1460 + if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') { 1.1461 + foundWhitespaceInLoop = true; 1.1462 + break; 1.1463 + } 1.1464 + 1.1465 + ++aPos; 1.1466 + ++length; 1.1467 + } while ( (!mDoWrap || colPos + length < mMaxColumn) && aPos < aEnd); 1.1468 + 1.1469 + // in the case we don't reached the end of the string, but we reached the maxcolumn, 1.1470 + // we see if there is a whitespace after the maxcolumn 1.1471 + // if yes, then we can append directly the string instead of 1.1472 + // appending a new line etc. 1.1473 + if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') { 1.1474 + foundWhitespaceInLoop = true; 1.1475 + } 1.1476 + 1.1477 + if (aPos == aEnd || foundWhitespaceInLoop) { 1.1478 + // there is enough room for the complete block we found 1.1479 + if (mDoFormat && !mColPos) { 1.1480 + AppendIndentation(aOutputStr); 1.1481 + } 1.1482 + else if (mAddSpace) { 1.1483 + aOutputStr.Append(char16_t(' ')); 1.1484 + mAddSpace = false; 1.1485 + } 1.1486 + 1.1487 + mColPos += length; 1.1488 + aOutputStr.Append(aSequenceStart, aPos - aSequenceStart); 1.1489 + 1.1490 + // We have not yet reached the max column, we will continue to 1.1491 + // fill the current line in the next outer loop iteration 1.1492 + // (this one in AppendToStringWrapped) 1.1493 + // make sure we return in this outer loop 1.1494 + onceAgainBecauseWeAddedBreakInFront = false; 1.1495 + } 1.1496 + else { // we reach the max column 1.1497 + if (!thisSequenceStartsAtBeginningOfLine && 1.1498 + (mAddSpace || (!mDoFormat && aSequenceStartAfterAWhiteSpace))) { 1.1499 + // when !mDoFormat, mAddSpace is not used, mAddSpace is always false 1.1500 + // so, in the case where mDoWrap && !mDoFormat, if we want to enter in this condition... 1.1501 + 1.1502 + // We can avoid to wrap. We try to add the whole block 1.1503 + // in an empty new line 1.1504 + 1.1505 + AppendNewLineToString(aOutputStr); 1.1506 + aPos = aSequenceStart; 1.1507 + thisSequenceStartsAtBeginningOfLine = true; 1.1508 + onceAgainBecauseWeAddedBreakInFront = true; 1.1509 + } 1.1510 + else { 1.1511 + // we must wrap 1.1512 + onceAgainBecauseWeAddedBreakInFront = false; 1.1513 + bool foundWrapPosition = false; 1.1514 + int32_t wrapPosition; 1.1515 + 1.1516 + nsILineBreaker *lineBreaker = nsContentUtils::LineBreaker(); 1.1517 + 1.1518 + wrapPosition = lineBreaker->Prev(aSequenceStart, 1.1519 + (aEnd - aSequenceStart), 1.1520 + (aPos - aSequenceStart) + 1); 1.1521 + if (wrapPosition != NS_LINEBREAKER_NEED_MORE_TEXT) { 1.1522 + foundWrapPosition = true; 1.1523 + } 1.1524 + else { 1.1525 + wrapPosition = lineBreaker->Next(aSequenceStart, 1.1526 + (aEnd - aSequenceStart), 1.1527 + (aPos - aSequenceStart)); 1.1528 + if (wrapPosition != NS_LINEBREAKER_NEED_MORE_TEXT) { 1.1529 + foundWrapPosition = true; 1.1530 + } 1.1531 + } 1.1532 + 1.1533 + if (foundWrapPosition) { 1.1534 + if (!mColPos && mDoFormat) { 1.1535 + AppendIndentation(aOutputStr); 1.1536 + } 1.1537 + else if (mAddSpace) { 1.1538 + aOutputStr.Append(char16_t(' ')); 1.1539 + mAddSpace = false; 1.1540 + } 1.1541 + aOutputStr.Append(aSequenceStart, wrapPosition); 1.1542 + 1.1543 + AppendNewLineToString(aOutputStr); 1.1544 + aPos = aSequenceStart + wrapPosition; 1.1545 + aMayIgnoreStartOfLineWhitespaceSequence = true; 1.1546 + } 1.1547 + else { 1.1548 + // try some simple fallback logic 1.1549 + // go forward up to the next whitespace position, 1.1550 + // in the worst case this will be all the rest of the data 1.1551 + 1.1552 + // we update the mColPos variable with the length of 1.1553 + // the part already parsed. 1.1554 + mColPos += length; 1.1555 + 1.1556 + // now try to find the next whitespace 1.1557 + do { 1.1558 + if (*aPos == ' ' || *aPos == '\t' || *aPos == '\n') { 1.1559 + break; 1.1560 + } 1.1561 + 1.1562 + ++aPos; 1.1563 + ++mColPos; 1.1564 + } while (aPos < aEnd); 1.1565 + 1.1566 + if (mAddSpace) { 1.1567 + aOutputStr.Append(char16_t(' ')); 1.1568 + mAddSpace = false; 1.1569 + } 1.1570 + aOutputStr.Append(aSequenceStart, aPos - aSequenceStart); 1.1571 + } 1.1572 + } 1.1573 + aSequenceStartAfterAWhiteSpace = false; 1.1574 + } 1.1575 + } while (onceAgainBecauseWeAddedBreakInFront); 1.1576 +} 1.1577 + 1.1578 +void 1.1579 +nsXMLContentSerializer::AppendToStringFormatedWrapped(const nsASingleFragmentString& aStr, 1.1580 + nsAString& aOutputStr) 1.1581 +{ 1.1582 + if (mBodyOnly && !mInBody) { 1.1583 + return; 1.1584 + } 1.1585 + 1.1586 + nsASingleFragmentString::const_char_iterator pos, end, sequenceStart; 1.1587 + 1.1588 + aStr.BeginReading(pos); 1.1589 + aStr.EndReading(end); 1.1590 + 1.1591 + bool sequenceStartAfterAWhitespace = false; 1.1592 + if (pos < end) { 1.1593 + nsAString::const_char_iterator end2; 1.1594 + aOutputStr.EndReading(end2); 1.1595 + --end2; 1.1596 + if (*end2 == ' ' || *end2 == '\n' || *end2 == '\t') { 1.1597 + sequenceStartAfterAWhitespace = true; 1.1598 + } 1.1599 + } 1.1600 + 1.1601 + // if the current line already has text on it, such as a tag, 1.1602 + // leading whitespace is significant 1.1603 + bool mayIgnoreStartOfLineWhitespaceSequence = 1.1604 + (!mColPos || (mIsIndentationAddedOnCurrentLine && 1.1605 + sequenceStartAfterAWhitespace && 1.1606 + uint32_t(mColPos) == mIndent.Length())); 1.1607 + 1.1608 + while (pos < end) { 1.1609 + sequenceStart = pos; 1.1610 + 1.1611 + // if beginning of a whitespace sequence 1.1612 + if (*pos == ' ' || *pos == '\n' || *pos == '\t') { 1.1613 + AppendFormatedWrapped_WhitespaceSequence(pos, end, sequenceStart, 1.1614 + mayIgnoreStartOfLineWhitespaceSequence, aOutputStr); 1.1615 + } 1.1616 + else { // any other non-whitespace char 1.1617 + AppendWrapped_NonWhitespaceSequence(pos, end, sequenceStart, 1.1618 + mayIgnoreStartOfLineWhitespaceSequence, sequenceStartAfterAWhitespace, aOutputStr); 1.1619 + } 1.1620 + } 1.1621 +} 1.1622 + 1.1623 +void 1.1624 +nsXMLContentSerializer::AppendWrapped_WhitespaceSequence( 1.1625 + nsASingleFragmentString::const_char_iterator &aPos, 1.1626 + const nsASingleFragmentString::const_char_iterator aEnd, 1.1627 + const nsASingleFragmentString::const_char_iterator aSequenceStart, 1.1628 + nsAString &aOutputStr) 1.1629 +{ 1.1630 + // Handle the complete sequence of whitespace. 1.1631 + // Continue to iterate until we find the first non-whitespace char. 1.1632 + // Updates "aPos" to point to the first unhandled char. 1.1633 + mAddSpace = false; 1.1634 + mIsIndentationAddedOnCurrentLine = false; 1.1635 + 1.1636 + bool leaveLoop = false; 1.1637 + nsASingleFragmentString::const_char_iterator lastPos = aPos; 1.1638 + 1.1639 + do { 1.1640 + switch (*aPos) { 1.1641 + case ' ': 1.1642 + case '\t': 1.1643 + // if there are too many spaces on a line, we wrap 1.1644 + if (mColPos >= mMaxColumn) { 1.1645 + if (lastPos != aPos) { 1.1646 + aOutputStr.Append(lastPos, aPos - lastPos); 1.1647 + } 1.1648 + AppendToString(mLineBreak, aOutputStr); 1.1649 + mColPos = 0; 1.1650 + lastPos = aPos; 1.1651 + } 1.1652 + 1.1653 + ++mColPos; 1.1654 + ++aPos; 1.1655 + break; 1.1656 + case '\n': 1.1657 + if (lastPos != aPos) { 1.1658 + aOutputStr.Append(lastPos, aPos - lastPos); 1.1659 + } 1.1660 + AppendToString(mLineBreak, aOutputStr); 1.1661 + mColPos = 0; 1.1662 + ++aPos; 1.1663 + lastPos = aPos; 1.1664 + break; 1.1665 + default: 1.1666 + leaveLoop = true; 1.1667 + break; 1.1668 + } 1.1669 + } while (!leaveLoop && aPos < aEnd); 1.1670 + 1.1671 + if (lastPos != aPos) { 1.1672 + aOutputStr.Append(lastPos, aPos - lastPos); 1.1673 + } 1.1674 +} 1.1675 + 1.1676 +void 1.1677 +nsXMLContentSerializer::AppendToStringWrapped(const nsASingleFragmentString& aStr, 1.1678 + nsAString& aOutputStr) 1.1679 +{ 1.1680 + if (mBodyOnly && !mInBody) { 1.1681 + return; 1.1682 + } 1.1683 + 1.1684 + nsASingleFragmentString::const_char_iterator pos, end, sequenceStart; 1.1685 + 1.1686 + aStr.BeginReading(pos); 1.1687 + aStr.EndReading(end); 1.1688 + 1.1689 + // not used in this case, but needed by AppendWrapped_NonWhitespaceSequence 1.1690 + bool mayIgnoreStartOfLineWhitespaceSequence = false; 1.1691 + mMayIgnoreLineBreakSequence = false; 1.1692 + 1.1693 + bool sequenceStartAfterAWhitespace = false; 1.1694 + if (pos < end && !aOutputStr.IsEmpty()) { 1.1695 + nsAString::const_char_iterator end2; 1.1696 + aOutputStr.EndReading(end2); 1.1697 + --end2; 1.1698 + if (*end2 == ' ' || *end2 == '\n' || *end2 == '\t') { 1.1699 + sequenceStartAfterAWhitespace = true; 1.1700 + } 1.1701 + } 1.1702 + 1.1703 + while (pos < end) { 1.1704 + sequenceStart = pos; 1.1705 + 1.1706 + // if beginning of a whitespace sequence 1.1707 + if (*pos == ' ' || *pos == '\n' || *pos == '\t') { 1.1708 + sequenceStartAfterAWhitespace = true; 1.1709 + AppendWrapped_WhitespaceSequence(pos, end, sequenceStart, aOutputStr); 1.1710 + } 1.1711 + else { // any other non-whitespace char 1.1712 + AppendWrapped_NonWhitespaceSequence(pos, end, sequenceStart, 1.1713 + mayIgnoreStartOfLineWhitespaceSequence, sequenceStartAfterAWhitespace, aOutputStr); 1.1714 + } 1.1715 + } 1.1716 +}