/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * nsIContentSerializer implementation that can be used with an * nsIDocumentEncoder to convert an XML DOM to an XML string that * could be parsed into more or less the original DOM. */ #ifndef nsXMLContentSerializer_h__ #define nsXMLContentSerializer_h__ #include "mozilla/Attributes.h" #include "nsIContentSerializer.h" #include "nsISupportsUtils.h" #include "nsCOMPtr.h" #include "nsTArray.h" #include "nsString.h" #define kIndentStr NS_LITERAL_STRING(" ") #define kEndTag NS_LITERAL_STRING("</") class nsIAtom; class nsINode; class nsXMLContentSerializer : public nsIContentSerializer { public: nsXMLContentSerializer(); NS_DECL_ISUPPORTS NS_IMETHOD Init(uint32_t flags, uint32_t aWrapColumn, const char* aCharSet, bool aIsCopying, bool aRewriteEncodingDeclaration) override; NS_IMETHOD AppendText(nsIContent* aText, int32_t aStartOffset, int32_t aEndOffset, nsAString& aStr) override; NS_IMETHOD AppendCDATASection(nsIContent* aCDATASection, int32_t aStartOffset, int32_t aEndOffset, nsAString& aStr) override; NS_IMETHOD AppendProcessingInstruction(nsIContent* aPI, int32_t aStartOffset, int32_t aEndOffset, nsAString& aStr) override; NS_IMETHOD AppendComment(nsIContent* aComment, int32_t aStartOffset, int32_t aEndOffset, nsAString& aStr) override; NS_IMETHOD AppendDoctype(nsIContent *aDoctype, nsAString& aStr) override; NS_IMETHOD AppendElementStart(mozilla::dom::Element* aElement, mozilla::dom::Element* aOriginalElement, nsAString& aStr) override; NS_IMETHOD AppendElementEnd(mozilla::dom::Element* aElement, nsAString& aStr) override; NS_IMETHOD Flush(nsAString& aStr) override { return NS_OK; } NS_IMETHOD AppendDocumentStart(nsIDocument *aDocument, nsAString& aStr) override; protected: virtual ~nsXMLContentSerializer(); /** * Appends a char16_t character and increments the column position */ MOZ_MUST_USE bool AppendToString(const char16_t aChar, nsAString& aOutputStr); /** * Appends a nsAString string and increments the column position */ MOZ_MUST_USE bool AppendToString(const nsAString& aStr, nsAString& aOutputStr); /** * Appends a string by replacing all line-endings * by mLineBreak, except in the case of raw output. * It increments the column position. */ MOZ_MUST_USE bool AppendToStringConvertLF(const nsAString& aStr, nsAString& aOutputStr); /** * Appends a string by wrapping it when necessary. * It updates the column position. */ MOZ_MUST_USE bool AppendToStringWrapped(const nsASingleFragmentString& aStr, nsAString& aOutputStr); /** * Appends a string by formating and wrapping it when necessary * It updates the column position. */ MOZ_MUST_USE bool AppendToStringFormatedWrapped(const nsASingleFragmentString& aStr, nsAString& aOutputStr); // used by AppendToStringWrapped MOZ_MUST_USE bool AppendWrapped_WhitespaceSequence( nsASingleFragmentString::const_char_iterator &aPos, const nsASingleFragmentString::const_char_iterator aEnd, const nsASingleFragmentString::const_char_iterator aSequenceStart, nsAString &aOutputStr); // used by AppendToStringFormatedWrapped MOZ_MUST_USE bool AppendFormatedWrapped_WhitespaceSequence( nsASingleFragmentString::const_char_iterator &aPos, const nsASingleFragmentString::const_char_iterator aEnd, const nsASingleFragmentString::const_char_iterator aSequenceStart, bool &aMayIgnoreStartOfLineWhitespaceSequence, nsAString &aOutputStr); // used by AppendToStringWrapped and AppendToStringFormatedWrapped MOZ_MUST_USE bool AppendWrapped_NonWhitespaceSequence( nsASingleFragmentString::const_char_iterator &aPos, const nsASingleFragmentString::const_char_iterator aEnd, const nsASingleFragmentString::const_char_iterator aSequenceStart, bool &aMayIgnoreStartOfLineWhitespaceSequence, bool &aSequenceStartAfterAWhiteSpace, nsAString &aOutputStr); /** * add mLineBreak to the string * It updates the column position and other flags. */ MOZ_MUST_USE bool AppendNewLineToString(nsAString& aOutputStr); /** * Appends a string by translating entities * It doesn't increment the column position */ MOZ_MUST_USE virtual bool AppendAndTranslateEntities(const nsAString& aStr, nsAString& aOutputStr); /** * retrieve the text content of the node and append it to the given string * It doesn't increment the column position */ nsresult AppendTextData(nsIContent* aNode, int32_t aStartOffset, int32_t aEndOffset, nsAString& aStr, bool aTranslateEntities); virtual nsresult PushNameSpaceDecl(const nsAString& aPrefix, const nsAString& aURI, nsIContent* aOwner); void PopNameSpaceDeclsFor(nsIContent* aOwner); /** * The problem that ConfirmPrefix fixes is that anyone can insert nodes * through the DOM that have a namespace URI and a random or empty or * previously existing prefix that's totally unrelated to the prefixes * declared at that point through xmlns attributes. So what ConfirmPrefix * does is ensure that we can map aPrefix to the namespace URI aURI (for * example, that the prefix is not already mapped to some other namespace). * aPrefix will be adjusted, if necessary, so the value of the prefix * _after_ this call is what should be serialized. * @param aPrefix the prefix that may need adjusting * @param aURI the namespace URI we want aPrefix to point to * @param aElement the element we're working with (needed for proper default * namespace handling) * @param aIsAttribute true if we're confirming a prefix for an attribute. * @return true if we need to push the (prefix, uri) pair on the namespace * stack (note that this can happen even if the prefix is * empty). */ bool ConfirmPrefix(nsAString& aPrefix, const nsAString& aURI, nsIContent* aElement, bool aIsAttribute); /** * GenerateNewPrefix generates a new prefix and writes it to aPrefix */ void GenerateNewPrefix(nsAString& aPrefix); uint32_t ScanNamespaceDeclarations(nsIContent* aContent, nsIContent *aOriginalElement, const nsAString& aTagNamespaceURI); MOZ_MUST_USE virtual bool SerializeAttributes(nsIContent* aContent, nsIContent *aOriginalElement, nsAString& aTagPrefix, const nsAString& aTagNamespaceURI, nsIAtom* aTagName, nsAString& aStr, uint32_t aSkipAttr, bool aAddNSAttr); MOZ_MUST_USE bool SerializeAttr(const nsAString& aPrefix, const nsAString& aName, const nsAString& aValue, nsAString& aStr, bool aDoEscapeEntities); bool IsJavaScript(nsIContent * aContent, nsIAtom* aAttrNameAtom, int32_t aAttrNamespaceID, const nsAString& aValueString); /** * This method can be redefined to check if the element can be serialized. * It is called when the serialization of the start tag is asked * (AppendElementStart) * In this method you can also force the formating * by setting aForceFormat to true. * @return boolean true if the element can be output */ virtual bool CheckElementStart(nsIContent * aContent, bool & aForceFormat, nsAString& aStr, nsresult& aResult); /** * This method is responsible for appending the '>' at the end of the start * tag, possibly preceded by '/' and maybe a ' ' before that too. * * aElement and aOriginalElement are the same as the corresponding arguments * to AppendElementStart. */ MOZ_MUST_USE bool AppendEndOfElementStart(mozilla::dom::Element* aEleemnt, mozilla::dom::Element* aOriginalElement, nsAString& aStr); /** * This method can be redefine to serialize additional things just after * after the serialization ot the start tag. * (called at the end of AppendElementStart) */ MOZ_MUST_USE virtual bool AfterElementStart(nsIContent* aContent, nsIContent* aOriginalElement, nsAString& aStr) { return true; }; /** * This method can be redefined to check if the element can be serialized. * It is called when the serialization of the end tag is asked * (AppendElementEnd) * In this method you can also force the formating * by setting aForceFormat to true. * @return boolean true if the element can be output */ virtual bool CheckElementEnd(mozilla::dom::Element* aElement, bool& aForceFormat, nsAString& aStr); /** * This method can be redefine to serialize additional things just after * after the serialization ot the end tag. * (called at the end of AppendElementStart) */ virtual void AfterElementEnd(nsIContent * aContent, nsAString& aStr) { }; /** * Returns true if a line break should be inserted before an element open tag */ virtual bool LineBreakBeforeOpen(int32_t aNamespaceID, nsIAtom* aName); /** * Returns true if a line break should be inserted after an element open tag */ virtual bool LineBreakAfterOpen(int32_t aNamespaceID, nsIAtom* aName); /** * Returns true if a line break should be inserted after an element close tag */ virtual bool LineBreakBeforeClose(int32_t aNamespaceID, nsIAtom* aName); /** * Returns true if a line break should be inserted after an element close tag */ virtual bool LineBreakAfterClose(int32_t aNamespaceID, nsIAtom* aName); /** * add intendation. Call only in the case of formating and if the current * position is at 0. It updates the column position. */ MOZ_MUST_USE bool AppendIndentation(nsAString& aStr); MOZ_MUST_USE bool IncrIndentation(nsIAtom* aName); void DecrIndentation(nsIAtom* aName); // Functions to check for newlines that needs to be added between nodes in // the root of a document. See mAddNewlineForRootNode MOZ_MUST_USE bool MaybeAddNewlineForRootNode(nsAString& aStr); void MaybeFlagNewlineForRootNode(nsINode* aNode); // Functions to check if we enter in or leave from a preformated content virtual void MaybeEnterInPreContent(nsIContent* aNode); virtual void MaybeLeaveFromPreContent(nsIContent* aNode); bool ShouldMaintainPreLevel() const; int32_t PreLevel() const { MOZ_ASSERT(ShouldMaintainPreLevel()); return mPreLevel; } int32_t& PreLevel() { MOZ_ASSERT(ShouldMaintainPreLevel()); return mPreLevel; } int32_t mPrefixIndex; struct NameSpaceDecl { nsString mPrefix; nsString mURI; nsIContent* mOwner; }; nsTArray<NameSpaceDecl> mNameSpaceStack; // nsIDocumentEncoder flags MOZ_INIT_OUTSIDE_CTOR uint32_t mFlags; // characters to use for line break nsString mLineBreak; // The charset that was passed to Init() nsCString mCharset; // current column position on the current line uint32_t mColPos; // true = pretty formating should be done (OutputFormated flag) MOZ_INIT_OUTSIDE_CTOR bool mDoFormat; // true = no formatting,(OutputRaw flag) // no newline convertion and no rewrap long lines even if OutputWrap is set. MOZ_INIT_OUTSIDE_CTOR bool mDoRaw; // true = wrapping should be done (OutputWrap flag) MOZ_INIT_OUTSIDE_CTOR bool mDoWrap; // true = we can break lines (OutputDisallowLineBreaking flag) MOZ_INIT_OUTSIDE_CTOR bool mAllowLineBreaking; // number of maximum column in a line, in the wrap mode MOZ_INIT_OUTSIDE_CTOR uint32_t mMaxColumn; // current indent value nsString mIndent; // this is the indentation level after the indentation reached // the maximum length of indentation int32_t mIndentOverflow; // says if the indentation has been already added on the current line bool mIsIndentationAddedOnCurrentLine; // the string which is currently added is in an attribute bool mInAttribute; // true = a newline character should be added. It's only // useful when serializing root nodes. see MaybeAddNewlineForRootNode and // MaybeFlagNewlineForRootNode bool mAddNewlineForRootNode; // Indicates that a space will be added if and only if content is // continued on the same line while serializing source. Otherwise, // the newline character acts as the whitespace and no space is needed. // used when mDoFormat = true bool mAddSpace; // says that if the next string to add contains a newline character at the // begining, then this newline character should be ignored, because a // such character has already been added into the output string bool mMayIgnoreLineBreakSequence; bool mBodyOnly; int32_t mInBody; private: // number of nested elements which have preformated content MOZ_INIT_OUTSIDE_CTOR int32_t mPreLevel; }; nsresult NS_NewXMLContentSerializer(nsIContentSerializer** aSerializer); #endif