tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsXHTMLContentSerializer.cpp (24568B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 /*
      8 * nsIContentSerializer implementation that can be used with an
      9 * nsIDocumentEncoder to convert an XHTML (not HTML!) DOM to an XHTML
     10 * string that could be parsed into more or less the original DOM.
     11 */
     12 
     13 #include "nsXHTMLContentSerializer.h"
     14 
     15 #include "mozilla/dom/Document.h"
     16 #include "mozilla/dom/Element.h"
     17 #include "nsAttrName.h"
     18 #include "nsCRT.h"
     19 #include "nsComputedDOMStyle.h"
     20 #include "nsContentUtils.h"
     21 #include "nsElementTable.h"
     22 #include "nsEscape.h"
     23 #include "nsGkAtoms.h"
     24 #include "nsIContent.h"
     25 #include "nsIDocumentEncoder.h"
     26 #include "nsIScriptElement.h"
     27 #include "nsIURI.h"
     28 #include "nsNameSpaceManager.h"
     29 #include "nsNetUtil.h"
     30 #include "nsString.h"
     31 #include "nsStubMutationObserver.h"
     32 #include "nsUnicharUtils.h"
     33 
     34 using namespace mozilla;
     35 using namespace mozilla::dom;
     36 
     37 static const int32_t kLongLineLen = 128;
     38 
     39 #define kXMLNS "xmlns"
     40 
     41 nsresult NS_NewXHTMLContentSerializer(nsIContentSerializer** aSerializer) {
     42  RefPtr<nsXHTMLContentSerializer> it = new nsXHTMLContentSerializer();
     43  it.forget(aSerializer);
     44  return NS_OK;
     45 }
     46 
     47 nsXHTMLContentSerializer::nsXHTMLContentSerializer()
     48    : mIsHTMLSerializer(false),
     49      mIsCopying(false),
     50      mDisableEntityEncoding(0),
     51      mRewriteEncodingDeclaration(false),
     52      mIsFirstChildOfOL(false) {}
     53 
     54 nsXHTMLContentSerializer::~nsXHTMLContentSerializer() {
     55  NS_ASSERTION(mOLStateStack.IsEmpty(), "Expected OL State stack to be empty");
     56 }
     57 
     58 NS_IMETHODIMP
     59 nsXHTMLContentSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn,
     60                               const Encoding* aEncoding, bool aIsCopying,
     61                               bool aRewriteEncodingDeclaration,
     62                               bool* aNeedsPreformatScanning,
     63                               nsAString& aOutput) {
     64  // The previous version of the HTML serializer did implicit wrapping
     65  // when there is no flags, so we keep wrapping in order to keep
     66  // compatibility with the existing calling code
     67  // XXXLJ perhaps should we remove this default settings later ?
     68  if (aFlags & nsIDocumentEncoder::OutputFormatted) {
     69    aFlags = aFlags | nsIDocumentEncoder::OutputWrap;
     70  }
     71 
     72  nsresult rv;
     73  rv = nsXMLContentSerializer::Init(aFlags, aWrapColumn, aEncoding, aIsCopying,
     74                                    aRewriteEncodingDeclaration,
     75                                    aNeedsPreformatScanning, aOutput);
     76  NS_ENSURE_SUCCESS(rv, rv);
     77 
     78  mRewriteEncodingDeclaration = aRewriteEncodingDeclaration;
     79  mIsCopying = aIsCopying;
     80  mIsFirstChildOfOL = false;
     81  mInBody = 0;
     82  mDisableEntityEncoding = 0;
     83  mBodyOnly = (mFlags & nsIDocumentEncoder::OutputBodyOnly);
     84 
     85  return NS_OK;
     86 }
     87 
     88 // See if the string has any lines longer than longLineLen:
     89 // if so, we presume formatting is wonky (e.g. the node has been edited)
     90 // and we'd better rewrap the whole text node.
     91 bool nsXHTMLContentSerializer::HasLongLines(const nsString& text,
     92                                            int32_t& aLastNewlineOffset) {
     93  uint32_t start = 0;
     94  uint32_t theLen = text.Length();
     95  bool rv = false;
     96  aLastNewlineOffset = kNotFound;
     97  for (start = 0; start < theLen;) {
     98    int32_t eol = text.FindChar('\n', start);
     99    if (eol < 0) {
    100      eol = text.Length();
    101    } else {
    102      aLastNewlineOffset = eol;
    103    }
    104    if (int32_t(eol - start) > kLongLineLen) rv = true;
    105    start = eol + 1;
    106  }
    107  return rv;
    108 }
    109 
    110 NS_IMETHODIMP
    111 nsXHTMLContentSerializer::AppendText(Text* aText, int32_t aStartOffset,
    112                                     int32_t aEndOffset) {
    113  NS_ENSURE_ARG(aText);
    114  NS_ENSURE_STATE(mOutput);
    115 
    116  nsAutoString data;
    117  nsresult rv;
    118 
    119  rv = AppendTextData(aText, aStartOffset, aEndOffset, data, true);
    120  if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
    121 
    122  if (mDoRaw || PreLevel() > 0) {
    123    NS_ENSURE_TRUE(AppendToStringConvertLF(data, *mOutput),
    124                   NS_ERROR_OUT_OF_MEMORY);
    125  } else if (mDoFormat) {
    126    NS_ENSURE_TRUE(AppendToStringFormatedWrapped(data, *mOutput),
    127                   NS_ERROR_OUT_OF_MEMORY);
    128  } else if (mDoWrap) {
    129    NS_ENSURE_TRUE(AppendToStringWrapped(data, *mOutput),
    130                   NS_ERROR_OUT_OF_MEMORY);
    131  } else {
    132    int32_t lastNewlineOffset = kNotFound;
    133    if (HasLongLines(data, lastNewlineOffset)) {
    134      // We have long lines, rewrap
    135      mDoWrap = true;
    136      bool result = AppendToStringWrapped(data, *mOutput);
    137      mDoWrap = false;
    138      NS_ENSURE_TRUE(result, NS_ERROR_OUT_OF_MEMORY);
    139    } else {
    140      NS_ENSURE_TRUE(AppendToStringConvertLF(data, *mOutput),
    141                     NS_ERROR_OUT_OF_MEMORY);
    142    }
    143  }
    144 
    145  return NS_OK;
    146 }
    147 
    148 bool nsXHTMLContentSerializer::SerializeAttributes(
    149    Element* aElement, Element* aOriginalElement, nsAString& aTagPrefix,
    150    const nsAString& aTagNamespaceURI, nsAtom* aTagName, nsAString& aStr,
    151    uint32_t aSkipAttr, bool aAddNSAttr) {
    152  nsresult rv;
    153  uint32_t index, count;
    154  nsAutoString prefixStr, uriStr, valueStr;
    155  nsAutoString xmlnsStr;
    156  xmlnsStr.AssignLiteral(kXMLNS);
    157 
    158  int32_t contentNamespaceID = aElement->GetNameSpaceID();
    159 
    160  MaybeSerializeIsValue(aElement, aStr);
    161 
    162  // this method is not called by nsHTMLContentSerializer
    163  // so we don't have to check HTML element, just XHTML
    164 
    165  if (mIsCopying && kNameSpaceID_XHTML == contentNamespaceID) {
    166    // Need to keep track of OL and LI elements in order to get ordinal number
    167    // for the LI.
    168    if (aTagName == nsGkAtoms::ol) {
    169      // We are copying and current node is an OL;
    170      // Store its start attribute value in olState->startVal.
    171      nsAutoString start;
    172      int32_t startAttrVal = 0;
    173      aElement->GetAttr(nsGkAtoms::start, start);
    174      if (!start.IsEmpty()) {
    175        nsresult rv = NS_OK;
    176        startAttrVal = start.ToInteger(&rv);
    177        // If OL has "start" attribute, first LI element has to start with that
    178        // value Therefore subtracting 1 as all the LI elements are incrementing
    179        // it before using it; In failure of ToInteger(), default StartAttrValue
    180        // to 0.
    181        if (NS_SUCCEEDED(rv))
    182          --startAttrVal;
    183        else
    184          startAttrVal = 0;
    185      }
    186      olState state(startAttrVal, true);
    187      mOLStateStack.AppendElement(state);
    188    } else if (aTagName == nsGkAtoms::li) {
    189      mIsFirstChildOfOL = IsFirstChildOfOL(aOriginalElement);
    190      if (mIsFirstChildOfOL) {
    191        // If OL is parent of this LI, serialize attributes in different manner.
    192        NS_ENSURE_TRUE(SerializeLIValueAttribute(aElement, aStr), false);
    193      }
    194    }
    195  }
    196 
    197  // If we had to add a new namespace declaration, serialize
    198  // and push it on the namespace stack
    199  if (aAddNSAttr) {
    200    if (aTagPrefix.IsEmpty()) {
    201      // Serialize default namespace decl
    202      NS_ENSURE_TRUE(
    203          SerializeAttr(u""_ns, xmlnsStr, aTagNamespaceURI, aStr, true), false);
    204    } else {
    205      // Serialize namespace decl
    206      NS_ENSURE_TRUE(
    207          SerializeAttr(xmlnsStr, aTagPrefix, aTagNamespaceURI, aStr, true),
    208          false);
    209    }
    210    PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement);
    211  }
    212 
    213  count = aElement->GetAttrCount();
    214 
    215  // Now serialize each of the attributes
    216  // XXX Unfortunately we need a namespace manager to get
    217  // attribute URIs.
    218  for (index = 0; index < count; index++) {
    219    if (aSkipAttr == index) {
    220      continue;
    221    }
    222 
    223    dom::BorrowedAttrInfo info = aElement->GetAttrInfoAt(index);
    224    const nsAttrName* name = info.mName;
    225 
    226    int32_t namespaceID = name->NamespaceID();
    227    nsAtom* attrName = name->LocalName();
    228    nsAtom* attrPrefix = name->GetPrefix();
    229 
    230    // Filter out any attribute starting with [-|_]moz
    231    nsDependentAtomString attrNameStr(attrName);
    232    if (StringBeginsWith(attrNameStr, u"_moz"_ns) ||
    233        StringBeginsWith(attrNameStr, u"-moz"_ns)) {
    234      continue;
    235    }
    236 
    237    if (attrPrefix) {
    238      attrPrefix->ToString(prefixStr);
    239    } else {
    240      prefixStr.Truncate();
    241    }
    242 
    243    bool addNSAttr = false;
    244    if (kNameSpaceID_XMLNS != namespaceID) {
    245      nsNameSpaceManager::GetInstance()->GetNameSpaceURI(namespaceID, uriStr);
    246      addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true);
    247    }
    248 
    249    info.mValue->ToString(valueStr);
    250 
    251    nsDependentAtomString nameStr(attrName);
    252    bool isJS = false;
    253 
    254    if (kNameSpaceID_XHTML == contentNamespaceID) {
    255      if (mIsCopying && mIsFirstChildOfOL && (aTagName == nsGkAtoms::li) &&
    256          (attrName == nsGkAtoms::value)) {
    257        // This is handled separately in SerializeLIValueAttribute()
    258        continue;
    259      }
    260 
    261      isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr);
    262 
    263      if (namespaceID == kNameSpaceID_None &&
    264          ((attrName == nsGkAtoms::href) || (attrName == nsGkAtoms::src))) {
    265        // Make all links absolute when converting only the selection:
    266        if (mFlags & nsIDocumentEncoder::OutputAbsoluteLinks) {
    267          // Would be nice to handle OBJECT tags,
    268          // but that gets more complicated since we have to
    269          // search the tag list for CODEBASE as well.
    270          // For now, just leave them relative.
    271          nsIURI* uri = aElement->GetBaseURI();
    272          if (uri) {
    273            nsAutoString absURI;
    274            rv = NS_MakeAbsoluteURI(absURI, valueStr, uri);
    275            if (NS_SUCCEEDED(rv)) {
    276              valueStr = absURI;
    277            }
    278          }
    279        }
    280      }
    281 
    282      if (mRewriteEncodingDeclaration && aTagName == nsGkAtoms::meta &&
    283          attrName == nsGkAtoms::content) {
    284        // If we're serializing a <meta http-equiv="content-type">,
    285        // use the proper value, rather than what's in the document.
    286        nsAutoString header;
    287        aElement->GetAttr(nsGkAtoms::httpEquiv, header);
    288        if (header.LowerCaseEqualsLiteral("content-type")) {
    289          valueStr =
    290              u"text/html; charset="_ns + NS_ConvertASCIItoUTF16(mCharset);
    291        }
    292      }
    293 
    294      // Expand shorthand attribute.
    295      if (namespaceID == kNameSpaceID_None &&
    296          IsShorthandAttr(attrName, aTagName) && valueStr.IsEmpty()) {
    297        valueStr = nameStr;
    298      }
    299    } else {
    300      isJS = IsJavaScript(aElement, attrName, namespaceID, valueStr);
    301    }
    302 
    303    NS_ENSURE_TRUE(SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS),
    304                   false);
    305 
    306    if (addNSAttr) {
    307      NS_ASSERTION(!prefixStr.IsEmpty(),
    308                   "Namespaced attributes must have a prefix");
    309      NS_ENSURE_TRUE(SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, true),
    310                     false);
    311      PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement);
    312    }
    313  }
    314 
    315  return true;
    316 }
    317 
    318 bool nsXHTMLContentSerializer::AfterElementStart(nsIContent* aContent,
    319                                                 nsIContent* aOriginalElement,
    320                                                 nsAString& aStr) {
    321  if (mRewriteEncodingDeclaration && aContent->IsHTMLElement(nsGkAtoms::head)) {
    322    // Check if there already are any content-type meta children.
    323    // If there are, they will be modified to use the correct charset.
    324    // If there aren't, we'll insert one here.
    325    bool hasMeta = false;
    326    for (nsIContent* child = aContent->GetFirstChild(); child;
    327         child = child->GetNextSibling()) {
    328      if (child->IsHTMLElement(nsGkAtoms::meta) &&
    329          child->AsElement()->HasAttr(nsGkAtoms::content)) {
    330        nsAutoString header;
    331        child->AsElement()->GetAttr(nsGkAtoms::httpEquiv, header);
    332 
    333        if (header.LowerCaseEqualsLiteral("content-type")) {
    334          hasMeta = true;
    335          break;
    336        }
    337      }
    338    }
    339 
    340    if (!hasMeta) {
    341      NS_ENSURE_TRUE(AppendNewLineToString(aStr), false);
    342      if (mDoFormat) {
    343        NS_ENSURE_TRUE(AppendIndentation(aStr), false);
    344      }
    345      NS_ENSURE_TRUE(
    346          AppendToString(u"<meta http-equiv=\"content-type\""_ns, aStr), false);
    347      NS_ENSURE_TRUE(AppendToString(u" content=\"text/html; charset="_ns, aStr),
    348                     false);
    349      NS_ENSURE_TRUE(AppendToString(NS_ConvertASCIItoUTF16(mCharset), aStr),
    350                     false);
    351      if (mIsHTMLSerializer) {
    352        NS_ENSURE_TRUE(AppendToString(u"\">"_ns, aStr), false);
    353      } else {
    354        NS_ENSURE_TRUE(AppendToString(u"\" />"_ns, aStr), false);
    355      }
    356    }
    357  }
    358 
    359  return true;
    360 }
    361 
    362 void nsXHTMLContentSerializer::AfterElementEnd(nsIContent* aContent,
    363                                               nsAString& aStr) {
    364  NS_ASSERTION(!mIsHTMLSerializer,
    365               "nsHTMLContentSerializer shouldn't call this method !");
    366 
    367  // this method is not called by nsHTMLContentSerializer
    368  // so we don't have to check HTML element, just XHTML
    369  if (aContent->IsHTMLElement(nsGkAtoms::body)) {
    370    --mInBody;
    371  }
    372 }
    373 
    374 NS_IMETHODIMP
    375 nsXHTMLContentSerializer::AppendDocumentStart(Document* aDocument) {
    376  if (!mBodyOnly) {
    377    return nsXMLContentSerializer::AppendDocumentStart(aDocument);
    378  }
    379 
    380  return NS_OK;
    381 }
    382 
    383 bool nsXHTMLContentSerializer::CheckElementStart(Element* aElement,
    384                                                 bool& aForceFormat,
    385                                                 nsAString& aStr,
    386                                                 nsresult& aResult) {
    387  aResult = NS_OK;
    388 
    389  // The _moz_dirty attribute is emitted by the editor to
    390  // indicate that this element should be pretty printed
    391  // even if we're not in pretty printing mode
    392  aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
    393                 aElement->HasAttr(nsGkAtoms::mozdirty);
    394 
    395  if (aElement->IsHTMLElement(nsGkAtoms::br) &&
    396      (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre) &&
    397      PreLevel() > 0) {
    398    aResult = AppendNewLineToString(aStr) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
    399    return false;
    400  }
    401 
    402  if (aElement->IsHTMLElement(nsGkAtoms::body)) {
    403    ++mInBody;
    404  }
    405 
    406  return true;
    407 }
    408 
    409 bool nsXHTMLContentSerializer::CheckElementEnd(Element* aElement,
    410                                               Element* aOriginalElement,
    411                                               bool& aForceFormat,
    412                                               nsAString& aStr) {
    413  NS_ASSERTION(!mIsHTMLSerializer,
    414               "nsHTMLContentSerializer shouldn't call this method !");
    415 
    416  aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
    417                 aElement->HasAttr(nsGkAtoms::mozdirty);
    418 
    419  if (mIsCopying && aElement->IsHTMLElement(nsGkAtoms::ol)) {
    420    NS_ASSERTION((!mOLStateStack.IsEmpty()), "Cannot have an empty OL Stack");
    421    /* Though at this point we must always have an state to be deleted as all
    422       the OL opening tags are supposed to push an olState object to the stack*/
    423    if (!mOLStateStack.IsEmpty()) {
    424      mOLStateStack.RemoveLastElement();
    425    }
    426  }
    427 
    428  bool dummyFormat;
    429  return nsXMLContentSerializer::CheckElementEnd(aElement, aOriginalElement,
    430                                                 dummyFormat, aStr);
    431 }
    432 
    433 bool nsXHTMLContentSerializer::AppendAndTranslateEntities(
    434    const nsAString& aStr, nsAString& aOutputStr) {
    435  if (mBodyOnly && !mInBody) {
    436    return true;
    437  }
    438 
    439  if (mDisableEntityEncoding) {
    440    return aOutputStr.Append(aStr, fallible);
    441  }
    442 
    443  return nsXMLContentSerializer::AppendAndTranslateEntities(aStr, aOutputStr);
    444 }
    445 
    446 bool nsXHTMLContentSerializer::IsShorthandAttr(const nsAtom* aAttrName,
    447                                               const nsAtom* aElementName) {
    448  // checked
    449  if ((aAttrName == nsGkAtoms::checked) && (aElementName == nsGkAtoms::input)) {
    450    return true;
    451  }
    452 
    453  // compact
    454  if ((aAttrName == nsGkAtoms::compact) &&
    455      (aElementName == nsGkAtoms::dir || aElementName == nsGkAtoms::dl ||
    456       aElementName == nsGkAtoms::menu || aElementName == nsGkAtoms::ol ||
    457       aElementName == nsGkAtoms::ul)) {
    458    return true;
    459  }
    460 
    461  // declare
    462  if ((aAttrName == nsGkAtoms::declare) &&
    463      (aElementName == nsGkAtoms::object)) {
    464    return true;
    465  }
    466 
    467  // defer
    468  if ((aAttrName == nsGkAtoms::defer) && (aElementName == nsGkAtoms::script)) {
    469    return true;
    470  }
    471 
    472  // disabled
    473  if ((aAttrName == nsGkAtoms::disabled) &&
    474      (aElementName == nsGkAtoms::button || aElementName == nsGkAtoms::input ||
    475       aElementName == nsGkAtoms::optgroup ||
    476       aElementName == nsGkAtoms::option || aElementName == nsGkAtoms::select ||
    477       aElementName == nsGkAtoms::textarea)) {
    478    return true;
    479  }
    480 
    481  // ismap
    482  if ((aAttrName == nsGkAtoms::ismap) &&
    483      (aElementName == nsGkAtoms::img || aElementName == nsGkAtoms::input)) {
    484    return true;
    485  }
    486 
    487  // multiple
    488  if ((aAttrName == nsGkAtoms::multiple) &&
    489      (aElementName == nsGkAtoms::select)) {
    490    return true;
    491  }
    492 
    493  // noresize
    494  if ((aAttrName == nsGkAtoms::noresize) &&
    495      (aElementName == nsGkAtoms::frame)) {
    496    return true;
    497  }
    498 
    499  // noshade
    500  if ((aAttrName == nsGkAtoms::noshade) && (aElementName == nsGkAtoms::hr)) {
    501    return true;
    502  }
    503 
    504  // nowrap
    505  if ((aAttrName == nsGkAtoms::nowrap) &&
    506      (aElementName == nsGkAtoms::td || aElementName == nsGkAtoms::th)) {
    507    return true;
    508  }
    509 
    510  // readonly
    511  if ((aAttrName == nsGkAtoms::readonly) &&
    512      (aElementName == nsGkAtoms::input ||
    513       aElementName == nsGkAtoms::textarea)) {
    514    return true;
    515  }
    516 
    517  // selected
    518  if ((aAttrName == nsGkAtoms::selected) &&
    519      (aElementName == nsGkAtoms::option)) {
    520    return true;
    521  }
    522 
    523  // autoplay and controls
    524  if ((aElementName == nsGkAtoms::video || aElementName == nsGkAtoms::audio) &&
    525      (aAttrName == nsGkAtoms::autoplay || aAttrName == nsGkAtoms::muted ||
    526       aAttrName == nsGkAtoms::controls)) {
    527    return true;
    528  }
    529 
    530  return false;
    531 }
    532 
    533 bool nsXHTMLContentSerializer::LineBreakBeforeOpen(int32_t aNamespaceID,
    534                                                   nsAtom* aName) {
    535  if (aNamespaceID != kNameSpaceID_XHTML) {
    536    return mAddSpace;
    537  }
    538 
    539  if (aName == nsGkAtoms::title || aName == nsGkAtoms::meta ||
    540      aName == nsGkAtoms::link || aName == nsGkAtoms::style ||
    541      aName == nsGkAtoms::select || aName == nsGkAtoms::option ||
    542      aName == nsGkAtoms::script || aName == nsGkAtoms::html) {
    543    return true;
    544  }
    545 
    546  return nsHTMLElement::IsBlock(nsHTMLTags::CaseSensitiveAtomTagToId(aName));
    547 }
    548 
    549 bool nsXHTMLContentSerializer::LineBreakAfterOpen(int32_t aNamespaceID,
    550                                                  nsAtom* aName) {
    551  if (aNamespaceID != kNameSpaceID_XHTML) {
    552    return false;
    553  }
    554 
    555  if ((aName == nsGkAtoms::html) || (aName == nsGkAtoms::head) ||
    556      (aName == nsGkAtoms::body) || (aName == nsGkAtoms::ul) ||
    557      (aName == nsGkAtoms::ol) || (aName == nsGkAtoms::dl) ||
    558      (aName == nsGkAtoms::table) || (aName == nsGkAtoms::tbody) ||
    559      (aName == nsGkAtoms::tr) || (aName == nsGkAtoms::br) ||
    560      (aName == nsGkAtoms::meta) || (aName == nsGkAtoms::link) ||
    561      (aName == nsGkAtoms::script) || (aName == nsGkAtoms::select) ||
    562      (aName == nsGkAtoms::map) || (aName == nsGkAtoms::area) ||
    563      (aName == nsGkAtoms::style)) {
    564    return true;
    565  }
    566 
    567  return false;
    568 }
    569 
    570 bool nsXHTMLContentSerializer::LineBreakBeforeClose(int32_t aNamespaceID,
    571                                                    nsAtom* aName) {
    572  if (aNamespaceID != kNameSpaceID_XHTML) {
    573    return false;
    574  }
    575 
    576  if ((aName == nsGkAtoms::html) || (aName == nsGkAtoms::head) ||
    577      (aName == nsGkAtoms::body) || (aName == nsGkAtoms::ul) ||
    578      (aName == nsGkAtoms::ol) || (aName == nsGkAtoms::dl) ||
    579      (aName == nsGkAtoms::select) || (aName == nsGkAtoms::table) ||
    580      (aName == nsGkAtoms::tbody)) {
    581    return true;
    582  }
    583  return false;
    584 }
    585 
    586 bool nsXHTMLContentSerializer::LineBreakAfterClose(int32_t aNamespaceID,
    587                                                   nsAtom* aName) {
    588  if (aNamespaceID != kNameSpaceID_XHTML) {
    589    return false;
    590  }
    591 
    592  if ((aName == nsGkAtoms::html) || (aName == nsGkAtoms::head) ||
    593      (aName == nsGkAtoms::body) || (aName == nsGkAtoms::tr) ||
    594      (aName == nsGkAtoms::th) || (aName == nsGkAtoms::td) ||
    595      (aName == nsGkAtoms::title) || (aName == nsGkAtoms::dt) ||
    596      (aName == nsGkAtoms::dd) || (aName == nsGkAtoms::select) ||
    597      (aName == nsGkAtoms::option) || (aName == nsGkAtoms::map)) {
    598    return true;
    599  }
    600 
    601  return nsHTMLElement::IsBlock(nsHTMLTags::CaseSensitiveAtomTagToId(aName));
    602 }
    603 
    604 void nsXHTMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode) {
    605  if (!ShouldMaintainPreLevel() || !aNode->IsHTMLElement()) {
    606    return;
    607  }
    608 
    609  if (IsElementPreformatted(aNode) ||
    610      aNode->IsAnyOfHTMLElements(nsGkAtoms::script, nsGkAtoms::style,
    611                                 nsGkAtoms::noscript, nsGkAtoms::noframes)) {
    612    PreLevel()++;
    613  }
    614 }
    615 
    616 void nsXHTMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode) {
    617  if (!ShouldMaintainPreLevel() || !aNode->IsHTMLElement()) {
    618    return;
    619  }
    620 
    621  if (IsElementPreformatted(aNode) ||
    622      aNode->IsAnyOfHTMLElements(nsGkAtoms::script, nsGkAtoms::style,
    623                                 nsGkAtoms::noscript, nsGkAtoms::noframes)) {
    624    --PreLevel();
    625  }
    626 }
    627 
    628 bool nsXHTMLContentSerializer::IsElementPreformatted(nsIContent* aNode) {
    629  MOZ_ASSERT(ShouldMaintainPreLevel(),
    630             "We should not be calling this needlessly");
    631 
    632  if (!aNode->IsElement()) {
    633    return false;
    634  }
    635  RefPtr<const ComputedStyle> computedStyle =
    636      nsComputedDOMStyle::GetComputedStyleNoFlush(aNode->AsElement());
    637  if (computedStyle) {
    638    const nsStyleText* textStyle = computedStyle->StyleText();
    639    return textStyle->WhiteSpaceOrNewlineIsSignificant();
    640  }
    641  return false;
    642 }
    643 
    644 bool nsXHTMLContentSerializer::SerializeLIValueAttribute(nsIContent* aElement,
    645                                                         nsAString& aStr) {
    646  // We are copying and we are at the "first" LI node of OL in selected range.
    647  // It may not be the first LI child of OL but it's first in the selected
    648  // range. Note that we get into this condition only once per a OL.
    649  bool found = false;
    650  nsAutoString valueStr;
    651 
    652  olState state(0, false);
    653 
    654  if (!mOLStateStack.IsEmpty()) {
    655    state = mOLStateStack[mOLStateStack.Length() - 1];
    656    // isFirstListItem should be true only before the serialization of the
    657    // first item in the list.
    658    state.isFirstListItem = false;
    659    mOLStateStack[mOLStateStack.Length() - 1] = state;
    660  }
    661 
    662  int32_t startVal = state.startVal;
    663  int32_t offset = 0;
    664 
    665  // Traverse previous siblings until we find one with "value" attribute.
    666  // offset keeps track of how many previous siblings we had to traverse.
    667  nsIContent* currNode = aElement;
    668  while (currNode && !found) {
    669    if (currNode->IsHTMLElement(nsGkAtoms::li)) {
    670      currNode->AsElement()->GetAttr(nsGkAtoms::value, valueStr);
    671      if (valueStr.IsEmpty()) {
    672        offset++;
    673      } else {
    674        found = true;
    675        nsresult rv = NS_OK;
    676        startVal = valueStr.ToInteger(&rv);
    677      }
    678    }
    679    currNode = currNode->GetPreviousSibling();
    680  }
    681  // If LI was not having "value", Set the "value" attribute for it.
    682  // Note that We are at the first LI in the selected range of OL.
    683  if (offset == 0 && found) {
    684    // offset = 0 => LI itself has the value attribute and we did not need to
    685    // traverse back. Just serialize value attribute like other tags.
    686    NS_ENSURE_TRUE(SerializeAttr(u""_ns, u"value"_ns, valueStr, aStr, false),
    687                   false);
    688  } else if (offset == 1 && !found) {
    689    /*(offset = 1 && !found) means either LI is the first child node of OL
    690    and LI is not having "value" attribute.
    691    In that case we would not like to set "value" attribute to reduce the
    692    changes.
    693    */
    694    // do nothing...
    695  } else if (offset > 0) {
    696    // Set value attribute.
    697    nsAutoString valueStr;
    698 
    699    // As serializer needs to use this valueAttr we are creating here,
    700    valueStr.AppendInt(startVal + offset);
    701    NS_ENSURE_TRUE(SerializeAttr(u""_ns, u"value"_ns, valueStr, aStr, false),
    702                   false);
    703  }
    704 
    705  return true;
    706 }
    707 
    708 bool nsXHTMLContentSerializer::IsFirstChildOfOL(nsIContent* aElement) {
    709  nsIContent* parent = aElement->GetParent();
    710  if (parent && parent->NodeName().LowerCaseEqualsLiteral("ol")) {
    711    if (!mOLStateStack.IsEmpty()) {
    712      olState state = mOLStateStack[mOLStateStack.Length() - 1];
    713      if (state.isFirstListItem) return true;
    714    }
    715  }
    716 
    717  return false;
    718 }
    719 
    720 bool nsXHTMLContentSerializer::HasNoChildren(nsIContent* aContent) {
    721  for (nsIContent* child = aContent->GetFirstChild(); child;
    722       child = child->GetNextSibling()) {
    723    if (!child->IsText()) return false;
    724 
    725    if (child->TextLength()) return false;
    726  }
    727 
    728  return true;
    729 }