michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef nsHtml5TreeOperation_h michael@0: #define nsHtml5TreeOperation_h michael@0: michael@0: #include "nsHtml5DocumentMode.h" michael@0: #include "nsHtml5HtmlAttributes.h" michael@0: #include "nsXPCOMStrings.h" michael@0: #include "mozilla/dom/FromParser.h" michael@0: michael@0: class nsIContent; michael@0: class nsHtml5TreeOpExecutor; michael@0: class nsHtml5StateSnapshot; michael@0: class nsHtml5DocumentBuilder; michael@0: michael@0: enum eHtml5TreeOperation { michael@0: #ifdef DEBUG michael@0: eTreeOpUninitialized, michael@0: #endif michael@0: // main HTML5 ops michael@0: eTreeOpAppend, michael@0: eTreeOpDetach, michael@0: eTreeOpAppendChildrenToNewParent, michael@0: eTreeOpFosterParent, michael@0: eTreeOpAppendToDocument, michael@0: eTreeOpAddAttributes, michael@0: eTreeOpDocumentMode, michael@0: eTreeOpCreateElementNetwork, michael@0: eTreeOpCreateElementNotNetwork, michael@0: eTreeOpSetFormElement, michael@0: eTreeOpAppendText, michael@0: eTreeOpAppendIsindexPrompt, michael@0: eTreeOpFosterParentText, michael@0: eTreeOpAppendComment, michael@0: eTreeOpAppendCommentToDocument, michael@0: eTreeOpAppendDoctypeToDocument, michael@0: eTreeOpGetDocumentFragmentForTemplate, michael@0: // Gecko-specific on-pop ops michael@0: eTreeOpMarkAsBroken, michael@0: eTreeOpRunScript, michael@0: eTreeOpRunScriptAsyncDefer, michael@0: eTreeOpPreventScriptExecution, michael@0: eTreeOpDoneAddingChildren, michael@0: eTreeOpDoneCreatingElement, michael@0: eTreeOpFlushPendingAppendNotifications, michael@0: eTreeOpSetDocumentCharset, michael@0: eTreeOpNeedsCharsetSwitchTo, michael@0: eTreeOpUpdateStyleSheet, michael@0: eTreeOpProcessMeta, michael@0: eTreeOpProcessOfflineManifest, michael@0: eTreeOpMarkMalformedIfScript, michael@0: eTreeOpStreamEnded, michael@0: eTreeOpSetStyleLineNumber, michael@0: eTreeOpSetScriptLineNumberAndFreeze, michael@0: eTreeOpSvgLoad, michael@0: eTreeOpMaybeComplainAboutCharset, michael@0: eTreeOpAddClass, michael@0: eTreeOpAddViewSourceHref, michael@0: eTreeOpAddError, michael@0: eTreeOpAddLineNumberId, michael@0: eTreeOpAddErrorAtom, michael@0: eTreeOpAddErrorTwoAtoms, michael@0: eTreeOpStartLayout michael@0: }; michael@0: michael@0: class nsHtml5TreeOperationStringPair { michael@0: private: michael@0: nsString mPublicId; michael@0: nsString mSystemId; michael@0: public: michael@0: nsHtml5TreeOperationStringPair(const nsAString& aPublicId, michael@0: const nsAString& aSystemId) michael@0: : mPublicId(aPublicId) michael@0: , mSystemId(aSystemId) michael@0: { michael@0: MOZ_COUNT_CTOR(nsHtml5TreeOperationStringPair); michael@0: } michael@0: michael@0: ~nsHtml5TreeOperationStringPair() michael@0: { michael@0: MOZ_COUNT_DTOR(nsHtml5TreeOperationStringPair); michael@0: } michael@0: michael@0: inline void Get(nsAString& aPublicId, nsAString& aSystemId) michael@0: { michael@0: aPublicId.Assign(mPublicId); michael@0: aSystemId.Assign(mSystemId); michael@0: } michael@0: }; michael@0: michael@0: class nsHtml5TreeOperation { michael@0: michael@0: public: michael@0: /** michael@0: * Atom is used inside the parser core are either static atoms that are michael@0: * the same as Gecko-wide static atoms or they are dynamic atoms scoped by michael@0: * both thread and parser to a particular nsHtml5AtomTable. In order to michael@0: * such scoped atoms coming into contact with the rest of Gecko, atoms michael@0: * that are about to exit the parser must go through this method which michael@0: * reobtains dynamic atoms from the Gecko-global atom table. michael@0: * michael@0: * @param aAtom a potentially parser-scoped atom michael@0: * @return an nsIAtom that's pointer comparable on the main thread with michael@0: * other not-parser atoms. michael@0: */ michael@0: static inline already_AddRefed Reget(nsIAtom* aAtom) michael@0: { michael@0: if (!aAtom || aAtom->IsStaticAtom()) { michael@0: return dont_AddRef(aAtom); michael@0: } michael@0: nsAutoString str; michael@0: aAtom->ToString(str); michael@0: return do_GetAtom(str); michael@0: } michael@0: michael@0: static nsresult AppendTextToTextNode(const char16_t* aBuffer, michael@0: uint32_t aLength, michael@0: nsIContent* aTextNode, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static nsresult AppendText(const char16_t* aBuffer, michael@0: uint32_t aLength, michael@0: nsIContent* aParent, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static nsresult Append(nsIContent* aNode, michael@0: nsIContent* aParent, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static nsresult AppendToDocument(nsIContent* aNode, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static void Detach(nsIContent* aNode, nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static nsresult AppendChildrenToNewParent(nsIContent* aNode, michael@0: nsIContent* aParent, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static nsresult FosterParent(nsIContent* aNode, michael@0: nsIContent* aParent, michael@0: nsIContent* aTable, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static nsresult AddAttributes(nsIContent* aNode, michael@0: nsHtml5HtmlAttributes* aAttributes, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static nsIContent* CreateElement(int32_t aNs, michael@0: nsIAtom* aName, michael@0: nsHtml5HtmlAttributes* aAttributes, michael@0: mozilla::dom::FromParser aFromParser, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static void SetFormElement(nsIContent* aNode, nsIContent* aParent); michael@0: michael@0: static nsresult AppendIsindexPrompt(nsIContent* parent, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static nsresult FosterParentText(nsIContent* aStackParent, michael@0: char16_t* aBuffer, michael@0: uint32_t aLength, michael@0: nsIContent* aTable, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static nsresult AppendComment(nsIContent* aParent, michael@0: char16_t* aBuffer, michael@0: int32_t aLength, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static nsresult AppendCommentToDocument(char16_t* aBuffer, michael@0: int32_t aLength, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static nsresult AppendDoctypeToDocument(nsIAtom* aName, michael@0: const nsAString& aPublicId, michael@0: const nsAString& aSystemId, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static nsIContent* GetDocumentFragmentForTemplate(nsIContent* aNode); michael@0: michael@0: static void PreventScriptExecution(nsIContent* aNode); michael@0: michael@0: static void DoneAddingChildren(nsIContent* aNode, michael@0: nsHtml5DocumentBuilder* aBuilder); michael@0: michael@0: static void DoneCreatingElement(nsIContent* aNode); michael@0: michael@0: static void SvgLoad(nsIContent* aNode); michael@0: michael@0: static void MarkMalformedIfScript(nsIContent* aNode); michael@0: michael@0: nsHtml5TreeOperation(); michael@0: michael@0: ~nsHtml5TreeOperation(); michael@0: michael@0: inline void Init(eHtml5TreeOperation aOpCode) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: mOpCode = aOpCode; michael@0: } michael@0: michael@0: inline void Init(eHtml5TreeOperation aOpCode, nsIContentHandle* aNode) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: NS_PRECONDITION(aNode, "Initialized tree op with null node."); michael@0: mOpCode = aOpCode; michael@0: mOne.node = static_cast(aNode); michael@0: } michael@0: michael@0: inline void Init(eHtml5TreeOperation aOpCode, michael@0: nsIContentHandle* aNode, michael@0: nsIContentHandle* aParent) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: NS_PRECONDITION(aNode, "Initialized tree op with null node."); michael@0: NS_PRECONDITION(aParent, "Initialized tree op with null parent."); michael@0: mOpCode = aOpCode; michael@0: mOne.node = static_cast(aNode); michael@0: mTwo.node = static_cast(aParent); michael@0: } michael@0: michael@0: inline void Init(eHtml5TreeOperation aOpCode, michael@0: const nsACString& aString, michael@0: int32_t aInt32) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: michael@0: int32_t len = aString.Length(); michael@0: char* str = new char[len + 1]; michael@0: const char* start = aString.BeginReading(); michael@0: for (int32_t i = 0; i < len; ++i) { michael@0: str[i] = start[i]; michael@0: } michael@0: str[len] = '\0'; michael@0: michael@0: mOpCode = aOpCode; michael@0: mOne.charPtr = str; michael@0: mFour.integer = aInt32; michael@0: } michael@0: michael@0: inline void Init(eHtml5TreeOperation aOpCode, michael@0: const nsACString& aString, michael@0: int32_t aInt32, michael@0: int32_t aLineNumber) michael@0: { michael@0: Init(aOpCode, aString, aInt32); michael@0: mTwo.integer = aLineNumber; michael@0: } michael@0: michael@0: inline void Init(eHtml5TreeOperation aOpCode, michael@0: nsIContentHandle* aNode, michael@0: nsIContentHandle* aParent, michael@0: nsIContentHandle* aTable) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: NS_PRECONDITION(aNode, "Initialized tree op with null node."); michael@0: NS_PRECONDITION(aParent, "Initialized tree op with null parent."); michael@0: NS_PRECONDITION(aTable, "Initialized tree op with null table."); michael@0: mOpCode = aOpCode; michael@0: mOne.node = static_cast(aNode); michael@0: mTwo.node = static_cast(aParent); michael@0: mThree.node = static_cast(aTable); michael@0: } michael@0: michael@0: inline void Init(nsHtml5DocumentMode aMode) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: mOpCode = eTreeOpDocumentMode; michael@0: mOne.mode = aMode; michael@0: } michael@0: michael@0: inline void InitScript(nsIContentHandle* aNode) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: NS_PRECONDITION(aNode, "Initialized tree op with null node."); michael@0: mOpCode = eTreeOpRunScript; michael@0: mOne.node = static_cast(aNode); michael@0: mTwo.state = nullptr; michael@0: } michael@0: michael@0: inline void Init(int32_t aNamespace, michael@0: nsIAtom* aName, michael@0: nsHtml5HtmlAttributes* aAttributes, michael@0: nsIContentHandle* aTarget, michael@0: bool aFromNetwork) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: NS_PRECONDITION(aName, "Initialized tree op with null name."); michael@0: NS_PRECONDITION(aTarget, "Initialized tree op with null target node."); michael@0: mOpCode = aFromNetwork ? michael@0: eTreeOpCreateElementNetwork : michael@0: eTreeOpCreateElementNotNetwork; michael@0: mFour.integer = aNamespace; michael@0: mOne.node = static_cast(aTarget); michael@0: mTwo.atom = aName; michael@0: if (aAttributes == nsHtml5HtmlAttributes::EMPTY_ATTRIBUTES) { michael@0: mThree.attributes = nullptr; michael@0: } else { michael@0: mThree.attributes = aAttributes; michael@0: } michael@0: } michael@0: michael@0: inline void Init(eHtml5TreeOperation aOpCode, michael@0: char16_t* aBuffer, michael@0: int32_t aLength, michael@0: nsIContentHandle* aStackParent, michael@0: nsIContentHandle* aTable) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: NS_PRECONDITION(aBuffer, "Initialized tree op with null buffer."); michael@0: mOpCode = aOpCode; michael@0: mOne.node = static_cast(aStackParent); michael@0: mTwo.unicharPtr = aBuffer; michael@0: mThree.node = static_cast(aTable); michael@0: mFour.integer = aLength; michael@0: } michael@0: michael@0: inline void Init(eHtml5TreeOperation aOpCode, michael@0: char16_t* aBuffer, michael@0: int32_t aLength, michael@0: nsIContentHandle* aParent) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: NS_PRECONDITION(aBuffer, "Initialized tree op with null buffer."); michael@0: mOpCode = aOpCode; michael@0: mOne.node = static_cast(aParent); michael@0: mTwo.unicharPtr = aBuffer; michael@0: mFour.integer = aLength; michael@0: } michael@0: michael@0: inline void Init(eHtml5TreeOperation aOpCode, michael@0: char16_t* aBuffer, michael@0: int32_t aLength) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: NS_PRECONDITION(aBuffer, "Initialized tree op with null buffer."); michael@0: mOpCode = aOpCode; michael@0: mTwo.unicharPtr = aBuffer; michael@0: mFour.integer = aLength; michael@0: } michael@0: michael@0: inline void Init(nsIContentHandle* aElement, michael@0: nsHtml5HtmlAttributes* aAttributes) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: NS_PRECONDITION(aElement, "Initialized tree op with null element."); michael@0: mOpCode = eTreeOpAddAttributes; michael@0: mOne.node = static_cast(aElement); michael@0: mTwo.attributes = aAttributes; michael@0: } michael@0: michael@0: inline void Init(nsIAtom* aName, michael@0: const nsAString& aPublicId, michael@0: const nsAString& aSystemId) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: mOpCode = eTreeOpAppendDoctypeToDocument; michael@0: mOne.atom = aName; michael@0: mTwo.stringPair = new nsHtml5TreeOperationStringPair(aPublicId, aSystemId); michael@0: } michael@0: michael@0: inline void Init(nsIContentHandle* aElement, michael@0: const char* aMsgId, michael@0: nsIAtom* aAtom, michael@0: nsIAtom* aOtherAtom) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: mOpCode = eTreeOpAddError; michael@0: mOne.node = static_cast(aElement); michael@0: mTwo.charPtr = (char*)aMsgId; michael@0: mThree.atom = aAtom; michael@0: mFour.atom = aOtherAtom; michael@0: } michael@0: michael@0: inline void Init(nsIContentHandle* aElement, michael@0: const char* aMsgId, michael@0: nsIAtom* aAtom) michael@0: { michael@0: Init(aElement, aMsgId, aAtom, nullptr); michael@0: } michael@0: michael@0: inline void Init(nsIContentHandle* aElement, michael@0: const char* aMsgId) michael@0: { michael@0: Init(aElement, aMsgId, nullptr, nullptr); michael@0: } michael@0: michael@0: inline void Init(const char* aMsgId, michael@0: bool aError, michael@0: int32_t aLineNumber) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: mOpCode = eTreeOpMaybeComplainAboutCharset; michael@0: mOne.charPtr = const_cast(aMsgId); michael@0: mTwo.integer = aError; michael@0: mThree.integer = aLineNumber; michael@0: } michael@0: michael@0: inline void Init(eHtml5TreeOperation aOpCode, const nsAString& aString) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: michael@0: char16_t* str = NS_StringCloneData(aString); michael@0: mOpCode = aOpCode; michael@0: mOne.unicharPtr = str; michael@0: } michael@0: michael@0: inline void Init(eHtml5TreeOperation aOpCode, michael@0: nsIContentHandle* aNode, michael@0: int32_t aInt) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: NS_PRECONDITION(aNode, "Initialized tree op with null node."); michael@0: mOpCode = aOpCode; michael@0: mOne.node = static_cast(aNode); michael@0: mFour.integer = aInt; michael@0: } michael@0: michael@0: inline void Init(nsresult aRv) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: NS_PRECONDITION(NS_FAILED(aRv), "Initialized tree op with non-failure."); michael@0: mOpCode = eTreeOpMarkAsBroken; michael@0: mOne.result = aRv; michael@0: } michael@0: michael@0: inline void InitAddClass(nsIContentHandle* aNode, const char16_t* aClass) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: NS_PRECONDITION(aNode, "Initialized tree op with null node."); michael@0: NS_PRECONDITION(aClass, "Initialized tree op with null string."); michael@0: // aClass must be a literal string that does not need freeing michael@0: mOpCode = eTreeOpAddClass; michael@0: mOne.node = static_cast(aNode); michael@0: mTwo.unicharPtr = (char16_t*)aClass; michael@0: } michael@0: michael@0: inline void InitAddLineNumberId(nsIContentHandle* aNode, michael@0: const int32_t aLineNumber) michael@0: { michael@0: NS_PRECONDITION(mOpCode == eTreeOpUninitialized, michael@0: "Op code must be uninitialized when initializing."); michael@0: NS_PRECONDITION(aNode, "Initialized tree op with null node."); michael@0: NS_PRECONDITION(aLineNumber > 0, "Initialized tree op with line number."); michael@0: // aClass must be a literal string that does not need freeing michael@0: mOpCode = eTreeOpAddLineNumberId; michael@0: mOne.node = static_cast(aNode); michael@0: mFour.integer = aLineNumber; michael@0: } michael@0: michael@0: inline bool IsRunScript() michael@0: { michael@0: return mOpCode == eTreeOpRunScript; michael@0: } michael@0: michael@0: inline void SetSnapshot(nsAHtml5TreeBuilderState* aSnapshot, int32_t aLine) michael@0: { michael@0: NS_ASSERTION(IsRunScript(), michael@0: "Setting a snapshot for a tree operation other than eTreeOpRunScript!"); michael@0: NS_PRECONDITION(aSnapshot, "Initialized tree op with null snapshot."); michael@0: mTwo.state = aSnapshot; michael@0: mFour.integer = aLine; michael@0: } michael@0: michael@0: nsresult Perform(nsHtml5TreeOpExecutor* aBuilder, michael@0: nsIContent** aScriptElement); michael@0: michael@0: private: michael@0: // possible optimization: michael@0: // Make the queue take items the size of pointer and make the op code michael@0: // decide how many operands it dequeues after it. michael@0: eHtml5TreeOperation mOpCode; michael@0: union { michael@0: nsIContent** node; michael@0: nsIAtom* atom; michael@0: nsHtml5HtmlAttributes* attributes; michael@0: nsHtml5DocumentMode mode; michael@0: char16_t* unicharPtr; michael@0: char* charPtr; michael@0: nsHtml5TreeOperationStringPair* stringPair; michael@0: nsAHtml5TreeBuilderState* state; michael@0: int32_t integer; michael@0: nsresult result; michael@0: } mOne, mTwo, mThree, mFour; michael@0: }; michael@0: michael@0: #endif // nsHtml5TreeOperation_h