/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* 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 mozilla_dom_XULDocument_h #define mozilla_dom_XULDocument_h #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsXULPrototypeDocument.h" #include "nsXULPrototypeCache.h" #include "nsTArray.h" #include "mozilla/dom/XMLDocument.h" #include "mozilla/StyleSheet.h" #include "nsForwardReference.h" #include "nsIContent.h" #include "nsIDOMXULCommandDispatcher.h" #include "nsIDOMXULDocument.h" #include "nsCOMArray.h" #include "nsIURI.h" #include "nsIXULDocument.h" #include "nsIStreamListener.h" #include "nsIStreamLoader.h" #include "nsICSSLoaderObserver.h" #include "nsIXULStore.h" #include "mozilla/Attributes.h" #include "mozilla/dom/ScriptLoader.h" #include "js/TracingAPI.h" #include "js/TypeDecls.h" class nsIRDFResource; class nsIRDFService; class nsPIWindowRoot; #if 0 // XXXbe save me, scc (need NSCAP_FORWARD_DECL(nsXULPrototypeScript)) class nsIObjectInputStream; class nsIObjectOutputStream; #else #include "nsIObjectInputStream.h" #include "nsIObjectOutputStream.h" #include "nsXULElement.h" #endif #include "nsURIHashKey.h" #include "nsInterfaceHashtable.h" class nsRefMapEntry : public nsStringHashKey { public: explicit nsRefMapEntry(const nsAString& aKey) : nsStringHashKey(&aKey) { } explicit nsRefMapEntry(const nsAString* aKey) : nsStringHashKey(aKey) { } nsRefMapEntry(const nsRefMapEntry& aOther) : nsStringHashKey(&aOther.GetKey()) { NS_ERROR("Should never be called"); } mozilla::dom::Element* GetFirstElement(); void AppendAll(nsCOMArray* aElements); /** * @return true if aElement was added, false if we failed due to OOM */ bool AddElement(mozilla::dom::Element* aElement); /** * @return true if aElement was removed and it was the last content for * this ref, so this entry should be removed from the map */ bool RemoveElement(mozilla::dom::Element* aElement); private: nsTArray mRefContentList; }; /** * The XUL document class */ namespace mozilla { namespace dom { class XULDocument final : public XMLDocument, public nsIXULDocument, public nsIDOMXULDocument, public nsIStreamLoaderObserver, public nsICSSLoaderObserver, public nsIOffThreadScriptReceiver { public: XULDocument(); // nsISupports interface NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSISTREAMLOADEROBSERVER // nsIDocument interface virtual void Reset(nsIChannel* aChannel, nsILoadGroup* aLoadGroup) override; virtual void ResetToURI(nsIURI *aURI, nsILoadGroup* aLoadGroup, nsIPrincipal* aPrincipal) override; virtual nsresult StartDocumentLoad(const char* aCommand, nsIChannel *channel, nsILoadGroup* aLoadGroup, nsISupports* aContainer, nsIStreamListener **aDocListener, bool aReset = true, nsIContentSink* aSink = nullptr) override; virtual void SetContentType(const nsAString& aContentType) override; virtual void EndLoad() override; // nsIMutationObserver interface NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE // nsIXULDocument interface virtual void GetElementsForID(const nsAString& aID, nsCOMArray& aElements) override; NS_IMETHOD AddSubtreeToDocument(nsIContent* aContent) override; NS_IMETHOD RemoveSubtreeFromDocument(nsIContent* aContent) override; NS_IMETHOD SetTemplateBuilderFor(nsIContent* aContent, nsIXULTemplateBuilder* aBuilder) override; NS_IMETHOD GetTemplateBuilderFor(nsIContent* aContent, nsIXULTemplateBuilder** aResult) override; NS_IMETHOD OnPrototypeLoadDone(bool aResumeWalk) override; bool OnDocumentParserError() override; // nsINode interface overrides virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult, bool aPreallocateChildren) const override; // nsIDOMNode interface NS_FORWARD_NSIDOMNODE_TO_NSINODE // nsIDOMDocument interface using nsDocument::CreateElement; using nsDocument::CreateElementNS; NS_FORWARD_NSIDOMDOCUMENT(XMLDocument::) // And explicitly import the things from nsDocument that we just shadowed using nsDocument::GetImplementation; using nsDocument::GetTitle; using nsDocument::SetTitle; using nsDocument::GetLastStyleSheetSet; using nsDocument::MozSetImageElement; using nsIDocument::GetLocation; // nsDocument interface overrides virtual Element* GetElementById(const nsAString & elementId) override; // nsIDOMXULDocument interface NS_DECL_NSIDOMXULDOCUMENT // nsICSSLoaderObserver NS_IMETHOD StyleSheetLoaded(mozilla::StyleSheet* aSheet, bool aWasAlternate, nsresult aStatus) override; virtual void EndUpdate(nsUpdateType aUpdateType) override; virtual bool IsDocumentRightToLeft() override; virtual void ResetDocumentDirection() override; virtual int GetDocumentLWTheme() override; virtual void ResetDocumentLWTheme() override { mDocLWTheme = Doc_Theme_Uninitialized; } NS_IMETHOD OnScriptCompileComplete(JSScript* aScript, nsresult aStatus) override; static bool MatchAttribute(Element* aContent, int32_t aNameSpaceID, nsIAtom* aAttrName, void* aData); NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULDocument, XMLDocument) void TraceProtos(JSTracer* aTrc, uint32_t aGCNumber); // WebIDL API already_AddRefed GetPopupNode(); void SetPopupNode(nsINode* aNode); already_AddRefed GetPopupRangeParent(ErrorResult& aRv); int32_t GetPopupRangeOffset(ErrorResult& aRv); already_AddRefed GetTooltipNode(); void SetTooltipNode(nsINode* aNode) { /* do nothing */ } nsIDOMXULCommandDispatcher* GetCommandDispatcher() const { return mCommandDispatcher; } int32_t GetWidth(ErrorResult& aRv); int32_t GetHeight(ErrorResult& aRv); already_AddRefed GetElementsByAttribute(const nsAString& aAttribute, const nsAString& aValue); already_AddRefed GetElementsByAttributeNS(const nsAString& aNamespaceURI, const nsAString& aAttribute, const nsAString& aValue, ErrorResult& aRv); void AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener, const nsAString& aAttr, ErrorResult& aRv); void RemoveBroadcastListenerFor(Element& aBroadcaster, Element& aListener, const nsAString& aAttr); void Persist(const nsAString& aId, const nsAString& aAttr, ErrorResult& aRv) { aRv = Persist(aId, aAttr); } using nsDocument::GetBoxObjectFor; void LoadOverlay(const nsAString& aURL, nsIObserver* aObserver, ErrorResult& aRv) { aRv = LoadOverlay(aURL, aObserver); } protected: virtual ~XULDocument(); // Implementation methods friend nsresult (::NS_NewXULDocument(nsIXULDocument** aResult)); nsresult Init(void) override; nsresult StartLayout(void); nsresult AddElementToRefMap(Element* aElement); void RemoveElementFromRefMap(Element* aElement); nsresult GetViewportSize(int32_t* aWidth, int32_t* aHeight); nsresult PrepareToLoad(nsISupports* aContainer, const char* aCommand, nsIChannel* aChannel, nsILoadGroup* aLoadGroup, nsIParser** aResult); nsresult PrepareToLoadPrototype(nsIURI* aURI, const char* aCommand, nsIPrincipal* aDocumentPrincipal, nsIParser** aResult); nsresult LoadOverlayInternal(nsIURI* aURI, bool aIsDynamic, bool* aShouldReturn, bool* aFailureFromContent); nsresult ApplyPersistentAttributes(); nsresult ApplyPersistentAttributesInternal(); nsresult ApplyPersistentAttributesToElements(const nsAString &aID, nsCOMArray& aElements); nsresult AddElementToDocumentPre(Element* aElement); nsresult AddElementToDocumentPost(Element* aElement); nsresult ExecuteOnBroadcastHandlerFor(Element* aBroadcaster, Element* aListener, nsIAtom* aAttr); nsresult BroadcastAttributeChangeFromOverlay(nsIContent* aNode, int32_t aNameSpaceID, nsIAtom* aAttribute, nsIAtom* aPrefix, const nsAString& aValue); already_AddRefed GetWindowRoot(); static void DirectionChanged(const char* aPrefName, void* aData); // pseudo constants static int32_t gRefCnt; static nsIAtom** kIdentityAttrs[]; static nsIRDFService* gRDFService; static nsIRDFResource* kNC_persist; static nsIRDFResource* kNC_attribute; static nsIRDFResource* kNC_value; static LazyLogModule gXULLog; nsresult Persist(nsIContent* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute); // Just like Persist but ignores the return value so we can use it // as a runnable method. void DoPersist(nsIContent* aElement, int32_t aNameSpaceID, nsIAtom* aAttribute) { Persist(aElement, aNameSpaceID, aAttribute); } virtual JSObject* WrapNode(JSContext *aCx, JS::Handle aGivenProto) override; // IMPORTANT: The ownership implicit in the following member // variables has been explicitly checked and set using nsCOMPtr // for owning pointers and raw COM interface pointers for weak // (ie, non owning) references. If you add any members to this // class, please make the ownership explicit (pinkerton, scc). // NOTE, THIS IS STILL IN PROGRESS, TALK TO PINK OR SCC BEFORE // CHANGING XULDocument* mNextSrcLoadWaiter; // [OWNER] but not COMPtr // Tracks elements with a 'ref' attribute, or an 'id' attribute where // the element's namespace has no registered ID attribute name. nsTHashtable mRefMap; nsCOMPtr mLocalStore; bool mApplyingPersistedAttrs; bool mIsWritingFastLoad; bool mDocumentLoaded; /** * Since ResumeWalk is interruptible, it's possible that last * stylesheet finishes loading while the PD walk is still in * progress (waiting for an overlay to finish loading). * mStillWalking prevents DoneLoading (and StartLayout) from being * called in this situation. */ bool mStillWalking; /** * These two values control where persistent attributes get applied. */ bool mRestrictPersistence; nsTHashtable mPersistenceIds; /** * An array of style sheets, that will be added (preserving order) to the * document after all of them are loaded (in DoneWalking). */ nsTArray> mOverlaySheets; nsCOMPtr mCommandDispatcher; // [OWNER] of the focus tracker // Maintains the template builders that have been attached to // content elements typedef nsInterfaceHashtable BuilderTable; BuilderTable* mTemplateBuilderTable; uint32_t mPendingSheets; /** * document lightweight theme for use with :-moz-lwtheme, :-moz-lwtheme-brighttext * and :-moz-lwtheme-darktext */ DocumentTheme mDocLWTheme; /** * 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); }; friend class ContextStack; ContextStack mContextStack; enum State { eState_Master, eState_Overlay }; State mState; /** * An array of overlay nsIURIs that have yet to be resolved. The * order of the array is significant: overlays at the _end_ of the * array are resolved before overlays earlier in the array (i.e., * it is a stack). * * In the current implementation the order the overlays are loaded * in is as follows: first overlays from xul-overlay PIs, in the * same order as in the document, then the overlays from the chrome * registry. */ nsTArray > mUnloadedOverlays; /** * Load the transcluded script at the specified URI. If the * prototype construction must 'block' until the load has * completed, aBlock will be set to true. */ nsresult LoadScript(nsXULPrototypeScript *aScriptProto, bool* aBlock); /** * Execute the precompiled script object scoped by this XUL document's * containing window object. */ nsresult ExecuteScript(nsXULPrototypeScript *aScript); /** * 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, bool aIsRoot); /** * Create a hook-up element to which content nodes can be attached for * later resolution. */ nsresult CreateOverlayElement(nsXULPrototypeElement* aPrototype, Element** aResult); /** * Add attributes from the prototype to the element. */ nsresult AddAttributes(nsXULPrototypeElement* aPrototype, nsIContent* aElement); /** * The prototype-script of the current transcluded script that is being * loaded. For document.write('