NS_IMETHODIMP 
nsXMLContentSerializer::AppendComment(nsIDOMComment* aComment,
                                      PRInt32 aStartOffset,
                                      PRInt32 aEndOffset,
                                      nsAString& aStr)
{
  NS_ENSURE_ARG(aComment);
  nsresult rv;
  nsAutoString data;

  rv = aComment->GetData(data);
  if (NS_FAILED(rv)) return NS_ERROR_FAILURE;

  MaybeAddNewline(aStr);

  AppendToString(NS_LITERAL_STRING("<!--"), aStr);
  if (aStartOffset || (aEndOffset != -1)) {
    PRInt32 length = (aEndOffset == -1) ? data.Length() : aEndOffset;
    length -= aStartOffset;

    nsAutoString frag;
    data.Mid(frag, aStartOffset, length);
    AppendToString(frag, aStr);
  }
  else {
    AppendToString(data, aStr);
  }
  AppendToString(NS_LITERAL_STRING("-->"), aStr);
  MaybeFlagNewline(aComment);
  
  return NS_OK;
}
NS_IMETHODIMP 
nsXMLContentSerializer::AppendProcessingInstruction(nsIDOMProcessingInstruction* aPI,
                                                    PRInt32 aStartOffset,
                                                    PRInt32 aEndOffset,
                                                    nsAString& aStr)
{
  NS_ENSURE_ARG(aPI);
  nsresult rv;
  nsAutoString target, data;

  MaybeAddNewline(aStr);

  rv = aPI->GetTarget(target);
  if (NS_FAILED(rv)) return NS_ERROR_FAILURE;

  rv = aPI->GetData(data);
  if (NS_FAILED(rv)) return NS_ERROR_FAILURE;

  AppendToString(NS_LITERAL_STRING("<?"), aStr);
  AppendToString(target, aStr);
  if (!data.IsEmpty()) {
    AppendToString(NS_LITERAL_STRING(" "), aStr);
    AppendToString(data, aStr);
  }
  AppendToString(NS_LITERAL_STRING("?>"), aStr);
  MaybeFlagNewline(aPI);
  
  return NS_OK;
}
NS_IMETHODIMP 
nsXMLContentSerializer::AppendElementStart(nsIDOMElement *aElement,
                                           nsIDOMElement *aOriginalElement,
                                           nsAString& aStr)
{
  NS_ENSURE_ARG(aElement);

  nsAutoString tagPrefix, tagLocalName, tagNamespaceURI;
  nsAutoString xmlnsStr;
  xmlnsStr.AssignLiteral(kXMLNS);

  nsCOMPtr<nsIContent> content(do_QueryInterface(aElement));
  if (!content) return NS_ERROR_FAILURE;

  aElement->GetPrefix(tagPrefix);
  aElement->GetLocalName(tagLocalName);
  aElement->GetNamespaceURI(tagNamespaceURI);

  PRUint32 index, count;
  nsAutoString nameStr, prefixStr, uriStr, valueStr;

  count = content->GetAttrCount();

  // First scan for namespace declarations, pushing each on the stack
  PRUint32 skipAttr = count;
  for (index = 0; index < count; index++) {
    
    const nsAttrName* name = content->GetAttrNameAt(index);
    PRInt32 namespaceID = name->NamespaceID();
    nsIAtom *attrName = name->LocalName();
    
    if (namespaceID == kNameSpaceID_XMLNS ||
        // Also push on the stack attrs named "xmlns" in the null
        // namespace... because once we serialize those out they'll look like
        // namespace decls.  :(
        // XXXbz what if we have both "xmlns" in the null namespace and "xmlns"
        // in the xmlns namespace?
        (namespaceID == kNameSpaceID_None &&
         attrName == nsGkAtoms::xmlns)) {
      content->GetAttr(namespaceID, attrName, uriStr);

      if (!name->GetPrefix()) {
        if (tagNamespaceURI.IsEmpty() && !uriStr.IsEmpty()) {
          // If the element is in no namespace we need to add a xmlns
          // attribute to declare that. That xmlns attribute must not have a
          // prefix (see http://www.w3.org/TR/REC-xml-names/#dt-prefix), ie it
          // must declare the default namespace. We just found an xmlns
          // attribute that declares the default namespace to something
          // non-empty. We're going to ignore this attribute, for children we
          // will detect that we need to add it again and attributes aren't
          // affected by the default namespace.
          skipAttr = index;
        }
        else {
          // Default NS attribute does not have prefix (and the name is "xmlns")
          PushNameSpaceDecl(EmptyString(), uriStr, aOriginalElement);
        }
      }
      else {
        attrName->ToString(nameStr);
        PushNameSpaceDecl(nameStr, uriStr, aOriginalElement);
      }
    }
  }

  PRBool addNSAttr;
    
  MaybeAddNewline(aStr);

  addNSAttr = ConfirmPrefix(tagPrefix, tagNamespaceURI, aOriginalElement,
                            PR_FALSE);
  // Serialize the qualified name of the element
  AppendToString(NS_LITERAL_STRING("<"), aStr);
  if (!tagPrefix.IsEmpty()) {
    AppendToString(tagPrefix, aStr);
    AppendToString(NS_LITERAL_STRING(":"), aStr);
  }
  AppendToString(tagLocalName, aStr);
    
  // If we had to add a new namespace declaration, serialize
  // and push it on the namespace stack
  if (addNSAttr) {
    if (tagPrefix.IsEmpty()) {
      // Serialize default namespace decl
      SerializeAttr(EmptyString(), xmlnsStr, tagNamespaceURI, aStr, PR_TRUE);
    } else {
      // Serialize namespace decl
      SerializeAttr(xmlnsStr, tagPrefix, tagNamespaceURI, aStr, PR_TRUE);
    }
    PushNameSpaceDecl(tagPrefix, tagNamespaceURI, aOriginalElement);
  }

  // Now serialize each of the attributes
  // XXX Unfortunately we need a namespace manager to get
  // attribute URIs.
  for (index = 0; index < count; index++) {
    if (skipAttr == index) {
        continue;
    }

    const nsAttrName* name = content->GetAttrNameAt(index);
    PRInt32 namespaceID = name->NamespaceID();
    nsIAtom* attrName = name->LocalName();
    nsIAtom* attrPrefix = name->GetPrefix();

    if (attrPrefix) {
      attrPrefix->ToString(prefixStr);
    }
    else {
      prefixStr.Truncate();
    }

    addNSAttr = PR_FALSE;
    if (kNameSpaceID_XMLNS != namespaceID) {
      nsContentUtils::NameSpaceManager()->GetNameSpaceURI(namespaceID, uriStr);
      addNSAttr = ConfirmPrefix(prefixStr, uriStr, aOriginalElement, PR_TRUE);
    }
    
    content->GetAttr(namespaceID, attrName, valueStr);
    attrName->ToString(nameStr);

    // XXX Hack to get around the fact that MathML can add
    //     attributes starting with '-', which makes them
    //     invalid XML.
    if (!nameStr.IsEmpty() && nameStr.First() == '-')
      continue;

    if (namespaceID == kNameSpaceID_None) {
      if (content->GetNameSpaceID() == kNameSpaceID_XHTML) {
        if (IsShorthandAttr(attrName, content->Tag()) &&
            valueStr.IsEmpty()) {
          valueStr = nameStr;
        }
      }
    }
    SerializeAttr(prefixStr, nameStr, valueStr, aStr, PR_TRUE);
    
    if (addNSAttr) {
      NS_ASSERTION(!prefixStr.IsEmpty(),
                   "Namespaced attributes must have a prefix");
      SerializeAttr(xmlnsStr, prefixStr, uriStr, aStr, PR_TRUE);
      PushNameSpaceDecl(prefixStr, uriStr, aOriginalElement);
    }
  }

  // We don't output a separate end tag for empty element
  PRBool hasChildren;
  if (NS_FAILED(aOriginalElement->HasChildNodes(&hasChildren)) ||
      !hasChildren) {
    AppendToString(NS_LITERAL_STRING("/>"), aStr);    
    MaybeFlagNewline(aElement);
  } else {
    AppendToString(NS_LITERAL_STRING(">"), aStr);    
  }
  
  return NS_OK;
}
NS_IMETHODIMP 
nsXMLContentSerializer::AppendDoctype(nsIDOMDocumentType *aDoctype,
                                      nsAString& aStr)
{
  NS_ENSURE_ARG(aDoctype);
  nsresult rv;
  nsAutoString name, publicId, systemId, internalSubset;

  rv = aDoctype->GetName(name);
  if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
  rv = aDoctype->GetPublicId(publicId);
  if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
  rv = aDoctype->GetSystemId(systemId);
  if (NS_FAILED(rv)) return NS_ERROR_FAILURE;
  rv = aDoctype->GetInternalSubset(internalSubset);
  if (NS_FAILED(rv)) return NS_ERROR_FAILURE;

  MaybeAddNewline(aStr);

  AppendToString(NS_LITERAL_STRING("<!DOCTYPE "), aStr);
  AppendToString(name, aStr);
  PRUnichar quote;
  if (!publicId.IsEmpty()) {
    AppendToString(NS_LITERAL_STRING(" PUBLIC "), aStr);
    if (publicId.FindChar(PRUnichar('"')) == -1) {
      quote = PRUnichar('"');
    }
    else {
      quote = PRUnichar('\'');
    }
    AppendToString(quote, aStr);
    AppendToString(publicId, aStr);
    AppendToString(quote, aStr);

    if (!systemId.IsEmpty()) {
      AppendToString(PRUnichar(' '), aStr);
      if (systemId.FindChar(PRUnichar('"')) == -1) {
        quote = PRUnichar('"');
      }
      else {
        quote = PRUnichar('\'');
      }
      AppendToString(quote, aStr);
      AppendToString(systemId, aStr);
      AppendToString(quote, aStr);
    }
  }
  else if (!systemId.IsEmpty()) {
    if (systemId.FindChar(PRUnichar('"')) == -1) {
      quote = PRUnichar('"');
    }
    else {
      quote = PRUnichar('\'');
    }
    AppendToString(NS_LITERAL_STRING(" SYSTEM "), aStr);
    AppendToString(quote, aStr);
    AppendToString(systemId, aStr);
    AppendToString(quote, aStr);
  }
  
  if (!internalSubset.IsEmpty()) {
    AppendToString(NS_LITERAL_STRING(" ["), aStr);
    AppendToString(internalSubset, aStr);
    AppendToString(PRUnichar(']'), aStr);
  }
    
  AppendToString(PRUnichar('>'), aStr);
  MaybeFlagNewline(aDoctype);

  return NS_OK;
}
NS_IMETHODIMP
nsHTMLContentSerializer::AppendElementStart(nsIDOMElement *aElement,
        nsIDOMElement *aOriginalElement,
        nsAString& aStr)
{
    NS_ENSURE_ARG(aElement);

    nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
    if (!content) return NS_ERROR_FAILURE;

    // The _moz_dirty attribute is emitted by the editor to
    // indicate that this element should be pretty printed
    // even if we're not in pretty printing mode
    PRBool hasDirtyAttr = content->HasAttr(kNameSpaceID_None,
                                           nsGkAtoms::mozdirty);

    nsIAtom *name = content->Tag();

    if (name == nsGkAtoms::br && mPreLevel > 0
            && (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre)) {
        AppendToString(mLineBreak, aStr);
        mMayIgnoreLineBreakSequence = PR_TRUE;
        mColPos = 0;
        return NS_OK;
    }

    if (name == nsGkAtoms::body) {
        mInBody = PR_TRUE;
    }

    if (LineBreakBeforeOpen(name, hasDirtyAttr)) {
        AppendToString(mLineBreak, aStr);
        mMayIgnoreLineBreakSequence = PR_TRUE;
        mColPos = 0;
        mAddSpace = PR_FALSE;
    }
    else if (mAddSpace) {
        AppendToString(PRUnichar(' '), aStr);
        mAddSpace = PR_FALSE;
    }
    else {
        MaybeAddNewline(aStr);
    }
    // Always reset to avoid false newlines in case MaybeAddNewline wasn't
    // called
    mAddNewline = PR_FALSE;

    StartIndentation(name, hasDirtyAttr, aStr);

    if (name == nsGkAtoms::pre ||
            name == nsGkAtoms::script ||
            name == nsGkAtoms::style) {
        mPreLevel++;
    }

    AppendToString(kLessThan, aStr);

    nsAutoString nameStr;
    name->ToString(nameStr);
    AppendToString(nameStr.get(), -1, aStr);

    // Need to keep track of OL and LI elements in order to get ordinal number
    // for the LI.
    if (mIsCopying && name == nsGkAtoms::ol) {
        // We are copying and current node is an OL;
        // Store it's start attribute value in olState->startVal.
        nsAutoString start;
        PRInt32 startAttrVal = 0;
        aElement->GetAttribute(NS_LITERAL_STRING("start"), start);
        if (!start.IsEmpty()) {
            PRInt32 rv = 0;
            startAttrVal = start.ToInteger(&rv);
            //If OL has "start" attribute, first LI element has to start with that value
            //Therefore subtracting 1 as all the LI elements are incrementing it before using it;
            //In failure of ToInteger(), default StartAttrValue to 0.
            if (NS_SUCCEEDED(rv))
                startAttrVal--;
            else
                startAttrVal = 0;
        }
        olState* state = new olState(startAttrVal, PR_TRUE);
        if (state)
            mOLStateStack.AppendElement(state);
    }

    if (mIsCopying && name == nsGkAtoms::li) {
        mIsFirstChildOfOL = IsFirstChildOfOL(aOriginalElement);
        if (mIsFirstChildOfOL) {
            // If OL is parent of this LI, serialize attributes in different manner.
            SerializeLIValueAttribute(aElement, aStr);
        }
    }

    // Even LI passed above have to go through this
    // for serializing attributes other than "value".
    SerializeAttributes(content, name, aStr);

    AppendToString(kGreaterThan, aStr);

    if (LineBreakAfterOpen(name, hasDirtyAttr)) {
        AppendToString(mLineBreak, aStr);
        mMayIgnoreLineBreakSequence = PR_TRUE;
        mColPos = 0;
    }

    if (name == nsGkAtoms::script ||
            name == nsGkAtoms::style ||
            name == nsGkAtoms::noscript ||
            name == nsGkAtoms::noframes) {
        mInCDATA = PR_TRUE;
    }

    if (mIsWholeDocument && name == nsGkAtoms::head) {
        // Check if there already are any content-type meta children.
        // If there are, they will be modified to use the correct charset.
        // If there aren't, we'll insert one here.
        PRBool hasMeta = PR_FALSE;
        PRUint32 i, childCount = content->GetChildCount();
        for (i = 0; i < childCount; ++i) {
            nsIContent* child = content->GetChildAt(i);
            if (child->IsNodeOfType(nsINode::eHTML) &&
                    child->Tag() == nsGkAtoms::meta &&
                    child->HasAttr(kNameSpaceID_None, nsGkAtoms::content)) {
                nsAutoString header;
                child->GetAttr(kNameSpaceID_None, nsGkAtoms::httpEquiv, header);

                if (header.LowerCaseEqualsLiteral("content-type")) {
                    hasMeta = PR_TRUE;
                    break;
                }
            }
        }

        if (!hasMeta) {
            AppendToString(mLineBreak, aStr);
            AppendToString(NS_LITERAL_STRING("<meta http-equiv=\"content-type\""),
                           aStr);
            AppendToString(NS_LITERAL_STRING(" content=\"text/html; charset="), aStr);
            AppendToString(NS_ConvertASCIItoUTF16(mCharset), aStr);
            AppendToString(NS_LITERAL_STRING("\">"), aStr);
        }
    }

    return NS_OK;
}
NS_IMETHODIMP
nsHTMLContentSerializer::AppendElementStart(nsIDOMElement *aElement,
                                            PRBool aHasChildren,
                                            nsAString& aStr)
{
  NS_ENSURE_ARG(aElement);
  
  nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
  if (!content) return NS_ERROR_FAILURE;
  
  // The _moz_dirty attribute is emitted by the editor to
  // indicate that this element should be pretty printed
  // even if we're not in pretty printing mode
  PRBool hasDirtyAttr = content->HasAttr(kNameSpaceID_None,
                                         nsLayoutAtoms::mozdirty);

  nsIAtom *name = content->Tag();

  if (name == nsHTMLAtoms::br && mPreLevel > 0
      && (mFlags & nsIDocumentEncoder::OutputNoFormattingInPre)) {
    AppendToString(mLineBreak, aStr);
    mMayIgnoreLineBreakSequence = PR_TRUE;
    mColPos = 0;
    return NS_OK;
  }

  if (name == nsHTMLAtoms::body) {
    mInBody = PR_TRUE;
  }

  if (LineBreakBeforeOpen(name, hasDirtyAttr)) {
    AppendToString(mLineBreak, aStr);
    mMayIgnoreLineBreakSequence = PR_TRUE;
    mColPos = 0;
    mAddSpace = PR_FALSE;
  }
  else if (mAddSpace) {
    AppendToString(PRUnichar(' '), aStr);
    mAddSpace = PR_FALSE;
  }
  else {
    MaybeAddNewline(aStr);
  }
  // Always reset to avoid false newlines in case MaybeAddNewline wasn't
  // called
  mAddNewline = PR_FALSE;

  StartIndentation(name, hasDirtyAttr, aStr);

  if (name == nsHTMLAtoms::pre ||
      name == nsHTMLAtoms::script ||
      name == nsHTMLAtoms::style) {
    mPreLevel++;
  }
  
  AppendToString(kLessThan, aStr);

  nsAutoString nameStr;
  name->ToString(nameStr);
  AppendToString(nameStr.get(), -1, aStr);

  // Need to keep track of OL and LI elements in order to get ordinal number 
  // for the LI.
  if (mIsCopying && name == nsHTMLAtoms::ol){
    // We are copying and current node is an OL;
    // Store it's start attribute value in olState->startVal.
    nsAutoString start;
    PRInt32 startAttrVal = 0;
    aElement->GetAttribute(NS_LITERAL_STRING("start"), start);
    if (!start.IsEmpty()){
      PRInt32 rv = 0;
      startAttrVal = start.ToInteger(&rv);
      //If OL has "start" attribute, first LI element has to start with that value
      //Therefore subtracting 1 as all the LI elements are incrementing it before using it;
      //In failure of ToInteger(), default StartAttrValue to 0.
      if (NS_SUCCEEDED(rv))
        startAttrVal--; 
      else
        startAttrVal = 0;
    }
    olState* state = new olState(startAttrVal, PR_TRUE);
    if (state)
      mOLStateStack.AppendElement(state);
  }

  if (mIsCopying && name == nsHTMLAtoms::li) {
    mIsFirstChildOfOL = IsFirstChildOfOL(aElement);
    if (mIsFirstChildOfOL){
      // If OL is parent of this LI, serialize attributes in different manner.
      SerializeLIValueAttribute(aElement, aStr);
    }
  }

  // Even LI passed above have to go through this 
  // for serializing attributes other than "value".
  SerializeAttributes(content, name, aStr);

  AppendToString(kGreaterThan, aStr);

  if (LineBreakAfterOpen(name, hasDirtyAttr)) {
    AppendToString(mLineBreak, aStr);
    mMayIgnoreLineBreakSequence = PR_TRUE;
    mColPos = 0;
  }

  if (name == nsHTMLAtoms::script ||
      name == nsHTMLAtoms::style ||
      name == nsHTMLAtoms::noscript ||
      name == nsHTMLAtoms::noframes) {
    mInCDATA = PR_TRUE;
  }

  return NS_OK;
}