content/base/src/nsXMLContentSerializer.cpp

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

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

mercurial