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