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