michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=2 sw=2 et tw=78: */ 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 nsHtml5DocumentBuilder_h michael@0: #define nsHtml5DocumentBuilder_h michael@0: michael@0: #include "nsHtml5PendingNotification.h" michael@0: #include "nsContentSink.h" michael@0: #include "nsHtml5DocumentMode.h" michael@0: #include "nsIDocument.h" michael@0: michael@0: typedef nsIContent* nsIContentPtr; michael@0: michael@0: enum eHtml5FlushState { michael@0: eNotFlushing = 0, // not flushing michael@0: eInFlush = 1, // the Flush() method is on the call stack michael@0: eInDocUpdate = 2, // inside an update batch on the document michael@0: eNotifying = 3 // flushing pending append notifications michael@0: }; michael@0: michael@0: class nsHtml5DocumentBuilder : public nsContentSink michael@0: { michael@0: public: michael@0: NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsHtml5DocumentBuilder, michael@0: nsContentSink) michael@0: michael@0: NS_DECL_ISUPPORTS_INHERITED michael@0: michael@0: inline void HoldElement(nsIContent* aContent) michael@0: { michael@0: mOwnedElements.AppendElement(aContent); michael@0: } michael@0: michael@0: inline bool HaveNotified(nsIContent* aNode) michael@0: { michael@0: NS_PRECONDITION(aNode, "HaveNotified called with null argument."); michael@0: const nsHtml5PendingNotification* start = mPendingNotifications.Elements(); michael@0: const nsHtml5PendingNotification* end = start + mPendingNotifications.Length(); michael@0: for (;;) { michael@0: nsIContent* parent = aNode->GetParent(); michael@0: if (!parent) { michael@0: return true; michael@0: } michael@0: for (nsHtml5PendingNotification* iter = (nsHtml5PendingNotification*)start; iter < end; ++iter) { michael@0: if (iter->Contains(parent)) { michael@0: return iter->HaveNotifiedIndex(parent->IndexOf(aNode)); michael@0: } michael@0: } michael@0: aNode = parent; michael@0: } michael@0: } michael@0: michael@0: void PostPendingAppendNotification(nsIContent* aParent, nsIContent* aChild) michael@0: { michael@0: bool newParent = true; michael@0: const nsIContentPtr* first = mElementsSeenInThisAppendBatch.Elements(); michael@0: const nsIContentPtr* last = first + mElementsSeenInThisAppendBatch.Length() - 1; michael@0: for (const nsIContentPtr* iter = last; iter >= first; --iter) { michael@0: #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH michael@0: sAppendBatchSlotsExamined++; michael@0: #endif michael@0: if (*iter == aParent) { michael@0: newParent = false; michael@0: break; michael@0: } michael@0: } michael@0: if (aChild->IsElement()) { michael@0: mElementsSeenInThisAppendBatch.AppendElement(aChild); michael@0: } michael@0: mElementsSeenInThisAppendBatch.AppendElement(aParent); michael@0: if (newParent) { michael@0: mPendingNotifications.AppendElement(aParent); michael@0: } michael@0: #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH michael@0: sAppendBatchExaminations++; michael@0: #endif michael@0: } michael@0: michael@0: void FlushPendingAppendNotifications() michael@0: { michael@0: NS_PRECONDITION(mFlushState == eInDocUpdate, "Notifications flushed outside update"); michael@0: mFlushState = eNotifying; michael@0: const nsHtml5PendingNotification* start = mPendingNotifications.Elements(); michael@0: const nsHtml5PendingNotification* end = start + mPendingNotifications.Length(); michael@0: for (nsHtml5PendingNotification* iter = (nsHtml5PendingNotification*)start; iter < end; ++iter) { michael@0: iter->Fire(); michael@0: } michael@0: mPendingNotifications.Clear(); michael@0: #ifdef DEBUG_NS_HTML5_TREE_OP_EXECUTOR_FLUSH michael@0: if (mElementsSeenInThisAppendBatch.Length() > sAppendBatchMaxSize) { michael@0: sAppendBatchMaxSize = mElementsSeenInThisAppendBatch.Length(); michael@0: } michael@0: #endif michael@0: mElementsSeenInThisAppendBatch.Clear(); michael@0: NS_ASSERTION(mFlushState == eNotifying, "mFlushState out of sync"); michael@0: mFlushState = eInDocUpdate; michael@0: } michael@0: michael@0: nsresult Init(nsIDocument* aDoc, nsIURI* aURI, michael@0: nsISupports* aContainer, nsIChannel* aChannel); michael@0: michael@0: // Getters and setters for fields from nsContentSink michael@0: nsIDocument* GetDocument() michael@0: { michael@0: return mDocument; michael@0: } michael@0: michael@0: nsNodeInfoManager* GetNodeInfoManager() michael@0: { michael@0: return mNodeInfoManager; michael@0: } michael@0: michael@0: /** michael@0: * Marks this parser as broken and tells the stream parser (if any) to michael@0: * terminate. michael@0: * michael@0: * @return aReason for convenience michael@0: */ michael@0: virtual nsresult MarkAsBroken(nsresult aReason); michael@0: michael@0: /** michael@0: * Checks if this parser is broken. Returns a non-NS_OK (i.e. non-0) michael@0: * value if broken. michael@0: */ michael@0: inline nsresult IsBroken() michael@0: { michael@0: return mBroken; michael@0: } michael@0: michael@0: inline void BeginDocUpdate() michael@0: { michael@0: NS_PRECONDITION(mFlushState == eInFlush, "Tried to double-open update."); michael@0: NS_PRECONDITION(mParser, "Started update without parser."); michael@0: mFlushState = eInDocUpdate; michael@0: mDocument->BeginUpdate(UPDATE_CONTENT_MODEL); michael@0: } michael@0: michael@0: inline void EndDocUpdate() michael@0: { michael@0: NS_PRECONDITION(mFlushState != eNotifying, "mFlushState out of sync"); michael@0: if (mFlushState == eInDocUpdate) { michael@0: FlushPendingAppendNotifications(); michael@0: mFlushState = eInFlush; michael@0: mDocument->EndUpdate(UPDATE_CONTENT_MODEL); michael@0: } michael@0: } michael@0: michael@0: void SetDocumentCharsetAndSource(nsACString& aCharset, int32_t aCharsetSource); michael@0: michael@0: /** michael@0: * Sets up style sheet load / parse michael@0: */ michael@0: void UpdateStyleSheet(nsIContent* aElement); michael@0: michael@0: void SetDocumentMode(nsHtml5DocumentMode m); michael@0: michael@0: void SetNodeInfoManager(nsNodeInfoManager* aManager) michael@0: { michael@0: mNodeInfoManager = aManager; michael@0: } michael@0: michael@0: // nsContentSink methods michael@0: virtual void UpdateChildCounts(); michael@0: virtual nsresult FlushTags(); michael@0: michael@0: protected: michael@0: inline void SetAppendBatchCapacity(uint32_t aCapacity) michael@0: { michael@0: mElementsSeenInThisAppendBatch.SetCapacity(aCapacity); michael@0: } michael@0: michael@0: nsHtml5DocumentBuilder(bool aRunsToCompletion); michael@0: virtual ~nsHtml5DocumentBuilder(); michael@0: michael@0: private: michael@0: nsTArray mPendingNotifications; michael@0: nsTArray mElementsSeenInThisAppendBatch; michael@0: protected: michael@0: nsTArray > mOwnedElements; michael@0: /** michael@0: * Non-NS_OK if this parser should refuse to process any more input. michael@0: * For example, the parser needs to be marked as broken if it drops some michael@0: * input due to a memory allocation failure. In such a case, the whole michael@0: * parser needs to be marked as broken, because some input has been lost michael@0: * and parsing more input could lead to a DOM where pieces of HTML source michael@0: * that weren't supposed to become scripts become scripts. michael@0: * michael@0: * Since NS_OK is actually 0, zeroing operator new takes care of michael@0: * initializing this. michael@0: */ michael@0: nsresult mBroken; michael@0: eHtml5FlushState mFlushState; michael@0: }; michael@0: michael@0: #endif // nsHtml5DocumentBuilder_h