content/base/src/nsXHTMLContentSerializer.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.)

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* vim: set ts=2 sw=2 et 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/. */
     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  */
    13 #include "nsXHTMLContentSerializer.h"
    15 #include "nsIDOMElement.h"
    16 #include "nsIContent.h"
    17 #include "nsIDocument.h"
    18 #include "nsNameSpaceManager.h"
    19 #include "nsString.h"
    20 #include "nsUnicharUtils.h"
    21 #include "nsXPIDLString.h"
    22 #include "nsIServiceManager.h"
    23 #include "nsIDocumentEncoder.h"
    24 #include "nsGkAtoms.h"
    25 #include "nsIURI.h"
    26 #include "nsNetUtil.h"
    27 #include "nsEscape.h"
    28 #include "nsITextToSubURI.h"
    29 #include "nsCRT.h"
    30 #include "nsIParserService.h"
    31 #include "nsContentUtils.h"
    32 #include "nsLWBrkCIID.h"
    33 #include "nsIScriptElement.h"
    34 #include "nsAttrName.h"
    35 #include "nsParserConstants.h"
    37 static const int32_t kLongLineLen = 128;
    39 #define kXMLNS "xmlns"
    41 nsresult NS_NewXHTMLContentSerializer(nsIContentSerializer** aSerializer)
    42 {
    43   nsXHTMLContentSerializer* it = new nsXHTMLContentSerializer();
    44   if (!it) {
    45     return NS_ERROR_OUT_OF_MEMORY;
    46   }
    48   return CallQueryInterface(it, aSerializer);
    49 }
    51 nsXHTMLContentSerializer::nsXHTMLContentSerializer()
    52   : mIsHTMLSerializer(false)
    53 {
    54 }
    56 nsXHTMLContentSerializer::~nsXHTMLContentSerializer()
    57 {
    58   NS_ASSERTION(mOLStateStack.IsEmpty(), "Expected OL State stack to be empty");
    59 }
    61 NS_IMETHODIMP
    62 nsXHTMLContentSerializer::Init(uint32_t aFlags, uint32_t aWrapColumn,
    63                               const char* aCharSet, bool aIsCopying,
    64                               bool aRewriteEncodingDeclaration)
    65 {
    66   // The previous version of the HTML serializer did implicit wrapping
    67   // when there is no flags, so we keep wrapping in order to keep
    68   // compatibility with the existing calling code
    69   // XXXLJ perhaps should we remove this default settings later ?
    70   if (aFlags & nsIDocumentEncoder::OutputFormatted ) {
    71       aFlags = aFlags | nsIDocumentEncoder::OutputWrap;
    72   }
    74   nsresult rv;
    75   rv = nsXMLContentSerializer::Init(aFlags, aWrapColumn, aCharSet, aIsCopying, aRewriteEncodingDeclaration);
    76   NS_ENSURE_SUCCESS(rv, rv);
    78   mRewriteEncodingDeclaration = aRewriteEncodingDeclaration;
    79   mIsCopying = aIsCopying;
    80   mIsFirstChildOfOL = false;
    81   mInBody = 0;
    82   mDisableEntityEncoding = 0;
    83   mBodyOnly = (mFlags & nsIDocumentEncoder::OutputBodyOnly) ? true
    84                                                             : false;
    86   // set up entity converter if we are going to need it
    87   if (mFlags & nsIDocumentEncoder::OutputEncodeW3CEntities) {
    88     mEntityConverter = do_CreateInstance(NS_ENTITYCONVERTER_CONTRACTID);
    89   }
    90   return NS_OK;
    91 }
    94 // See if the string has any lines longer than longLineLen:
    95 // if so, we presume formatting is wonky (e.g. the node has been edited)
    96 // and we'd better rewrap the whole text node.
    97 bool
    98 nsXHTMLContentSerializer::HasLongLines(const nsString& text, int32_t& aLastNewlineOffset)
    99 {
   100   uint32_t start=0;
   101   uint32_t theLen = text.Length();
   102   bool rv = false;
   103   aLastNewlineOffset = kNotFound;
   104   for (start = 0; start < theLen; ) {
   105     int32_t eol = text.FindChar('\n', start);
   106     if (eol < 0) {
   107       eol = text.Length();
   108     }
   109     else {
   110       aLastNewlineOffset = eol;
   111     }
   112     if (int32_t(eol - start) > kLongLineLen)
   113       rv = true;
   114     start = eol + 1;
   115   }
   116   return rv;
   117 }
   119 NS_IMETHODIMP
   120 nsXHTMLContentSerializer::AppendText(nsIContent* aText,
   121                                      int32_t aStartOffset,
   122                                      int32_t aEndOffset,
   123                                      nsAString& aStr)
   124 {
   125   NS_ENSURE_ARG(aText);
   127   nsAutoString data;
   128   nsresult rv;
   130   rv = AppendTextData(aText, aStartOffset, aEndOffset, data, true);
   131   if (NS_FAILED(rv))
   132     return NS_ERROR_FAILURE;
   134   if (mPreLevel > 0 || mDoRaw) {
   135     AppendToStringConvertLF(data, aStr);
   136   }
   137   else if (mDoFormat) {
   138     AppendToStringFormatedWrapped(data, aStr);
   139   }
   140   else if (mDoWrap) {
   141     AppendToStringWrapped(data, aStr);
   142   }
   143   else {
   144     int32_t lastNewlineOffset = kNotFound;
   145     if (HasLongLines(data, lastNewlineOffset)) {
   146       // We have long lines, rewrap
   147       mDoWrap = true;
   148       AppendToStringWrapped(data, aStr);
   149       mDoWrap = false;
   150     }
   151     else {
   152       AppendToStringConvertLF(data, aStr);
   153     }
   154   }
   156   return NS_OK;
   157 }
   159 nsresult
   160 nsXHTMLContentSerializer::EscapeURI(nsIContent* aContent, const nsAString& aURI, nsAString& aEscapedURI)
   161 {
   162   // URL escape %xx cannot be used in JS.
   163   // No escaping if the scheme is 'javascript'.
   164   if (IsJavaScript(aContent, nsGkAtoms::href, kNameSpaceID_None, aURI)) {
   165     aEscapedURI = aURI;
   166     return NS_OK;
   167   }
   169   // nsITextToSubURI does charset convert plus uri escape
   170   // This is needed to convert to a document charset which is needed to support existing browsers.
   171   // But we eventually want to use UTF-8 instead of a document charset, then the code would be much simpler.
   172   // See HTML 4.01 spec, "Appendix B.2.1 Non-ASCII characters in URI attribute values"
   173   nsCOMPtr<nsITextToSubURI> textToSubURI;
   174   nsAutoString uri(aURI); // in order to use FindCharInSet()
   175   nsresult rv = NS_OK;
   177   if (!mCharset.IsEmpty() && !IsASCII(uri)) {
   178     textToSubURI = do_GetService(NS_ITEXTTOSUBURI_CONTRACTID, &rv);
   179     NS_ENSURE_SUCCESS(rv, rv);
   180   }
   182   int32_t start = 0;
   183   int32_t end;
   184   nsAutoString part;
   185   nsXPIDLCString escapedURI;
   186   aEscapedURI.Truncate(0);
   188   // Loop and escape parts by avoiding escaping reserved characters
   189   // (and '%', '#', as well as '[' and ']' for IPv6 address literals).
   190   while ((end = uri.FindCharInSet("%#;/?:@&=+$,[]", start)) != -1) {
   191     part = Substring(aURI, start, (end-start));
   192     if (textToSubURI && !IsASCII(part)) {
   193       rv = textToSubURI->ConvertAndEscape(mCharset.get(), part.get(), getter_Copies(escapedURI));
   194       NS_ENSURE_SUCCESS(rv, rv);
   195     }
   196     else {
   197       escapedURI.Adopt(nsEscape(NS_ConvertUTF16toUTF8(part).get(), url_Path));
   198     }
   199     AppendASCIItoUTF16(escapedURI, aEscapedURI);
   201     // Append a reserved character without escaping.
   202     part = Substring(aURI, end, 1);
   203     aEscapedURI.Append(part);
   204     start = end + 1;
   205   }
   207   if (start < (int32_t) aURI.Length()) {
   208     // Escape the remaining part.
   209     part = Substring(aURI, start, aURI.Length()-start);
   210     if (textToSubURI) {
   211       rv = textToSubURI->ConvertAndEscape(mCharset.get(), part.get(), getter_Copies(escapedURI));
   212       NS_ENSURE_SUCCESS(rv, rv);
   213     }
   214     else {
   215       escapedURI.Adopt(nsEscape(NS_ConvertUTF16toUTF8(part).get(), url_Path));
   216     }
   217     AppendASCIItoUTF16(escapedURI, aEscapedURI);
   218   }
   220   return rv;
   221 }
   223 void
   224 nsXHTMLContentSerializer::SerializeAttributes(nsIContent* aContent,
   225                                               nsIContent *aOriginalElement,
   226                                               nsAString& aTagPrefix,
   227                                               const nsAString& aTagNamespaceURI,
   228                                               nsIAtom* aTagName,
   229                                               nsAString& aStr,
   230                                               uint32_t aSkipAttr,
   231                                               bool aAddNSAttr)
   232 {
   233   nsresult rv;
   234   uint32_t index, count;
   235   nsAutoString prefixStr, uriStr, valueStr;
   236   nsAutoString xmlnsStr;
   237   xmlnsStr.AssignLiteral(kXMLNS);
   239   int32_t contentNamespaceID = aContent->GetNameSpaceID();
   241   // this method is not called by nsHTMLContentSerializer
   242   // so we don't have to check HTML element, just XHTML
   244   if (mIsCopying && kNameSpaceID_XHTML == contentNamespaceID) {
   246     // Need to keep track of OL and LI elements in order to get ordinal number 
   247     // for the LI.
   248     if (aTagName == nsGkAtoms::ol) {
   249       // We are copying and current node is an OL;
   250       // Store its start attribute value in olState->startVal.
   251       nsAutoString start;
   252       int32_t startAttrVal = 0;
   253       aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::start, start);
   254       if (!start.IsEmpty()) {
   255         nsresult rv = NS_OK;
   256         startAttrVal = start.ToInteger(&rv);
   257         //If OL has "start" attribute, first LI element has to start with that value
   258         //Therefore subtracting 1 as all the LI elements are incrementing it before using it;
   259         //In failure of ToInteger(), default StartAttrValue to 0.
   260         if (NS_SUCCEEDED(rv))
   261           --startAttrVal;
   262         else
   263           startAttrVal = 0;
   264       }
   265       olState state (startAttrVal, true);
   266       mOLStateStack.AppendElement(state);
   267     }
   268     else if (aTagName == nsGkAtoms::li) {
   269       mIsFirstChildOfOL = IsFirstChildOfOL(aOriginalElement);
   270       if (mIsFirstChildOfOL) {
   271         // If OL is parent of this LI, serialize attributes in different manner.
   272         SerializeLIValueAttribute(aContent, aStr);
   273       }
   274     }
   275   }
   277   // If we had to add a new namespace declaration, serialize
   278   // and push it on the namespace stack
   279   if (aAddNSAttr) {
   280     if (aTagPrefix.IsEmpty()) {
   281       // Serialize default namespace decl
   282       SerializeAttr(EmptyString(), xmlnsStr, aTagNamespaceURI, aStr, true);
   283     } else {
   284       // Serialize namespace decl
   285       SerializeAttr(xmlnsStr, aTagPrefix, aTagNamespaceURI, aStr, true);
   286     }
   287     PushNameSpaceDecl(aTagPrefix, aTagNamespaceURI, aOriginalElement);
   288   }
   290   NS_NAMED_LITERAL_STRING(_mozStr, "_moz");
   292   count = aContent->GetAttrCount();
   294   // Now serialize each of the attributes
   295   // XXX Unfortunately we need a namespace manager to get
   296   // attribute URIs.
   297   for (index = 0; index < count; index++) {
   299     if (aSkipAttr == index) {
   300         continue;
   301     }
   303     const nsAttrName* name = aContent->GetAttrNameAt(index);
   304     int32_t namespaceID = name->NamespaceID();
   305     nsIAtom* attrName = name->LocalName();
   306     nsIAtom* attrPrefix = name->GetPrefix();
   308     // Filter out any attribute starting with [-|_]moz
   309     nsDependentAtomString attrNameStr(attrName);
   310     if (StringBeginsWith(attrNameStr, NS_LITERAL_STRING("_moz")) ||
   311         StringBeginsWith(attrNameStr, NS_LITERAL_STRING("-moz"))) {
   312       continue;
   313     }
   315     if (attrPrefix) {
   316       attrPrefix->ToString(prefixStr);
   317     }
   318     else {
   319       prefixStr.Truncate();
   320     }
   322     bool addNSAttr = false;
   323     if (kNameSpaceID_XMLNS != namespaceID) {
   324       nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr);
   325       addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, true);
   326     }
   328     aContent->GetAttr(namespaceID, attrName, valueStr);
   330     nsDependentAtomString nameStr(attrName);
   331     bool isJS = false;
   333     if (kNameSpaceID_XHTML == contentNamespaceID) {
   334       //
   335       // Filter out special case of <br type="_moz"> or <br _moz*>,
   336       // used by the editor.  Bug 16988.  Yuck.
   337       //
   338       if (namespaceID == kNameSpaceID_None && aTagName == nsGkAtoms::br && attrName == nsGkAtoms::type
   339           && StringBeginsWith(valueStr, _mozStr)) {
   340         continue;
   341       }
   343       if (mIsCopying && mIsFirstChildOfOL && (aTagName == nsGkAtoms::li)
   344           && (attrName == nsGkAtoms::value)) {
   345         // This is handled separately in SerializeLIValueAttribute()
   346         continue;
   347       }
   349       isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
   351       if (namespaceID == kNameSpaceID_None && 
   352           ((attrName == nsGkAtoms::href) ||
   353           (attrName == nsGkAtoms::src))) {
   354         // Make all links absolute when converting only the selection:
   355         if (mFlags & nsIDocumentEncoder::OutputAbsoluteLinks) {
   356           // Would be nice to handle OBJECT and APPLET tags,
   357           // but that gets more complicated since we have to
   358           // search the tag list for CODEBASE as well.
   359           // For now, just leave them relative.
   360           nsCOMPtr<nsIURI> uri = aContent->GetBaseURI();
   361           if (uri) {
   362             nsAutoString absURI;
   363             rv = NS_MakeAbsoluteURI(absURI, valueStr, uri);
   364             if (NS_SUCCEEDED(rv)) {
   365               valueStr = absURI;
   366             }
   367           }
   368         }
   369         // Need to escape URI.
   370         nsAutoString tempURI(valueStr);
   371         if (!isJS && NS_FAILED(EscapeURI(aContent, tempURI, valueStr)))
   372           valueStr = tempURI;
   373       }
   375       if (mRewriteEncodingDeclaration && aTagName == nsGkAtoms::meta &&
   376           attrName == nsGkAtoms::content) {
   377         // If we're serializing a <meta http-equiv="content-type">,
   378         // use the proper value, rather than what's in the document.
   379         nsAutoString header;
   380         aContent->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
   381         if (header.LowerCaseEqualsLiteral("content-type")) {
   382           valueStr = NS_LITERAL_STRING("text/html; charset=") +
   383             NS_ConvertASCIItoUTF16(mCharset);
   384         }
   385       }
   387       // Expand shorthand attribute.
   388       if (namespaceID == kNameSpaceID_None && IsShorthandAttr(attrName, aTagName) && valueStr.IsEmpty()) {
   389         valueStr = nameStr;
   390       }
   391     }
   392     else {
   393       isJS = IsJavaScript(aContent, attrName, namespaceID, valueStr);
   394     }
   396     SerializeAttr(prefixStr, nameStr, valueStr, aStr, !isJS);
   398     if (addNSAttr) {
   399       NS_ASSERTION(!prefixStr.IsEmpty(),
   400                    "Namespaced attributes must have a prefix");
   401       SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, true);
   402       PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement);
   403     }
   404   }
   405 }
   408 void 
   409 nsXHTMLContentSerializer::AppendEndOfElementStart(nsIContent *aOriginalElement,
   410                                                   nsIAtom * aName,
   411                                                   int32_t aNamespaceID,
   412                                                   nsAString& aStr)
   413 {
   414   // this method is not called by nsHTMLContentSerializer
   415   // so we don't have to check HTML element, just XHTML
   416   NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
   418   if (kNameSpaceID_XHTML != aNamespaceID) {
   419     nsXMLContentSerializer::AppendEndOfElementStart(aOriginalElement, aName,
   420                                                     aNamespaceID, aStr);
   421     return;
   422   }
   424   nsIContent* content = aOriginalElement;
   426   // for non empty elements, even if they are not a container, we always
   427   // serialize their content, because the XHTML element could contain non XHTML
   428   // nodes useful in some context, like in an XSLT stylesheet
   429   if (HasNoChildren(content)) {
   431     nsIParserService* parserService = nsContentUtils::GetParserService();
   433     if (parserService) {
   434       bool isContainer;
   435       parserService->
   436         IsContainer(parserService->HTMLCaseSensitiveAtomTagToId(aName),
   437                     isContainer);
   438       if (!isContainer) {
   439         // for backward compatibility with HTML 4 user agents
   440         // only non-container HTML elements can be closed immediatly,
   441         // and a space is added before />
   442         AppendToString(NS_LITERAL_STRING(" />"), aStr);
   443         return;
   444       }
   445     }
   446   }
   447   AppendToString(kGreaterThan, aStr);
   448 }
   450 void
   451 nsXHTMLContentSerializer::AfterElementStart(nsIContent * aContent,
   452                                             nsIContent *aOriginalElement,
   453                                             nsAString& aStr)
   454 {
   455   nsIAtom *name = aContent->Tag();
   456   if (aContent->GetNameSpaceID() == kNameSpaceID_XHTML &&
   457       mRewriteEncodingDeclaration &&
   458       name == nsGkAtoms::head) {
   460     // Check if there already are any content-type meta children.
   461     // If there are, they will be modified to use the correct charset.
   462     // If there aren't, we'll insert one here.
   463     bool hasMeta = false;
   464     for (nsIContent* child = aContent->GetFirstChild();
   465          child;
   466          child = child->GetNextSibling()) {
   467       if (child->IsHTML(nsGkAtoms::meta) &&
   468           child->HasAttr(kNameSpaceID_None, nsGkAtoms::content)) {
   469         nsAutoString header;
   470         child->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);
   472         if (header.LowerCaseEqualsLiteral("content-type")) {
   473           hasMeta = true;
   474           break;
   475         }
   476       }
   477     }
   479     if (!hasMeta) {
   480       AppendNewLineToString(aStr);
   481       if (mDoFormat) {
   482         AppendIndentation(aStr);
   483       }
   484       AppendToString(NS_LITERAL_STRING("<meta http-equiv=\"content-type\""),
   485                     aStr);
   486       AppendToString(NS_LITERAL_STRING(" content=\"text/html; charset="), aStr);
   487       AppendToString(NS_ConvertASCIItoUTF16(mCharset), aStr);
   488       if (mIsHTMLSerializer)
   489         AppendToString(NS_LITERAL_STRING("\">"), aStr);
   490       else
   491         AppendToString(NS_LITERAL_STRING("\" />"), aStr);
   492     }
   493   }
   494 }
   496 void
   497 nsXHTMLContentSerializer::AfterElementEnd(nsIContent * aContent,
   498                                           nsAString& aStr)
   499 {
   500   NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
   502   int32_t namespaceID = aContent->GetNameSpaceID();
   503   nsIAtom *name = aContent->Tag();
   505   // this method is not called by nsHTMLContentSerializer
   506   // so we don't have to check HTML element, just XHTML
   507   if (kNameSpaceID_XHTML == namespaceID && name == nsGkAtoms::body) {
   508     --mInBody;
   509   }
   510 }
   513 NS_IMETHODIMP
   514 nsXHTMLContentSerializer::AppendDocumentStart(nsIDocument *aDocument,
   515                                               nsAString& aStr)
   516 {
   517   if (!mBodyOnly)
   518     return nsXMLContentSerializer::AppendDocumentStart(aDocument, aStr);
   520   return NS_OK;
   521 }
   523 bool
   524 nsXHTMLContentSerializer::CheckElementStart(nsIContent * aContent,
   525                                             bool & aForceFormat,
   526                                             nsAString& aStr)
   527 {
   528   // The _moz_dirty attribute is emitted by the editor to
   529   // indicate that this element should be pretty printed
   530   // even if we're not in pretty printing mode
   531   aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
   532                  aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
   534   nsIAtom *name = aContent->Tag();
   535   int32_t namespaceID = aContent->GetNameSpaceID();
   537   if (namespaceID == kNameSpaceID_XHTML) {
   538     if (name == nsGkAtoms::br && mPreLevel > 0 && 
   539         (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre)) {
   540       AppendNewLineToString(aStr);
   541       return false;
   542     }
   544     if (name == nsGkAtoms::body) {
   545       ++mInBody;
   546     }
   547   }
   548   return true;
   549 }
   551 bool
   552 nsXHTMLContentSerializer::CheckElementEnd(nsIContent * aContent,
   553                                           bool & aForceFormat,
   554                                           nsAString& aStr)
   555 {
   556   NS_ASSERTION(!mIsHTMLSerializer, "nsHTMLContentSerializer shouldn't call this method !");
   558   aForceFormat = !(mFlags & nsIDocumentEncoder::OutputIgnoreMozDirty) &&
   559                  aContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozdirty);
   561   nsIAtom *name = aContent->Tag();
   562   int32_t namespaceID = aContent->GetNameSpaceID();
   564   // this method is not called by nsHTMLContentSerializer
   565   // so we don't have to check HTML element, just XHTML
   566   if (namespaceID == kNameSpaceID_XHTML) {
   567     if (mIsCopying && name == nsGkAtoms::ol) {
   568       NS_ASSERTION((!mOLStateStack.IsEmpty()), "Cannot have an empty OL Stack");
   569       /* Though at this point we must always have an state to be deleted as all 
   570       the OL opening tags are supposed to push an olState object to the stack*/
   571       if (!mOLStateStack.IsEmpty()) {
   572         mOLStateStack.RemoveElementAt(mOLStateStack.Length() -1);
   573       }
   574     }
   576     if (HasNoChildren(aContent)) {
   577       nsIParserService* parserService = nsContentUtils::GetParserService();
   579       if (parserService) {
   580         bool isContainer;
   582         parserService->
   583           IsContainer(parserService->HTMLCaseSensitiveAtomTagToId(name),
   584                       isContainer);
   585         if (!isContainer) {
   586           // non-container HTML elements are already closed,
   587           // see AppendEndOfElementStart
   588           return false;
   589         }
   590       }
   591     }
   592     // for backward compatibility with old HTML user agents,
   593     // empty elements should have an ending tag, so we mustn't call
   594     // nsXMLContentSerializer::CheckElementEnd
   595     return true;
   596   }
   598   bool dummyFormat;
   599   return nsXMLContentSerializer::CheckElementEnd(aContent, dummyFormat, aStr);
   600 }
   602 void
   603 nsXHTMLContentSerializer::AppendAndTranslateEntities(const nsAString& aStr,
   604                                                      nsAString& aOutputStr)
   605 {
   606   if (mBodyOnly && !mInBody) {
   607     return;
   608   }
   610   if (mDisableEntityEncoding) {
   611     aOutputStr.Append(aStr);
   612     return;
   613   }
   615   nsXMLContentSerializer::AppendAndTranslateEntities(aStr, aOutputStr);
   616 }
   618 bool
   619 nsXHTMLContentSerializer::IsShorthandAttr(const nsIAtom* aAttrName,
   620                                           const nsIAtom* aElementName)
   621 {
   622   // checked
   623   if ((aAttrName == nsGkAtoms::checked) &&
   624       (aElementName == nsGkAtoms::input)) {
   625     return true;
   626   }
   628   // compact
   629   if ((aAttrName == nsGkAtoms::compact) &&
   630       (aElementName == nsGkAtoms::dir || 
   631        aElementName == nsGkAtoms::dl ||
   632        aElementName == nsGkAtoms::menu ||
   633        aElementName == nsGkAtoms::ol ||
   634        aElementName == nsGkAtoms::ul)) {
   635     return true;
   636   }
   638   // declare
   639   if ((aAttrName == nsGkAtoms::declare) &&
   640       (aElementName == nsGkAtoms::object)) {
   641     return true;
   642   }
   644   // defer
   645   if ((aAttrName == nsGkAtoms::defer) &&
   646       (aElementName == nsGkAtoms::script)) {
   647     return true;
   648   }
   650   // disabled
   651   if ((aAttrName == nsGkAtoms::disabled) &&
   652       (aElementName == nsGkAtoms::button ||
   653        aElementName == nsGkAtoms::input ||
   654        aElementName == nsGkAtoms::optgroup ||
   655        aElementName == nsGkAtoms::option ||
   656        aElementName == nsGkAtoms::select ||
   657        aElementName == nsGkAtoms::textarea)) {
   658     return true;
   659   }
   661   // ismap
   662   if ((aAttrName == nsGkAtoms::ismap) &&
   663       (aElementName == nsGkAtoms::img ||
   664        aElementName == nsGkAtoms::input)) {
   665     return true;
   666   }
   668   // multiple
   669   if ((aAttrName == nsGkAtoms::multiple) &&
   670       (aElementName == nsGkAtoms::select)) {
   671     return true;
   672   }
   674   // noresize
   675   if ((aAttrName == nsGkAtoms::noresize) &&
   676       (aElementName == nsGkAtoms::frame)) {
   677     return true;
   678   }
   680   // noshade
   681   if ((aAttrName == nsGkAtoms::noshade) &&
   682       (aElementName == nsGkAtoms::hr)) {
   683     return true;
   684   }
   686   // nowrap
   687   if ((aAttrName == nsGkAtoms::nowrap) &&
   688       (aElementName == nsGkAtoms::td ||
   689        aElementName == nsGkAtoms::th)) {
   690     return true;
   691   }
   693   // readonly
   694   if ((aAttrName == nsGkAtoms::readonly) &&
   695       (aElementName == nsGkAtoms::input ||
   696        aElementName == nsGkAtoms::textarea)) {
   697     return true;
   698   }
   700   // selected
   701   if ((aAttrName == nsGkAtoms::selected) &&
   702       (aElementName == nsGkAtoms::option)) {
   703     return true;
   704   }
   706   // autoplay and controls
   707   if ((aElementName == nsGkAtoms::video || aElementName == nsGkAtoms::audio) &&
   708     (aAttrName == nsGkAtoms::autoplay || aAttrName == nsGkAtoms::muted ||
   709      aAttrName == nsGkAtoms::controls)) {
   710     return true;
   711   }
   713   return false;
   714 }
   716 bool
   717 nsXHTMLContentSerializer::LineBreakBeforeOpen(int32_t aNamespaceID, nsIAtom* aName)
   718 {
   720   if (aNamespaceID != kNameSpaceID_XHTML) {
   721     return mAddSpace;
   722   }
   724   if (aName == nsGkAtoms::title ||
   725       aName == nsGkAtoms::meta  ||
   726       aName == nsGkAtoms::link  ||
   727       aName == nsGkAtoms::style ||
   728       aName == nsGkAtoms::select ||
   729       aName == nsGkAtoms::option ||
   730       aName == nsGkAtoms::script ||
   731       aName == nsGkAtoms::html) {
   732     return true;
   733   }
   734   else {
   735     nsIParserService* parserService = nsContentUtils::GetParserService();
   737     if (parserService) {
   738       bool res;
   739       parserService->
   740         IsBlock(parserService->HTMLCaseSensitiveAtomTagToId(aName), res);
   741       return res;
   742     }
   743   }
   745   return mAddSpace;
   746 }
   748 bool 
   749 nsXHTMLContentSerializer::LineBreakAfterOpen(int32_t aNamespaceID, nsIAtom* aName)
   750 {
   752   if (aNamespaceID != kNameSpaceID_XHTML) {
   753     return false;
   754   }
   756   if ((aName == nsGkAtoms::html) ||
   757       (aName == nsGkAtoms::head) ||
   758       (aName == nsGkAtoms::body) ||
   759       (aName == nsGkAtoms::ul) ||
   760       (aName == nsGkAtoms::ol) ||
   761       (aName == nsGkAtoms::dl) ||
   762       (aName == nsGkAtoms::table) ||
   763       (aName == nsGkAtoms::tbody) ||
   764       (aName == nsGkAtoms::tr) ||
   765       (aName == nsGkAtoms::br) ||
   766       (aName == nsGkAtoms::meta) ||
   767       (aName == nsGkAtoms::link) ||
   768       (aName == nsGkAtoms::script) ||
   769       (aName == nsGkAtoms::select) ||
   770       (aName == nsGkAtoms::map) ||
   771       (aName == nsGkAtoms::area) ||
   772       (aName == nsGkAtoms::style)) {
   773     return true;
   774   }
   776   return false;
   777 }
   779 bool 
   780 nsXHTMLContentSerializer::LineBreakBeforeClose(int32_t aNamespaceID, nsIAtom* aName)
   781 {
   783   if (aNamespaceID != kNameSpaceID_XHTML) {
   784     return false;
   785   }
   787   if ((aName == nsGkAtoms::html) ||
   788       (aName == nsGkAtoms::head) ||
   789       (aName == nsGkAtoms::body) ||
   790       (aName == nsGkAtoms::ul) ||
   791       (aName == nsGkAtoms::ol) ||
   792       (aName == nsGkAtoms::dl) ||
   793       (aName == nsGkAtoms::select) ||
   794       (aName == nsGkAtoms::table) ||
   795       (aName == nsGkAtoms::tbody)) {
   796     return true;
   797   }
   798   return false;
   799 }
   801 bool 
   802 nsXHTMLContentSerializer::LineBreakAfterClose(int32_t aNamespaceID, nsIAtom* aName)
   803 {
   805   if (aNamespaceID != kNameSpaceID_XHTML) {
   806     return false;
   807   }
   809   if ((aName == nsGkAtoms::html) ||
   810       (aName == nsGkAtoms::head) ||
   811       (aName == nsGkAtoms::body) ||
   812       (aName == nsGkAtoms::tr) ||
   813       (aName == nsGkAtoms::th) ||
   814       (aName == nsGkAtoms::td) ||
   815       (aName == nsGkAtoms::pre) ||
   816       (aName == nsGkAtoms::title) ||
   817       (aName == nsGkAtoms::li) ||
   818       (aName == nsGkAtoms::dt) ||
   819       (aName == nsGkAtoms::dd) ||
   820       (aName == nsGkAtoms::blockquote) ||
   821       (aName == nsGkAtoms::select) ||
   822       (aName == nsGkAtoms::option) ||
   823       (aName == nsGkAtoms::p) ||
   824       (aName == nsGkAtoms::map) ||
   825       (aName == nsGkAtoms::div)) {
   826     return true;
   827   }
   828   else {
   829     nsIParserService* parserService = nsContentUtils::GetParserService();
   831     if (parserService) {
   832       bool res;
   833       parserService->
   834         IsBlock(parserService->HTMLCaseSensitiveAtomTagToId(aName), res);
   835       return res;
   836     }
   837   }
   839   return false;
   840 }
   843 void
   844 nsXHTMLContentSerializer::MaybeEnterInPreContent(nsIContent* aNode)
   845 {
   847   if (aNode->GetNameSpaceID() != kNameSpaceID_XHTML) {
   848     return;
   849   }
   851   nsIAtom *name = aNode->Tag();
   853   if (name == nsGkAtoms::pre ||
   854       name == nsGkAtoms::script ||
   855       name == nsGkAtoms::style ||
   856       name == nsGkAtoms::noscript ||
   857       name == nsGkAtoms::noframes
   858       ) {
   859     mPreLevel++;
   860   }
   861 }
   863 void
   864 nsXHTMLContentSerializer::MaybeLeaveFromPreContent(nsIContent* aNode)
   865 {
   866   if (aNode->GetNameSpaceID() != kNameSpaceID_XHTML) {
   867     return;
   868   }
   870   nsIAtom *name = aNode->Tag();
   871   if (name == nsGkAtoms::pre ||
   872       name == nsGkAtoms::script ||
   873       name == nsGkAtoms::style ||
   874       name == nsGkAtoms::noscript ||
   875       name == nsGkAtoms::noframes
   876     ) {
   877     --mPreLevel;
   878   }
   879 }
   881 void 
   882 nsXHTMLContentSerializer::SerializeLIValueAttribute(nsIContent* aElement,
   883                                                     nsAString& aStr)
   884 {
   885   // We are copying and we are at the "first" LI node of OL in selected range.
   886   // It may not be the first LI child of OL but it's first in the selected range.
   887   // Note that we get into this condition only once per a OL.
   888   bool found = false;
   889   nsCOMPtr<nsIDOMNode> currNode = do_QueryInterface(aElement);
   890   nsAutoString valueStr;
   892   olState state (0, false);
   894   if (!mOLStateStack.IsEmpty()) {
   895     state = mOLStateStack[mOLStateStack.Length()-1];
   896     // isFirstListItem should be true only before the serialization of the
   897     // first item in the list.
   898     state.isFirstListItem = false;
   899     mOLStateStack[mOLStateStack.Length()-1] = state;
   900   }
   902   int32_t startVal = state.startVal;
   903   int32_t offset = 0;
   905   // Traverse previous siblings until we find one with "value" attribute.
   906   // offset keeps track of how many previous siblings we had tocurrNode traverse.
   907   while (currNode && !found) {
   908     nsCOMPtr<nsIDOMElement> currElement = do_QueryInterface(currNode);
   909     // currElement may be null if it were a text node.
   910     if (currElement) {
   911       nsAutoString tagName;
   912       currElement->GetTagName(tagName);
   913       if (tagName.LowerCaseEqualsLiteral("li")) {
   914         currElement->GetAttribute(NS_LITERAL_STRING("value"), valueStr);
   915         if (valueStr.IsEmpty())
   916           offset++;
   917         else {
   918           found = true;
   919           nsresult rv = NS_OK;
   920           startVal = valueStr.ToInteger(&rv);
   921         }
   922       }
   923     }
   924     nsCOMPtr<nsIDOMNode> tmp;
   925     currNode->GetPreviousSibling(getter_AddRefs(tmp));
   926     currNode.swap(tmp);
   927   }
   928   // If LI was not having "value", Set the "value" attribute for it.
   929   // Note that We are at the first LI in the selected range of OL.
   930   if (offset == 0 && found) {
   931     // offset = 0 => LI itself has the value attribute and we did not need to traverse back.
   932     // Just serialize value attribute like other tags.
   933     SerializeAttr(EmptyString(), NS_LITERAL_STRING("value"), valueStr, aStr, false);
   934   }
   935   else if (offset == 1 && !found) {
   936     /*(offset = 1 && !found) means either LI is the first child node of OL
   937     and LI is not having "value" attribute.
   938     In that case we would not like to set "value" attribute to reduce the changes.
   939     */
   940     //do nothing...
   941   }
   942   else if (offset > 0) {
   943     // Set value attribute.
   944     nsAutoString valueStr;
   946     //As serializer needs to use this valueAttr we are creating here, 
   947     valueStr.AppendInt(startVal + offset);
   948     SerializeAttr(EmptyString(), NS_LITERAL_STRING("value"), valueStr, aStr, false);
   949   }
   950 }
   952 bool
   953 nsXHTMLContentSerializer::IsFirstChildOfOL(nsIContent* aElement)
   954 {
   955   nsCOMPtr<nsIDOMNode> node = do_QueryInterface(aElement);
   956   nsAutoString parentName;
   958   nsCOMPtr<nsIDOMNode> parentNode;
   959   node->GetParentNode(getter_AddRefs(parentNode));
   960   if (parentNode)
   961     parentNode->GetNodeName(parentName);
   962   else
   963     return false;
   965   if (parentName.LowerCaseEqualsLiteral("ol")) {
   967     if (!mOLStateStack.IsEmpty()) {
   968       olState state = mOLStateStack[mOLStateStack.Length()-1];
   969       if (state.isFirstListItem)
   970         return true;
   971     }
   973     return false;
   974   }
   975   else
   976     return false;
   977 }
   979 bool
   980 nsXHTMLContentSerializer::HasNoChildren(nsIContent * aContent) {
   982   for (nsIContent* child = aContent->GetFirstChild();
   983        child;
   984        child = child->GetNextSibling()) {
   986     if (!child->IsNodeOfType(nsINode::eTEXT))
   987       return false;
   989     if (child->TextLength())
   990       return false;
   991   }
   993   return true;
   994 }

mercurial