diff options
Diffstat (limited to 'dom/prototype/PrototypeDocumentContentSink.h')
-rw-r--r-- | dom/prototype/PrototypeDocumentContentSink.h | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/dom/prototype/PrototypeDocumentContentSink.h b/dom/prototype/PrototypeDocumentContentSink.h new file mode 100644 index 0000000000..c4735ed52c --- /dev/null +++ b/dom/prototype/PrototypeDocumentContentSink.h @@ -0,0 +1,262 @@ +/* -*- 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 https://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_PrototypeDocumentContentSink_h__ +#define mozilla_dom_PrototypeDocumentContentSink_h__ + +#include "mozilla/Attributes.h" +#include "nsIContentSink.h" +#include "nsTArray.h" +#include "nsCOMPtr.h" +#include "nsCRT.h" +#include "nsCycleCollectionNoteChild.h" +#include "nsCycleCollectionParticipant.h" +#include "nsIDTD.h" +#include "mozilla/dom/FromParser.h" +#include "nsXULPrototypeDocument.h" +#include "nsIStreamLoader.h" +#include "nsIScriptContext.h" +#include "nsICSSLoaderObserver.h" +#include "mozilla/Logging.h" +#include "js/experimental/JSStencil.h" +#include "mozilla/RefPtr.h" + +class nsIURI; +class nsIChannel; +class nsIContent; +class nsIParser; +class nsTextNode; +class nsINode; +class nsXULPrototypeElement; +class nsXULPrototypePI; +class nsXULPrototypeScript; + +namespace mozilla::dom { +class Element; +class ScriptLoader; +class Document; +class XMLStylesheetProcessingInstruction; +} // namespace mozilla::dom + +nsresult NS_NewPrototypeDocumentContentSink(nsIContentSink** aResult, + mozilla::dom::Document* aDoc, + nsIURI* aURI, + nsISupports* aContainer, + nsIChannel* aChannel); + +namespace mozilla::dom { + +class PrototypeDocumentContentSink final : public nsIStreamLoaderObserver, + public nsIContentSink, + public nsICSSLoaderObserver, + public nsIOffThreadScriptReceiver { + public: + PrototypeDocumentContentSink(); + + nsresult Init(Document* aDoc, nsIURI* aURL, nsISupports* aContainer, + nsIChannel* aChannel); + + // nsISupports + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_NSISTREAMLOADEROBSERVER + + NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(PrototypeDocumentContentSink, + nsIContentSink) + + // nsIContentSink + NS_IMETHOD WillParse(void) override { return NS_OK; }; + NS_IMETHOD WillInterrupt(void) override { return NS_OK; }; + void WillResume() override{}; + NS_IMETHOD SetParser(nsParserBase* aParser) override; + virtual void InitialTranslationCompleted() override; + virtual void FlushPendingNotifications(FlushType aType) override{}; + virtual void SetDocumentCharset(NotNull<const Encoding*> aEncoding) override; + virtual nsISupports* GetTarget() override; + virtual bool IsScriptExecuting() override; + virtual void ContinueInterruptedParsingAsync() override; + + // nsICSSLoaderObserver + NS_IMETHOD StyleSheetLoaded(StyleSheet* aSheet, bool aWasDeferred, + nsresult aStatus) override; + + // nsIOffThreadScriptReceiver + NS_IMETHOD OnScriptCompileComplete(JS::Stencil* aStencil, + nsresult aStatus) override; + + nsresult OnPrototypeLoadDone(nsXULPrototypeDocument* aPrototype); + + protected: + virtual ~PrototypeDocumentContentSink(); + + static LazyLogModule gLog; + + nsIParser* GetParser(); + + void ContinueInterruptedParsingIfEnabled(); + void StartLayout(); + + virtual nsresult AddAttributes(nsXULPrototypeElement* aPrototype, + Element* aElement); + + RefPtr<nsParserBase> mParser; + nsCOMPtr<nsIURI> mDocumentURI; + RefPtr<Document> mDocument; + RefPtr<ScriptLoader> mScriptLoader; + + PrototypeDocumentContentSink* mNextSrcLoadWaiter; // [OWNER] but not COMPtr + + /** + * The prototype-script of the current transcluded script that is being + * loaded. For document.write('<script src="nestedwrite.js"><\/script>') + * to work, these need to be in a stack element type, and we need to hold + * the top of stack here. + */ + nsXULPrototypeScript* mCurrentScriptProto; + + /** + * Whether the current transcluded script is being compiled off thread. + * The load event is blocked while this is in progress. + */ + bool mOffThreadCompiling; + + /** + * If the current transcluded script is being compiled off thread, the + * source for that script. + */ + Utf8Unit* mOffThreadCompileStringBuf; + size_t mOffThreadCompileStringLength; + + /** + * Wether the prototype document is still be traversed to create the DOM. + * Layout will not be started until false. + */ + bool mStillWalking; + + /** + * Number of style sheets still loading. Layout will not start until zero. + */ + uint32_t mPendingSheets; + + /** + * Context stack, which maintains the state of the Builder and allows + * it to be interrupted. + */ + class ContextStack { + protected: + struct Entry { + nsXULPrototypeElement* mPrototype; + nsIContent* mElement; + int32_t mIndex; + Entry* mNext; + }; + + Entry* mTop; + int32_t mDepth; + + public: + ContextStack(); + ~ContextStack(); + + int32_t Depth() { return mDepth; } + + nsresult Push(nsXULPrototypeElement* aPrototype, nsIContent* aElement); + nsresult Pop(); + nsresult Peek(nsXULPrototypeElement** aPrototype, nsIContent** aElement, + int32_t* aIndex); + + nsresult SetTopIndex(int32_t aIndex); + + void Traverse(nsCycleCollectionTraversalCallback& aCallback, + const char* aName, uint32_t aFlags = 0); + void Clear(); + + // Cycle collector helpers for ContextStack. + friend void ImplCycleCollectionUnlink( + PrototypeDocumentContentSink::ContextStack& aField) { + aField.Clear(); + } + + friend void ImplCycleCollectionTraverse( + nsCycleCollectionTraversalCallback& aCallback, + PrototypeDocumentContentSink::ContextStack& aField, const char* aName, + uint32_t aFlags = 0) { + aField.Traverse(aCallback, aName, aFlags); + } + }; + + friend class ContextStack; + ContextStack mContextStack; + + /** + * The current prototype that we are walking to construct the + * content model. + */ + RefPtr<nsXULPrototypeDocument> mCurrentPrototype; + nsresult CreateAndInsertPI(const nsXULPrototypePI* aProtoPI); + nsresult ExecuteScript(nsXULPrototypeScript* aScript); + nsresult LoadScript(nsXULPrototypeScript* aScriptProto, bool* aBlock); + + /** + * A wrapper around ResumeWalkInternal to report walking errors. + */ + nsresult ResumeWalk(); + + /** + * Resume (or initiate) an interrupted (or newly prepared) + * prototype walk. + */ + nsresult ResumeWalkInternal(); + + /** + * Called at the end of ResumeWalk(), from StyleSheetLoaded(), + * and from DocumentL10n. + * If walking, stylesheets and l10n are not blocking, it + * will trigger `DoneWalking()`. + */ + nsresult MaybeDoneWalking(); + + /** + * Called from `MaybeDoneWalking()`. + * Expects that both the prototype document walk is complete and + * all referenced stylesheets finished loading. + */ + nsresult DoneWalking(); + + /** + * Create a delegate content model element from a prototype. + * Note that the resulting content node is not bound to any tree + */ + nsresult CreateElementFromPrototype(nsXULPrototypeElement* aPrototype, + Element** aResult, nsIContent* aParent); + /** + * Prepare to walk the current prototype. + */ + nsresult PrepareToWalk(); + /** + * Creates a processing instruction based on aProtoPI and inserts + * it to the DOM. + */ + nsresult CreateAndInsertPI(const nsXULPrototypePI* aProtoPI, nsINode* aParent, + nsINode* aBeforeThis); + + /** + * Inserts the passed <?xml-stylesheet ?> PI at the specified + * index. Loads and applies the associated stylesheet + * asynchronously. + * The prototype document walk can happen before the stylesheets + * are loaded, but the final steps in the load process (see + * DoneWalking()) are not run before all the stylesheets are done + * loading. + */ + nsresult InsertXMLStylesheetPI(const nsXULPrototypePI* aProtoPI, + nsINode* aParent, nsINode* aBeforeThis, + XMLStylesheetProcessingInstruction* aPINode); + void CloseElement(Element* aElement, bool aHadChildren); +}; + +} // namespace mozilla::dom + +#endif // mozilla_dom_PrototypeDocumentContentSink_h__ |