michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 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 nsINode_h___ michael@0: #define nsINode_h___ michael@0: michael@0: #include "mozilla/Likely.h" michael@0: #include "nsCOMPtr.h" // for member, local michael@0: #include "nsGkAtoms.h" // for nsGkAtoms::baseURIProperty michael@0: #include "nsIDOMNode.h" michael@0: #include "nsINodeInfo.h" // member (in nsCOMPtr) michael@0: #include "nsIVariant.h" // for use in GetUserData() michael@0: #include "nsNodeInfoManager.h" // for use in NodePrincipal() michael@0: #include "nsPropertyTable.h" // for typedefs michael@0: #include "nsTObserverArray.h" // for member michael@0: #include "mozilla/ErrorResult.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/dom/EventTarget.h" // for base class michael@0: #include "js/TypeDecls.h" // for Handle, Value, JSObject, JSContext michael@0: #include "mozilla/dom/DOMString.h" michael@0: #include "mozilla/dom/BindingDeclarations.h" michael@0: michael@0: // Including 'windows.h' will #define GetClassInfo to something else. michael@0: #ifdef XP_WIN michael@0: #ifdef GetClassInfo michael@0: #undef GetClassInfo michael@0: #endif michael@0: #endif michael@0: michael@0: class nsAttrAndChildArray; michael@0: class nsChildContentList; michael@0: class nsCSSSelectorList; michael@0: class nsDOMAttributeMap; michael@0: class nsIContent; michael@0: class nsIDocument; michael@0: class nsIDOMElement; michael@0: class nsIDOMNodeList; michael@0: class nsIDOMUserDataHandler; michael@0: class nsIEditor; michael@0: class nsIFrame; michael@0: class nsIMutationObserver; michael@0: class nsINodeList; michael@0: class nsIPresShell; michael@0: class nsIPrincipal; michael@0: class nsIURI; michael@0: class nsNodeSupportsWeakRefTearoff; michael@0: class nsNodeWeakReference; michael@0: class nsXPCClassInfo; michael@0: class nsDOMMutationObserver; michael@0: michael@0: namespace mozilla { michael@0: class EventListenerManager; michael@0: namespace dom { michael@0: /** michael@0: * @return true if aChar is what the DOM spec defines as 'space character'. michael@0: * http://dom.spec.whatwg.org/#space-character michael@0: */ michael@0: inline bool IsSpaceCharacter(char16_t aChar) { michael@0: return aChar == ' ' || aChar == '\t' || aChar == '\n' || aChar == '\r' || michael@0: aChar == '\f'; michael@0: } michael@0: inline bool IsSpaceCharacter(char aChar) { michael@0: return aChar == ' ' || aChar == '\t' || aChar == '\n' || aChar == '\r' || michael@0: aChar == '\f'; michael@0: } michael@0: struct BoxQuadOptions; michael@0: struct ConvertCoordinateOptions; michael@0: class DOMPoint; michael@0: class DOMQuad; michael@0: class DOMRectReadOnly; michael@0: class Element; michael@0: class EventHandlerNonNull; michael@0: class OnErrorEventHandlerNonNull; michael@0: template class Optional; michael@0: class TextOrElementOrDocument; michael@0: struct DOMPointInit; michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: #define NODE_FLAG_BIT(n_) (1U << (WRAPPER_CACHE_FLAGS_BITS_USED + (n_))) michael@0: michael@0: enum { michael@0: // This bit will be set if the node has a listener manager. michael@0: NODE_HAS_LISTENERMANAGER = NODE_FLAG_BIT(0), michael@0: michael@0: // Whether this node has had any properties set on it michael@0: NODE_HAS_PROPERTIES = NODE_FLAG_BIT(1), michael@0: michael@0: // Whether this node is the root of an anonymous subtree. Note that this michael@0: // need not be a native anonymous subtree. Any anonymous subtree, including michael@0: // XBL-generated ones, will do. This flag is set-once: once a node has it, michael@0: // it must not be removed. michael@0: // NOTE: Should only be used on nsIContent nodes michael@0: NODE_IS_ANONYMOUS_ROOT = NODE_FLAG_BIT(2), michael@0: michael@0: // Whether the node has some ancestor, possibly itself, that is native michael@0: // anonymous. This includes ancestors crossing XBL scopes, in cases when an michael@0: // XBL binding is attached to an element which has a native anonymous michael@0: // ancestor. This flag is set-once: once a node has it, it must not be michael@0: // removed. michael@0: // NOTE: Should only be used on nsIContent nodes michael@0: NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE = NODE_FLAG_BIT(3), michael@0: michael@0: // Whether this node is the root of a native anonymous (from the perspective michael@0: // of its parent) subtree. This flag is set-once: once a node has it, it michael@0: // must not be removed. michael@0: // NOTE: Should only be used on nsIContent nodes michael@0: NODE_IS_NATIVE_ANONYMOUS_ROOT = NODE_FLAG_BIT(4), michael@0: michael@0: // Forces the XBL code to treat this node as if it were michael@0: // in the document and therefore should get bindings attached. michael@0: NODE_FORCE_XBL_BINDINGS = NODE_FLAG_BIT(5), michael@0: michael@0: // Whether a binding manager may have a pointer to this michael@0: NODE_MAY_BE_IN_BINDING_MNGR = NODE_FLAG_BIT(6), michael@0: michael@0: NODE_IS_EDITABLE = NODE_FLAG_BIT(7), michael@0: michael@0: // For all Element nodes, NODE_MAY_HAVE_CLASS is guaranteed to be set if the michael@0: // node in fact has a class, but may be set even if it doesn't. michael@0: NODE_MAY_HAVE_CLASS = NODE_FLAG_BIT(8), michael@0: michael@0: // Whether the node participates in a shadow tree. michael@0: NODE_IS_IN_SHADOW_TREE = NODE_FLAG_BIT(9), michael@0: michael@0: // Node has an :empty or :-moz-only-whitespace selector michael@0: NODE_HAS_EMPTY_SELECTOR = NODE_FLAG_BIT(10), michael@0: michael@0: // A child of the node has a selector such that any insertion, michael@0: // removal, or appending of children requires restyling the parent. michael@0: NODE_HAS_SLOW_SELECTOR = NODE_FLAG_BIT(11), michael@0: michael@0: // A child of the node has a :first-child, :-moz-first-node, michael@0: // :only-child, :last-child or :-moz-last-node selector. michael@0: NODE_HAS_EDGE_CHILD_SELECTOR = NODE_FLAG_BIT(12), michael@0: michael@0: // A child of the node has a selector such that any insertion or michael@0: // removal of children requires restyling later siblings of that michael@0: // element. Additionally (in this manner it is stronger than michael@0: // NODE_HAS_SLOW_SELECTOR), if a child's style changes due to any michael@0: // other content tree changes (e.g., the child changes to or from michael@0: // matching :empty due to a grandchild insertion or removal), the michael@0: // child's later siblings must also be restyled. michael@0: NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS = NODE_FLAG_BIT(13), michael@0: michael@0: NODE_ALL_SELECTOR_FLAGS = NODE_HAS_EMPTY_SELECTOR | michael@0: NODE_HAS_SLOW_SELECTOR | michael@0: NODE_HAS_EDGE_CHILD_SELECTOR | michael@0: NODE_HAS_SLOW_SELECTOR_LATER_SIBLINGS, michael@0: michael@0: NODE_ATTACH_BINDING_ON_POSTCREATE = NODE_FLAG_BIT(14), michael@0: michael@0: // This node needs to go through frame construction to get a frame (or michael@0: // undisplayed entry). michael@0: NODE_NEEDS_FRAME = NODE_FLAG_BIT(15), michael@0: michael@0: // At least one descendant in the flattened tree has NODE_NEEDS_FRAME set. michael@0: // This should be set on every node on the flattened tree path between the michael@0: // node(s) with NODE_NEEDS_FRAME and the root content. michael@0: NODE_DESCENDANTS_NEED_FRAMES = NODE_FLAG_BIT(16), michael@0: michael@0: // Set if the node has the accesskey attribute set. michael@0: NODE_HAS_ACCESSKEY = NODE_FLAG_BIT(17), michael@0: michael@0: // Set if the node has right-to-left directionality michael@0: NODE_HAS_DIRECTION_RTL = NODE_FLAG_BIT(18), michael@0: michael@0: // Set if the node has left-to-right directionality michael@0: NODE_HAS_DIRECTION_LTR = NODE_FLAG_BIT(19), michael@0: michael@0: NODE_ALL_DIRECTION_FLAGS = NODE_HAS_DIRECTION_LTR | michael@0: NODE_HAS_DIRECTION_RTL, michael@0: michael@0: NODE_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(20), michael@0: michael@0: NODE_IS_ROOT_OF_CHROME_ONLY_ACCESS = NODE_FLAG_BIT(21), michael@0: michael@0: // Remaining bits are node type specific. michael@0: NODE_TYPE_SPECIFIC_BITS_OFFSET = 22 michael@0: }; michael@0: michael@0: // Make sure we have space for our bits michael@0: #define ASSERT_NODE_FLAGS_SPACE(n) \ michael@0: static_assert(WRAPPER_CACHE_FLAGS_BITS_USED + (n) <= 32, \ michael@0: "Not enough space for our bits") michael@0: ASSERT_NODE_FLAGS_SPACE(NODE_TYPE_SPECIFIC_BITS_OFFSET); michael@0: michael@0: /** michael@0: * Class used to detect unexpected mutations. To use the class create an michael@0: * nsMutationGuard on the stack before unexpected mutations could occur. michael@0: * You can then at any time call Mutated to check if any unexpected mutations michael@0: * have occurred. michael@0: * michael@0: * When a guard is instantiated sMutationCount is set to 300. It is then michael@0: * decremented by every mutation (capped at 0). This means that we can only michael@0: * detect 300 mutations during the lifetime of a single guard, however that michael@0: * should be more then we ever care about as we usually only care if more then michael@0: * one mutation has occurred. michael@0: * michael@0: * When the guard goes out of scope it will adjust sMutationCount so that over michael@0: * the lifetime of the guard the guard itself has not affected sMutationCount, michael@0: * while mutations that happened while the guard was alive still will. This michael@0: * allows a guard to be instantiated even if there is another guard higher up michael@0: * on the callstack watching for mutations. michael@0: * michael@0: * The only thing that has to be avoided is for an outer guard to be used michael@0: * while an inner guard is alive. This can be avoided by only ever michael@0: * instantiating a single guard per scope and only using the guard in the michael@0: * current scope. michael@0: */ michael@0: class nsMutationGuard { michael@0: public: michael@0: nsMutationGuard() michael@0: { michael@0: mDelta = eMaxMutations - sMutationCount; michael@0: sMutationCount = eMaxMutations; michael@0: } michael@0: ~nsMutationGuard() michael@0: { michael@0: sMutationCount = michael@0: mDelta > sMutationCount ? 0 : sMutationCount - mDelta; michael@0: } michael@0: michael@0: /** michael@0: * Returns true if any unexpected mutations have occurred. You can pass in michael@0: * an 8-bit ignore count to ignore a number of expected mutations. michael@0: */ michael@0: bool Mutated(uint8_t aIgnoreCount) michael@0: { michael@0: return sMutationCount < static_cast(eMaxMutations - aIgnoreCount); michael@0: } michael@0: michael@0: // This function should be called whenever a mutation that we want to keep michael@0: // track of happen. For now this is only done when children are added or michael@0: // removed, but we might do it for attribute changes too in the future. michael@0: static void DidMutate() michael@0: { michael@0: if (sMutationCount) { michael@0: --sMutationCount; michael@0: } michael@0: } michael@0: michael@0: private: michael@0: // mDelta is the amount sMutationCount was adjusted when the guard was michael@0: // initialized. It is needed so that we can undo that adjustment once michael@0: // the guard dies. michael@0: uint32_t mDelta; michael@0: michael@0: // The value 300 is not important, as long as it is bigger then anything michael@0: // ever passed to Mutated(). michael@0: enum { eMaxMutations = 300 }; michael@0: michael@0: michael@0: // sMutationCount is a global mutation counter which is decreased by one at michael@0: // every mutation. It is capped at 0 to avoid wrapping. michael@0: // Its value is always between 0 and 300, inclusive. michael@0: static uint32_t sMutationCount; michael@0: }; michael@0: michael@0: // This should be used for any nsINode sub-class that has fields of its own michael@0: // that it needs to measure; any sub-class that doesn't use it will inherit michael@0: // SizeOfExcludingThis from its super-class. SizeOfIncludingThis() need not be michael@0: // defined, it is inherited from nsINode. michael@0: // This macro isn't actually specific to nodes, and bug 956400 will move it into MFBT. michael@0: #define NS_DECL_SIZEOF_EXCLUDING_THIS \ michael@0: virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; michael@0: michael@0: // Categories of node properties michael@0: // 0 is global. michael@0: #define DOM_USER_DATA 1 michael@0: #define DOM_USER_DATA_HANDLER 2 michael@0: #define SMIL_MAPPED_ATTR_ANIMVAL 3 michael@0: michael@0: // IID for the nsINode interface michael@0: #define NS_INODE_IID \ michael@0: { 0x77a62cd0, 0xb34f, 0x42cb, \ michael@0: { 0x94, 0x52, 0xae, 0xb2, 0x4d, 0x93, 0x2c, 0xb4 } } michael@0: michael@0: /** michael@0: * An internal interface that abstracts some DOMNode-related parts that both michael@0: * nsIContent and nsIDocument share. An instance of this interface has a list michael@0: * of nsIContent children and provides access to them. michael@0: */ michael@0: class nsINode : public mozilla::dom::EventTarget michael@0: { michael@0: public: michael@0: typedef mozilla::dom::BoxQuadOptions BoxQuadOptions; michael@0: typedef mozilla::dom::ConvertCoordinateOptions ConvertCoordinateOptions; michael@0: typedef mozilla::dom::DOMPoint DOMPoint; michael@0: typedef mozilla::dom::DOMPointInit DOMPointInit; michael@0: typedef mozilla::dom::DOMQuad DOMQuad; michael@0: typedef mozilla::dom::DOMRectReadOnly DOMRectReadOnly; michael@0: typedef mozilla::dom::TextOrElementOrDocument TextOrElementOrDocument; michael@0: typedef mozilla::ErrorResult ErrorResult; michael@0: michael@0: NS_DECLARE_STATIC_IID_ACCESSOR(NS_INODE_IID) michael@0: michael@0: // Among the sub-classes that inherit (directly or indirectly) from nsINode, michael@0: // measurement of the following members may be added later if DMD finds it is michael@0: // worthwhile: michael@0: // - nsGenericHTMLElement: mForm, mFieldSet michael@0: // - nsGenericHTMLFrameElement: mFrameLoader (bug 672539) michael@0: // - HTMLBodyElement: mContentStyleRule michael@0: // - HTMLDataListElement: mOptions michael@0: // - HTMLFieldSetElement: mElements, mDependentElements, mFirstLegend michael@0: // - HTMLFormElement: many! michael@0: // - HTMLFrameSetElement: mRowSpecs, mColSpecs michael@0: // - HTMLInputElement: mInputData, mFiles, mFileList, mStaticDocfileList michael@0: // - nsHTMLMapElement: mAreas michael@0: // - HTMLMediaElement: many! michael@0: // - nsHTMLOutputElement: mDefaultValue, mTokenList michael@0: // - nsHTMLRowElement: mCells michael@0: // - nsHTMLSelectElement: mOptions, mRestoreState michael@0: // - nsHTMLTableElement: mTBodies, mRows, mTableInheritedAttributes michael@0: // - nsHTMLTableSectionElement: mRows michael@0: // - nsHTMLTextAreaElement: mControllers, mState michael@0: // michael@0: // The following members don't need to be measured: michael@0: // - nsIContent: mPrimaryFrame, because it's non-owning and measured elsewhere michael@0: // michael@0: NS_DECL_SIZEOF_EXCLUDING_THIS michael@0: michael@0: // SizeOfIncludingThis doesn't need to be overridden by sub-classes because michael@0: // sub-classes of nsINode are guaranteed to be laid out in memory in such a michael@0: // way that |this| points to the start of the allocated object, even in michael@0: // methods of nsINode's sub-classes, and so |aMallocSizeOf(this)| is always michael@0: // safe to call no matter which object it was invoked on. michael@0: virtual size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const { michael@0: return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); michael@0: } michael@0: michael@0: friend class nsNodeUtils; michael@0: friend class nsNodeWeakReference; michael@0: friend class nsNodeSupportsWeakRefTearoff; michael@0: friend class nsAttrAndChildArray; michael@0: michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: nsINode(already_AddRefed& aNodeInfo) michael@0: : mNodeInfo(aNodeInfo), michael@0: mParent(nullptr), michael@0: mBoolFlags(0), michael@0: mNextSibling(nullptr), michael@0: mPreviousSibling(nullptr), michael@0: mFirstChild(nullptr), michael@0: mSubtreeRoot(MOZ_THIS_IN_INITIALIZER_LIST()), michael@0: mSlots(nullptr) michael@0: { michael@0: SetIsDOMBinding(); michael@0: } michael@0: #endif michael@0: michael@0: virtual ~nsINode(); michael@0: michael@0: /** michael@0: * Bit-flags to pass (or'ed together) to IsNodeOfType() michael@0: */ michael@0: enum { michael@0: /** nsIContent nodes */ michael@0: eCONTENT = 1 << 0, michael@0: /** nsIDocument nodes */ michael@0: eDOCUMENT = 1 << 1, michael@0: /** nsIAttribute nodes */ michael@0: eATTRIBUTE = 1 << 2, michael@0: /** text nodes */ michael@0: eTEXT = 1 << 3, michael@0: /** xml processing instructions */ michael@0: ePROCESSING_INSTRUCTION = 1 << 4, michael@0: /** comment nodes */ michael@0: eCOMMENT = 1 << 5, michael@0: /** form control elements */ michael@0: eHTML_FORM_CONTROL = 1 << 6, michael@0: /** document fragments */ michael@0: eDOCUMENT_FRAGMENT = 1 << 7, michael@0: /** data nodes (comments, PIs, text). Nodes of this type always michael@0: returns a non-null value for nsIContent::GetText() */ michael@0: eDATA_NODE = 1 << 8, michael@0: /** HTMLMediaElement */ michael@0: eMEDIA = 1 << 9, michael@0: /** animation elements */ michael@0: eANIMATION = 1 << 10, michael@0: /** filter elements that implement SVGFilterPrimitiveStandardAttributes */ michael@0: eFILTER = 1 << 11 michael@0: }; michael@0: michael@0: /** michael@0: * API for doing a quick check if a content is of a given michael@0: * type, such as Text, Document, Comment ... Use this when you can instead of michael@0: * checking the tag. michael@0: * michael@0: * @param aFlags what types you want to test for (see above) michael@0: * @return whether the content matches ALL flags passed in michael@0: */ michael@0: virtual bool IsNodeOfType(uint32_t aFlags) const = 0; michael@0: michael@0: virtual JSObject* WrapObject(JSContext *aCx) MOZ_OVERRIDE; michael@0: michael@0: protected: michael@0: /** michael@0: * WrapNode is called from WrapObject to actually wrap this node, WrapObject michael@0: * does some additional checks and fix-up that's common to all nodes. WrapNode michael@0: * should just call the DOM binding's Wrap function. michael@0: */ michael@0: virtual JSObject* WrapNode(JSContext *aCx) michael@0: { michael@0: MOZ_ASSERT(!IsDOMBinding(), "Someone forgot to override WrapNode"); michael@0: return nullptr; michael@0: } michael@0: michael@0: // Subclasses that wish to override the parent behavior should return the michael@0: // result of GetParentObjectIntenral, which handles the XBL scope stuff. michael@0: // michael@0: mozilla::dom::ParentObject GetParentObjectInternal(nsINode* aNativeParent) const { michael@0: mozilla::dom::ParentObject p(aNativeParent); michael@0: // Note that mUseXBLScope is a no-op for chrome, and other places where we michael@0: // don't use XBL scopes. michael@0: p.mUseXBLScope = IsInAnonymousSubtree() && !IsAnonymousContentInSVGUseSubtree(); michael@0: return p; michael@0: } michael@0: michael@0: public: michael@0: mozilla::dom::ParentObject GetParentObject() const; // Implemented in nsIDocument.h michael@0: michael@0: /** michael@0: * Return whether the node is an Element node michael@0: */ michael@0: bool IsElement() const { michael@0: return GetBoolFlag(NodeIsElement); michael@0: } michael@0: michael@0: /** michael@0: * Return this node as an Element. Should only be used for nodes michael@0: * for which IsElement() is true. This is defined inline in Element.h. michael@0: */ michael@0: mozilla::dom::Element* AsElement(); michael@0: const mozilla::dom::Element* AsElement() const; michael@0: michael@0: /** michael@0: * Return this node as nsIContent. Should only be used for nodes for which michael@0: * IsContent() is true. This is defined inline in nsIContent.h. michael@0: */ michael@0: nsIContent* AsContent(); michael@0: const nsIContent* AsContent() const michael@0: { michael@0: return const_cast(this)->AsContent(); michael@0: } michael@0: michael@0: virtual nsIDOMNode* AsDOMNode() = 0; michael@0: michael@0: /** michael@0: * Return if this node has any children. michael@0: */ michael@0: bool HasChildren() const { return !!mFirstChild; } michael@0: michael@0: /** michael@0: * Get the number of children michael@0: * @return the number of children michael@0: */ michael@0: virtual uint32_t GetChildCount() const = 0; michael@0: michael@0: /** michael@0: * Get a child by index michael@0: * @param aIndex the index of the child to get michael@0: * @return the child, or null if index out of bounds michael@0: */ michael@0: virtual nsIContent* GetChildAt(uint32_t aIndex) const = 0; michael@0: michael@0: /** michael@0: * Get a raw pointer to the child array. This should only be used if you michael@0: * plan to walk a bunch of the kids, promise to make sure that nothing ever michael@0: * mutates (no attribute changes, not DOM tree changes, no script execution, michael@0: * NOTHING), and will never ever peform an out-of-bounds access here. This michael@0: * method may return null if there are no children, or it may return a michael@0: * garbage pointer. In all cases the out param will be set to the number of michael@0: * children. michael@0: */ michael@0: virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const = 0; michael@0: michael@0: /** michael@0: * Get the index of a child within this content michael@0: * @param aPossibleChild the child to get the index of. michael@0: * @return the index of the child, or -1 if not a child michael@0: * michael@0: * If the return value is not -1, then calling GetChildAt() with that value michael@0: * will return aPossibleChild. michael@0: */ michael@0: virtual int32_t IndexOf(const nsINode* aPossibleChild) const = 0; michael@0: michael@0: /** michael@0: * Return the "owner document" of this node. Note that this is not the same michael@0: * as the DOM ownerDocument -- that's null for Document nodes, whereas for a michael@0: * nsIDocument GetOwnerDocument returns the document itself. For nsIContent michael@0: * implementations the two are the same. michael@0: */ michael@0: nsIDocument *OwnerDoc() const michael@0: { michael@0: return mNodeInfo->GetDocument(); michael@0: } michael@0: michael@0: /** michael@0: * Return the "owner document" of this node as an nsINode*. Implemented michael@0: * in nsIDocument.h. michael@0: */ michael@0: nsINode *OwnerDocAsNode() const; michael@0: michael@0: /** michael@0: * Returns true if the content has an ancestor that is a document. michael@0: * michael@0: * @return whether this content is in a document tree michael@0: */ michael@0: bool IsInDoc() const michael@0: { michael@0: return GetBoolFlag(IsInDocument); michael@0: } michael@0: michael@0: /** michael@0: * Get the document that this content is currently in, if any. This will be michael@0: * null if the content has no ancestor that is a document. michael@0: * michael@0: * @return the current document michael@0: */ michael@0: nsIDocument *GetCurrentDoc() const michael@0: { michael@0: return IsInDoc() ? OwnerDoc() : nullptr; michael@0: } michael@0: michael@0: /** michael@0: * The values returned by this function are the ones defined for michael@0: * nsIDOMNode.nodeType michael@0: */ michael@0: uint16_t NodeType() const michael@0: { michael@0: return mNodeInfo->NodeType(); michael@0: } michael@0: const nsString& NodeName() const michael@0: { michael@0: return mNodeInfo->NodeName(); michael@0: } michael@0: const nsString& LocalName() const michael@0: { michael@0: return mNodeInfo->LocalName(); michael@0: } michael@0: michael@0: /** michael@0: * Get the tag for this element. This will always return a non-null atom michael@0: * pointer (as implied by the naming of the method). For elements this is michael@0: * the non-namespaced tag, and for other nodes it's something like "#text", michael@0: * "#comment", "#document", etc. michael@0: */ michael@0: nsIAtom* Tag() const michael@0: { michael@0: return mNodeInfo->NameAtom(); michael@0: } michael@0: michael@0: /** michael@0: * Insert a content node at a particular index. This method handles calling michael@0: * BindToTree on the child appropriately. michael@0: * michael@0: * @param aKid the content to insert michael@0: * @param aIndex the index it is being inserted at (the index it will have michael@0: * after it is inserted) michael@0: * @param aNotify whether to notify the document (current document for michael@0: * nsIContent, and |this| for nsIDocument) that the insert has michael@0: * occurred michael@0: * michael@0: * @throws NS_ERROR_DOM_HIERARCHY_REQUEST_ERR if one attempts to have more michael@0: * than one element node as a child of a document. Doing this will also michael@0: * assert -- you shouldn't be doing it! Check with michael@0: * nsIDocument::GetRootElement() first if you're not sure. Apart from this michael@0: * one constraint, this doesn't do any checking on whether aKid is a valid michael@0: * child of |this|. michael@0: * michael@0: * @throws NS_ERROR_OUT_OF_MEMORY in some cases (from BindToTree). michael@0: */ michael@0: virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, michael@0: bool aNotify) = 0; michael@0: michael@0: /** michael@0: * Append a content node to the end of the child list. This method handles michael@0: * calling BindToTree on the child appropriately. michael@0: * michael@0: * @param aKid the content to append michael@0: * @param aNotify whether to notify the document (current document for michael@0: * nsIContent, and |this| for nsIDocument) that the append has michael@0: * occurred michael@0: * michael@0: * @throws NS_ERROR_DOM_HIERARCHY_REQUEST_ERR if one attempts to have more michael@0: * than one element node as a child of a document. Doing this will also michael@0: * assert -- you shouldn't be doing it! Check with michael@0: * nsIDocument::GetRootElement() first if you're not sure. Apart from this michael@0: * one constraint, this doesn't do any checking on whether aKid is a valid michael@0: * child of |this|. michael@0: * michael@0: * @throws NS_ERROR_OUT_OF_MEMORY in some cases (from BindToTree). michael@0: */ michael@0: nsresult AppendChildTo(nsIContent* aKid, bool aNotify) michael@0: { michael@0: return InsertChildAt(aKid, GetChildCount(), aNotify); michael@0: } michael@0: michael@0: /** michael@0: * Remove a child from this node. This method handles calling UnbindFromTree michael@0: * on the child appropriately. michael@0: * michael@0: * @param aIndex the index of the child to remove michael@0: * @param aNotify whether to notify the document (current document for michael@0: * nsIContent, and |this| for nsIDocument) that the remove has michael@0: * occurred michael@0: * michael@0: * Note: If there is no child at aIndex, this method will simply do nothing. michael@0: */ michael@0: virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) = 0; michael@0: michael@0: /** michael@0: * Get a property associated with this node. michael@0: * michael@0: * @param aPropertyName name of property to get. michael@0: * @param aStatus out parameter for storing resulting status. michael@0: * Set to NS_PROPTABLE_PROP_NOT_THERE if the property michael@0: * is not set. michael@0: * @return the property. Null if the property is not set michael@0: * (though a null return value does not imply the michael@0: * property was not set, i.e. it can be set to null). michael@0: */ michael@0: void* GetProperty(nsIAtom *aPropertyName, michael@0: nsresult *aStatus = nullptr) const michael@0: { michael@0: return GetProperty(0, aPropertyName, aStatus); michael@0: } michael@0: michael@0: /** michael@0: * Get a property associated with this node. michael@0: * michael@0: * @param aCategory category of property to get. michael@0: * @param aPropertyName name of property to get. michael@0: * @param aStatus out parameter for storing resulting status. michael@0: * Set to NS_PROPTABLE_PROP_NOT_THERE if the property michael@0: * is not set. michael@0: * @return the property. Null if the property is not set michael@0: * (though a null return value does not imply the michael@0: * property was not set, i.e. it can be set to null). michael@0: */ michael@0: virtual void* GetProperty(uint16_t aCategory, michael@0: nsIAtom *aPropertyName, michael@0: nsresult *aStatus = nullptr) const; michael@0: michael@0: /** michael@0: * Set a property to be associated with this node. This will overwrite an michael@0: * existing value if one exists. The existing value is destroyed using the michael@0: * destructor function given when that value was set. michael@0: * michael@0: * @param aPropertyName name of property to set. michael@0: * @param aValue new value of property. michael@0: * @param aDtor destructor function to be used when this property michael@0: * is destroyed. michael@0: * @param aTransfer if true the property will not be deleted when the michael@0: * ownerDocument of the node changes, if false it michael@0: * will be deleted. michael@0: * michael@0: * @return NS_PROPTABLE_PROP_OVERWRITTEN (success value) if the property michael@0: * was already set michael@0: * @throws NS_ERROR_OUT_OF_MEMORY if that occurs michael@0: */ michael@0: nsresult SetProperty(nsIAtom *aPropertyName, michael@0: void *aValue, michael@0: NSPropertyDtorFunc aDtor = nullptr, michael@0: bool aTransfer = false) michael@0: { michael@0: return SetProperty(0, aPropertyName, aValue, aDtor, aTransfer); michael@0: } michael@0: michael@0: /** michael@0: * Set a property to be associated with this node. This will overwrite an michael@0: * existing value if one exists. The existing value is destroyed using the michael@0: * destructor function given when that value was set. michael@0: * michael@0: * @param aCategory category of property to set. michael@0: * @param aPropertyName name of property to set. michael@0: * @param aValue new value of property. michael@0: * @param aDtor destructor function to be used when this property michael@0: * is destroyed. michael@0: * @param aTransfer if true the property will not be deleted when the michael@0: * ownerDocument of the node changes, if false it michael@0: * will be deleted. michael@0: * @param aOldValue [out] previous value of property. michael@0: * michael@0: * @return NS_PROPTABLE_PROP_OVERWRITTEN (success value) if the property michael@0: * was already set michael@0: * @throws NS_ERROR_OUT_OF_MEMORY if that occurs michael@0: */ michael@0: virtual nsresult SetProperty(uint16_t aCategory, michael@0: nsIAtom *aPropertyName, michael@0: void *aValue, michael@0: NSPropertyDtorFunc aDtor = nullptr, michael@0: bool aTransfer = false, michael@0: void **aOldValue = nullptr); michael@0: michael@0: /** michael@0: * A generic destructor for property values allocated with new. michael@0: */ michael@0: template michael@0: static void DeleteProperty(void *, nsIAtom *, void *aPropertyValue, void *) michael@0: { michael@0: delete static_cast(aPropertyValue); michael@0: } michael@0: michael@0: /** michael@0: * Destroys a property associated with this node. The value is destroyed michael@0: * using the destruction function given when that value was set. michael@0: * michael@0: * @param aPropertyName name of property to destroy. michael@0: */ michael@0: void DeleteProperty(nsIAtom *aPropertyName) michael@0: { michael@0: DeleteProperty(0, aPropertyName); michael@0: } michael@0: michael@0: /** michael@0: * Destroys a property associated with this node. The value is destroyed michael@0: * using the destruction function given when that value was set. michael@0: * michael@0: * @param aCategory category of property to destroy. michael@0: * @param aPropertyName name of property to destroy. michael@0: */ michael@0: virtual void DeleteProperty(uint16_t aCategory, nsIAtom *aPropertyName); michael@0: michael@0: /** michael@0: * Unset a property associated with this node. The value will not be michael@0: * destroyed but rather returned. It is the caller's responsibility to michael@0: * destroy the value after that point. michael@0: * michael@0: * @param aPropertyName name of property to unset. michael@0: * @param aStatus out parameter for storing resulting status. michael@0: * Set to NS_PROPTABLE_PROP_NOT_THERE if the property michael@0: * is not set. michael@0: * @return the property. Null if the property is not set michael@0: * (though a null return value does not imply the michael@0: * property was not set, i.e. it can be set to null). michael@0: */ michael@0: void* UnsetProperty(nsIAtom *aPropertyName, michael@0: nsresult *aStatus = nullptr) michael@0: { michael@0: return UnsetProperty(0, aPropertyName, aStatus); michael@0: } michael@0: michael@0: /** michael@0: * Unset a property associated with this node. The value will not be michael@0: * destroyed but rather returned. It is the caller's responsibility to michael@0: * destroy the value after that point. michael@0: * michael@0: * @param aCategory category of property to unset. michael@0: * @param aPropertyName name of property to unset. michael@0: * @param aStatus out parameter for storing resulting status. michael@0: * Set to NS_PROPTABLE_PROP_NOT_THERE if the property michael@0: * is not set. michael@0: * @return the property. Null if the property is not set michael@0: * (though a null return value does not imply the michael@0: * property was not set, i.e. it can be set to null). michael@0: */ michael@0: virtual void* UnsetProperty(uint16_t aCategory, michael@0: nsIAtom *aPropertyName, michael@0: nsresult *aStatus = nullptr); michael@0: michael@0: bool HasProperties() const michael@0: { michael@0: return HasFlag(NODE_HAS_PROPERTIES); michael@0: } michael@0: michael@0: /** michael@0: * Return the principal of this node. This is guaranteed to never be a null michael@0: * pointer. michael@0: */ michael@0: nsIPrincipal* NodePrincipal() const { michael@0: return mNodeInfo->NodeInfoManager()->DocumentPrincipal(); michael@0: } michael@0: michael@0: /** michael@0: * Get the parent nsIContent for this node. michael@0: * @return the parent, or null if no parent or the parent is not an nsIContent michael@0: */ michael@0: nsIContent* GetParent() const { michael@0: return MOZ_LIKELY(GetBoolFlag(ParentIsContent)) ? michael@0: reinterpret_cast(mParent) : nullptr; michael@0: } michael@0: michael@0: /** michael@0: * Get the parent nsINode for this node. This can be either an nsIContent, michael@0: * an nsIDocument or an nsIAttribute. michael@0: * @return the parent node michael@0: */ michael@0: nsINode* GetParentNode() const michael@0: { michael@0: return mParent; michael@0: } michael@0: michael@0: /** michael@0: * Get the parent nsINode for this node if it is an Element. michael@0: * @return the parent node michael@0: */ michael@0: mozilla::dom::Element* GetParentElement() const michael@0: { michael@0: return mParent && mParent->IsElement() ? mParent->AsElement() : nullptr; michael@0: } michael@0: michael@0: /** michael@0: * Get the root of the subtree this node belongs to. This never returns michael@0: * null. It may return 'this' (e.g. for document nodes, and nodes that michael@0: * are the roots of disconnected subtrees). michael@0: */ michael@0: nsINode* SubtreeRoot() const michael@0: { michael@0: // There are three cases of interest here. nsINodes that are really: michael@0: // 1. nsIDocument nodes - Are always in the document. michael@0: // 2. nsIContent nodes - Are either in the document, or mSubtreeRoot michael@0: // is updated in BindToTree/UnbindFromTree. michael@0: // 3. nsIAttribute nodes - Are never in the document, and mSubtreeRoot michael@0: // is always 'this' (as set in nsINode's ctor). michael@0: nsINode* node = IsInDoc() ? OwnerDocAsNode() : mSubtreeRoot; michael@0: NS_ASSERTION(node, "Should always have a node here!"); michael@0: #ifdef DEBUG michael@0: { michael@0: const nsINode* slowNode = this; michael@0: const nsINode* iter = slowNode; michael@0: while ((iter = iter->GetParentNode())) { michael@0: slowNode = iter; michael@0: } michael@0: michael@0: NS_ASSERTION(slowNode == node, "These should always be in sync!"); michael@0: } michael@0: #endif michael@0: return node; michael@0: } michael@0: michael@0: /** michael@0: * See nsIDOMEventTarget michael@0: */ michael@0: NS_DECL_NSIDOMEVENTTARGET michael@0: michael@0: virtual mozilla::EventListenerManager* michael@0: GetExistingListenerManager() const MOZ_OVERRIDE; michael@0: virtual mozilla::EventListenerManager* michael@0: GetOrCreateListenerManager() MOZ_OVERRIDE; michael@0: michael@0: using mozilla::dom::EventTarget::RemoveEventListener; michael@0: using nsIDOMEventTarget::AddEventListener; michael@0: virtual void AddEventListener(const nsAString& aType, michael@0: mozilla::dom::EventListener* aListener, michael@0: bool aUseCapture, michael@0: const mozilla::dom::Nullable& aWantsUntrusted, michael@0: mozilla::ErrorResult& aRv) MOZ_OVERRIDE; michael@0: using nsIDOMEventTarget::AddSystemEventListener; michael@0: virtual nsIDOMWindow* GetOwnerGlobal() MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Adds a mutation observer to be notified when this node, or any of its michael@0: * descendants, are modified. The node will hold a weak reference to the michael@0: * observer, which means that it is the responsibility of the observer to michael@0: * remove itself in case it dies before the node. If an observer is added michael@0: * while observers are being notified, it may also be notified. In general, michael@0: * adding observers while inside a notification is not a good idea. An michael@0: * observer that is already observing the node must not be added without michael@0: * being removed first. michael@0: */ michael@0: void AddMutationObserver(nsIMutationObserver* aMutationObserver) michael@0: { michael@0: nsSlots* s = Slots(); michael@0: NS_ASSERTION(s->mMutationObservers.IndexOf(aMutationObserver) == michael@0: nsTArray::NoIndex, michael@0: "Observer already in the list"); michael@0: s->mMutationObservers.AppendElement(aMutationObserver); michael@0: } michael@0: michael@0: /** michael@0: * Same as above, but only adds the observer if its not observing michael@0: * the node already. michael@0: */ michael@0: void AddMutationObserverUnlessExists(nsIMutationObserver* aMutationObserver) michael@0: { michael@0: nsSlots* s = Slots(); michael@0: s->mMutationObservers.AppendElementUnlessExists(aMutationObserver); michael@0: } michael@0: michael@0: /** michael@0: * Removes a mutation observer. michael@0: */ michael@0: void RemoveMutationObserver(nsIMutationObserver* aMutationObserver) michael@0: { michael@0: nsSlots* s = GetExistingSlots(); michael@0: if (s) { michael@0: s->mMutationObservers.RemoveElement(aMutationObserver); michael@0: } michael@0: } michael@0: michael@0: /** michael@0: * Clones this node. This needs to be overriden by all node classes. aNodeInfo michael@0: * should be identical to this node's nodeInfo, except for the document which michael@0: * may be different. When cloning an element, all attributes of the element michael@0: * will be cloned. The children of the node will not be cloned. michael@0: * michael@0: * @param aNodeInfo the nodeinfo to use for the clone michael@0: * @param aResult the clone michael@0: */ michael@0: virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const = 0; michael@0: michael@0: // This class can be extended by subclasses that wish to store more michael@0: // information in the slots. michael@0: class nsSlots michael@0: { michael@0: public: michael@0: nsSlots() michael@0: : mChildNodes(nullptr), michael@0: mWeakReference(nullptr) michael@0: { michael@0: } michael@0: michael@0: // If needed we could remove the vtable pointer this dtor causes by michael@0: // putting a DestroySlots function on nsINode michael@0: virtual ~nsSlots(); michael@0: michael@0: void Traverse(nsCycleCollectionTraversalCallback &cb); michael@0: void Unlink(); michael@0: michael@0: /** michael@0: * A list of mutation observers michael@0: */ michael@0: nsTObserverArray mMutationObservers; michael@0: michael@0: /** michael@0: * An object implementing nsIDOMNodeList for this content (childNodes) michael@0: * @see nsIDOMNodeList michael@0: * @see nsGenericHTMLElement::GetChildNodes michael@0: * michael@0: * MSVC 7 doesn't like this as an nsRefPtr michael@0: */ michael@0: nsChildContentList* mChildNodes; michael@0: michael@0: /** michael@0: * Weak reference to this node michael@0: */ michael@0: nsNodeWeakReference* mWeakReference; michael@0: }; michael@0: michael@0: /** michael@0: * Functions for managing flags and slots michael@0: */ michael@0: #ifdef DEBUG michael@0: nsSlots* DebugGetSlots() michael@0: { michael@0: return Slots(); michael@0: } michael@0: #endif michael@0: michael@0: void SetFlags(uint32_t aFlagsToSet) michael@0: { michael@0: NS_ASSERTION(!(aFlagsToSet & (NODE_IS_ANONYMOUS_ROOT | michael@0: NODE_IS_NATIVE_ANONYMOUS_ROOT | michael@0: NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE | michael@0: NODE_ATTACH_BINDING_ON_POSTCREATE | michael@0: NODE_DESCENDANTS_NEED_FRAMES | michael@0: NODE_NEEDS_FRAME | michael@0: NODE_CHROME_ONLY_ACCESS)) || michael@0: IsNodeOfType(eCONTENT), michael@0: "Flag only permitted on nsIContent nodes"); michael@0: nsWrapperCache::SetFlags(aFlagsToSet); michael@0: } michael@0: michael@0: void UnsetFlags(uint32_t aFlagsToUnset) michael@0: { michael@0: NS_ASSERTION(!(aFlagsToUnset & michael@0: (NODE_IS_ANONYMOUS_ROOT | michael@0: NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE | michael@0: NODE_IS_NATIVE_ANONYMOUS_ROOT)), michael@0: "Trying to unset write-only flags"); michael@0: nsWrapperCache::UnsetFlags(aFlagsToUnset); michael@0: } michael@0: michael@0: void SetEditableFlag(bool aEditable) michael@0: { michael@0: if (aEditable) { michael@0: SetFlags(NODE_IS_EDITABLE); michael@0: } michael@0: else { michael@0: UnsetFlags(NODE_IS_EDITABLE); michael@0: } michael@0: } michael@0: michael@0: bool IsEditable() const michael@0: { michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: return IsEditableInternal(); michael@0: #else michael@0: return IsEditableExternal(); michael@0: #endif michael@0: } michael@0: michael@0: /** michael@0: * Returns true if |this| or any of its ancestors is native anonymous. michael@0: */ michael@0: bool IsInNativeAnonymousSubtree() const michael@0: { michael@0: #ifdef DEBUG michael@0: if (HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE)) { michael@0: return true; michael@0: } michael@0: CheckNotNativeAnonymous(); michael@0: return false; michael@0: #else michael@0: return HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE); michael@0: #endif michael@0: } michael@0: michael@0: bool IsInAnonymousSubtree() const; michael@0: michael@0: // Note: This asserts |IsInAnonymousSubtree()|. michael@0: bool IsAnonymousContentInSVGUseSubtree() const; michael@0: michael@0: // True for native anonymous content and for XBL content if the binging michael@0: // has chromeOnlyContent="true". michael@0: bool ChromeOnlyAccess() const michael@0: { michael@0: return HasFlag(NODE_IS_IN_NATIVE_ANONYMOUS_SUBTREE | NODE_CHROME_ONLY_ACCESS); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if |this| node is the common ancestor of the start/end michael@0: * nodes of a Range in a Selection or a descendant of such a common ancestor. michael@0: * This node is definitely not selected when |false| is returned, but it may michael@0: * or may not be selected when |true| is returned. michael@0: */ michael@0: bool IsSelectionDescendant() const michael@0: { michael@0: return IsDescendantOfCommonAncestorForRangeInSelection() || michael@0: IsCommonAncestorForRangeInSelection(); michael@0: } michael@0: michael@0: /** michael@0: * Get the root content of an editor. So, this node must be a descendant of michael@0: * an editor. Note that this should be only used for getting input or textarea michael@0: * editor's root content. This method doesn't support HTML editors. michael@0: */ michael@0: nsIContent* GetTextEditorRootContent(nsIEditor** aEditor = nullptr); michael@0: michael@0: /** michael@0: * Get the nearest selection root, ie. the node that will be selected if the michael@0: * user does "Select All" while the focus is in this node. Note that if this michael@0: * node is not in an editor, the result comes from the nsFrameSelection that michael@0: * is related to aPresShell, so the result might not be the ancestor of this michael@0: * node. Be aware that if this node and the computed selection limiter are michael@0: * not in same subtree, this returns the root content of the closeset subtree. michael@0: */ michael@0: nsIContent* GetSelectionRootContent(nsIPresShell* aPresShell); michael@0: michael@0: virtual nsINodeList* ChildNodes(); michael@0: nsIContent* GetFirstChild() const { return mFirstChild; } michael@0: nsIContent* GetLastChild() const michael@0: { michael@0: uint32_t count; michael@0: nsIContent* const* children = GetChildArray(&count); michael@0: michael@0: return count > 0 ? children[count - 1] : nullptr; michael@0: } michael@0: michael@0: /** michael@0: * Implementation is in nsIDocument.h, because it needs to cast from michael@0: * nsIDocument* to nsINode*. michael@0: */ michael@0: nsIDocument* GetOwnerDocument() const; michael@0: michael@0: void Normalize(); michael@0: michael@0: /** michael@0: * Get the base URI for any relative URIs within this piece of michael@0: * content. Generally, this is the document's base URI, but certain michael@0: * content carries a local base for backward compatibility, and XML michael@0: * supports setting a per-node base URI. michael@0: * michael@0: * @return the base URI michael@0: */ michael@0: virtual already_AddRefed GetBaseURI(bool aTryUseXHRDocBaseURI = false) const = 0; michael@0: already_AddRefed GetBaseURIObject() const; michael@0: michael@0: /** michael@0: * Facility for explicitly setting a base URI on a node. michael@0: */ michael@0: nsresult SetExplicitBaseURI(nsIURI* aURI); michael@0: /** michael@0: * The explicit base URI, if set, otherwise null michael@0: */ michael@0: protected: michael@0: nsIURI* GetExplicitBaseURI() const { michael@0: if (HasExplicitBaseURI()) { michael@0: return static_cast(GetProperty(nsGkAtoms::baseURIProperty)); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: public: michael@0: void GetTextContent(nsAString& aTextContent) michael@0: { michael@0: GetTextContentInternal(aTextContent); michael@0: } michael@0: void SetTextContent(const nsAString& aTextContent, michael@0: mozilla::ErrorResult& aError) michael@0: { michael@0: SetTextContentInternal(aTextContent, aError); michael@0: } michael@0: michael@0: mozilla::dom::Element* QuerySelector(const nsAString& aSelector, michael@0: mozilla::ErrorResult& aResult); michael@0: already_AddRefed QuerySelectorAll(const nsAString& aSelector, michael@0: mozilla::ErrorResult& aResult); michael@0: michael@0: nsresult QuerySelector(const nsAString& aSelector, nsIDOMElement **aReturn); michael@0: nsresult QuerySelectorAll(const nsAString& aSelector, nsIDOMNodeList **aReturn); michael@0: michael@0: protected: michael@0: // nsIDocument overrides this with its own (faster) version. This michael@0: // should really only be called for elements and document fragments. michael@0: mozilla::dom::Element* GetElementById(const nsAString& aId); michael@0: michael@0: public: michael@0: /** michael@0: * Associate an object aData to aKey on this node. If aData is null any michael@0: * previously registered object and UserDataHandler associated to aKey on michael@0: * this node will be removed. michael@0: * Should only be used to implement the DOM Level 3 UserData API. michael@0: * michael@0: * @param aKey the key to associate the object to michael@0: * @param aData the object to associate to aKey on this node (may be null) michael@0: * @param aHandler the UserDataHandler to call when the node is michael@0: * cloned/deleted/imported/renamed (may be null) michael@0: * @param aResult [out] the previously registered object for aKey on this michael@0: * node, if any michael@0: * @return whether adding the object and UserDataHandler succeeded michael@0: */ michael@0: nsresult SetUserData(const nsAString& aKey, nsIVariant* aData, michael@0: nsIDOMUserDataHandler* aHandler, nsIVariant** aResult); michael@0: michael@0: /** michael@0: * Get the UserData object registered for a Key on this node, if any. michael@0: * Should only be used to implement the DOM Level 3 UserData API. michael@0: * michael@0: * @param aKey the key to get UserData for michael@0: * @return aResult the previously registered object for aKey on this node, if michael@0: * any michael@0: */ michael@0: nsIVariant* GetUserData(const nsAString& aKey); michael@0: michael@0: nsresult GetUserData(const nsAString& aKey, nsIVariant** aResult) michael@0: { michael@0: NS_IF_ADDREF(*aResult = GetUserData(aKey)); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void LookupPrefix(const nsAString& aNamespace, nsAString& aResult); michael@0: bool IsDefaultNamespace(const nsAString& aNamespaceURI) michael@0: { michael@0: nsAutoString defaultNamespace; michael@0: LookupNamespaceURI(EmptyString(), defaultNamespace); michael@0: return aNamespaceURI.Equals(defaultNamespace); michael@0: } michael@0: void LookupNamespaceURI(const nsAString& aNamespacePrefix, michael@0: nsAString& aNamespaceURI); michael@0: michael@0: nsresult IsEqualNode(nsIDOMNode* aOther, bool* aReturn); michael@0: michael@0: nsIContent* GetNextSibling() const { return mNextSibling; } michael@0: nsIContent* GetPreviousSibling() const { return mPreviousSibling; } michael@0: michael@0: /** michael@0: * Get the next node in the pre-order tree traversal of the DOM. If michael@0: * aRoot is non-null, then it must be an ancestor of |this| michael@0: * (possibly equal to |this|) and only nodes that are descendants of michael@0: * aRoot, not including aRoot itself, will be returned. Returns michael@0: * null if there are no more nodes to traverse. michael@0: */ michael@0: nsIContent* GetNextNode(const nsINode* aRoot = nullptr) const michael@0: { michael@0: return GetNextNodeImpl(aRoot, false); michael@0: } michael@0: michael@0: /** michael@0: * Get the next node in the pre-order tree traversal of the DOM but ignoring michael@0: * the children of this node. If aRoot is non-null, then it must be an michael@0: * ancestor of |this| (possibly equal to |this|) and only nodes that are michael@0: * descendants of aRoot, not including aRoot itself, will be returned. michael@0: * Returns null if there are no more nodes to traverse. michael@0: */ michael@0: nsIContent* GetNextNonChildNode(const nsINode* aRoot = nullptr) const michael@0: { michael@0: return GetNextNodeImpl(aRoot, true); michael@0: } michael@0: michael@0: /** michael@0: * Returns true if 'this' is either document or element or michael@0: * document fragment and aOther is a descendant in the same michael@0: * anonymous tree. michael@0: */ michael@0: bool Contains(const nsINode* aOther) const; michael@0: nsresult Contains(nsIDOMNode* aOther, bool* aReturn); michael@0: michael@0: bool UnoptimizableCCNode() const; michael@0: michael@0: private: michael@0: michael@0: nsIContent* GetNextNodeImpl(const nsINode* aRoot, michael@0: const bool aSkipChildren) const michael@0: { michael@0: // Can't use nsContentUtils::ContentIsDescendantOf here, since we michael@0: // can't include it here. michael@0: #ifdef DEBUG michael@0: if (aRoot) { michael@0: const nsINode* cur = this; michael@0: for (; cur; cur = cur->GetParentNode()) michael@0: if (cur == aRoot) break; michael@0: NS_ASSERTION(cur, "aRoot not an ancestor of |this|?"); michael@0: } michael@0: #endif michael@0: if (!aSkipChildren) { michael@0: nsIContent* kid = GetFirstChild(); michael@0: if (kid) { michael@0: return kid; michael@0: } michael@0: } michael@0: if (this == aRoot) { michael@0: return nullptr; michael@0: } michael@0: const nsINode* cur = this; michael@0: while (1) { michael@0: nsIContent* next = cur->GetNextSibling(); michael@0: if (next) { michael@0: return next; michael@0: } michael@0: nsINode* parent = cur->GetParentNode(); michael@0: if (parent == aRoot) { michael@0: return nullptr; michael@0: } michael@0: cur = parent; michael@0: } michael@0: NS_NOTREACHED("How did we get here?"); michael@0: } michael@0: michael@0: public: michael@0: michael@0: /** michael@0: * Get the previous nsIContent in the pre-order tree traversal of the DOM. If michael@0: * aRoot is non-null, then it must be an ancestor of |this| michael@0: * (possibly equal to |this|) and only nsIContents that are descendants of michael@0: * aRoot, including aRoot itself, will be returned. Returns michael@0: * null if there are no more nsIContents to traverse. michael@0: */ michael@0: nsIContent* GetPreviousContent(const nsINode* aRoot = nullptr) const michael@0: { michael@0: // Can't use nsContentUtils::ContentIsDescendantOf here, since we michael@0: // can't include it here. michael@0: #ifdef DEBUG michael@0: if (aRoot) { michael@0: const nsINode* cur = this; michael@0: for (; cur; cur = cur->GetParentNode()) michael@0: if (cur == aRoot) break; michael@0: NS_ASSERTION(cur, "aRoot not an ancestor of |this|?"); michael@0: } michael@0: #endif michael@0: michael@0: if (this == aRoot) { michael@0: return nullptr; michael@0: } michael@0: nsIContent* cur = this->GetParent(); michael@0: nsIContent* iter = this->GetPreviousSibling(); michael@0: while (iter) { michael@0: cur = iter; michael@0: iter = reinterpret_cast(iter)->GetLastChild(); michael@0: } michael@0: return cur; michael@0: } michael@0: michael@0: /** michael@0: * Boolean flags michael@0: */ michael@0: private: michael@0: enum BooleanFlag { michael@0: // Set if we're being used from -moz-element michael@0: NodeHasRenderingObservers, michael@0: // Set if our parent chain (including this node itself) terminates michael@0: // in a document michael@0: IsInDocument, michael@0: // Set if mParent is an nsIContent michael@0: ParentIsContent, michael@0: // Set if this node is an Element michael@0: NodeIsElement, michael@0: // Set if the element has a non-empty id attribute. This can in rare michael@0: // cases lie for nsXMLElement, such as when the node has been moved between michael@0: // documents with different id mappings. michael@0: ElementHasID, michael@0: // Set if the element might have inline style. michael@0: ElementMayHaveStyle, michael@0: // Set if the element has a name attribute set. michael@0: ElementHasName, michael@0: // Set if the element might have a contenteditable attribute set. michael@0: ElementMayHaveContentEditableAttr, michael@0: // Set if the node is the common ancestor of the start/end nodes of a Range michael@0: // that is in a Selection. michael@0: NodeIsCommonAncestorForRangeInSelection, michael@0: // Set if the node is a descendant of a node with the above bit set. michael@0: NodeIsDescendantOfCommonAncestorForRangeInSelection, michael@0: // Set if CanSkipInCC check has been done for this subtree root. michael@0: NodeIsCCMarkedRoot, michael@0: // Maybe set if this node is in black subtree. michael@0: NodeIsCCBlackTree, michael@0: // Maybe set if the node is a root of a subtree michael@0: // which needs to be kept in the purple buffer. michael@0: NodeIsPurpleRoot, michael@0: // Set if the node has an explicit base URI stored michael@0: NodeHasExplicitBaseURI, michael@0: // Set if the element has some style states locked michael@0: ElementHasLockedStyleStates, michael@0: // Set if element has pointer locked michael@0: ElementHasPointerLock, michael@0: // Set if the node may have DOMMutationObserver attached to it. michael@0: NodeMayHaveDOMMutationObserver, michael@0: // Set if node is Content michael@0: NodeIsContent, michael@0: // Set if the node has animations or transitions michael@0: ElementHasAnimations, michael@0: // Set if node has a dir attribute with a valid value (ltr, rtl, or auto) michael@0: NodeHasValidDirAttribute, michael@0: // Set if node has a dir attribute with a fixed value (ltr or rtl, NOT auto) michael@0: NodeHasFixedDir, michael@0: // Set if the node has dir=auto and has a property pointing to the text michael@0: // node that determines its direction michael@0: NodeHasDirAutoSet, michael@0: // Set if the node is a text node descendant of a node with dir=auto michael@0: // and has a TextNodeDirectionalityMap property listing the elements whose michael@0: // direction it determines. michael@0: NodeHasTextNodeDirectionalityMap, michael@0: // Set if the node has dir=auto. michael@0: NodeHasDirAuto, michael@0: // Set if a node in the node's parent chain has dir=auto. michael@0: NodeAncestorHasDirAuto, michael@0: // Set if the element is in the scope of a scoped style sheet; this flag is michael@0: // only accurate for elements bound to a document michael@0: ElementIsInStyleScope, michael@0: // Set if the element is a scoped style sheet root michael@0: ElementIsScopedStyleRoot, michael@0: // Set if the node is handling a click. michael@0: NodeHandlingClick, michael@0: // Set if the node has had :hover selectors matched against it michael@0: NodeHasRelevantHoverRules, michael@0: // Set if the element has a parser insertion mode other than "in body", michael@0: // per the HTML5 "Parse state" section. michael@0: ElementHasWeirdParserInsertionMode, michael@0: // Guard value michael@0: BooleanFlagCount michael@0: }; michael@0: michael@0: void SetBoolFlag(BooleanFlag name, bool value) { michael@0: static_assert(BooleanFlagCount <= 8*sizeof(mBoolFlags), michael@0: "Too many boolean flags"); michael@0: mBoolFlags = (mBoolFlags & ~(1 << name)) | (value << name); michael@0: } michael@0: michael@0: void SetBoolFlag(BooleanFlag name) { michael@0: static_assert(BooleanFlagCount <= 8*sizeof(mBoolFlags), michael@0: "Too many boolean flags"); michael@0: mBoolFlags |= (1 << name); michael@0: } michael@0: michael@0: void ClearBoolFlag(BooleanFlag name) { michael@0: static_assert(BooleanFlagCount <= 8*sizeof(mBoolFlags), michael@0: "Too many boolean flags"); michael@0: mBoolFlags &= ~(1 << name); michael@0: } michael@0: michael@0: bool GetBoolFlag(BooleanFlag name) const { michael@0: static_assert(BooleanFlagCount <= 8*sizeof(mBoolFlags), michael@0: "Too many boolean flags"); michael@0: return mBoolFlags & (1 << name); michael@0: } michael@0: michael@0: public: michael@0: bool HasRenderingObservers() const michael@0: { return GetBoolFlag(NodeHasRenderingObservers); } michael@0: void SetHasRenderingObservers(bool aValue) michael@0: { SetBoolFlag(NodeHasRenderingObservers, aValue); } michael@0: bool IsContent() const { return GetBoolFlag(NodeIsContent); } michael@0: bool HasID() const { return GetBoolFlag(ElementHasID); } michael@0: bool MayHaveStyle() const { return GetBoolFlag(ElementMayHaveStyle); } michael@0: bool HasName() const { return GetBoolFlag(ElementHasName); } michael@0: bool MayHaveContentEditableAttr() const michael@0: { return GetBoolFlag(ElementMayHaveContentEditableAttr); } michael@0: bool IsCommonAncestorForRangeInSelection() const michael@0: { return GetBoolFlag(NodeIsCommonAncestorForRangeInSelection); } michael@0: void SetCommonAncestorForRangeInSelection() michael@0: { SetBoolFlag(NodeIsCommonAncestorForRangeInSelection); } michael@0: void ClearCommonAncestorForRangeInSelection() michael@0: { ClearBoolFlag(NodeIsCommonAncestorForRangeInSelection); } michael@0: bool IsDescendantOfCommonAncestorForRangeInSelection() const michael@0: { return GetBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); } michael@0: void SetDescendantOfCommonAncestorForRangeInSelection() michael@0: { SetBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); } michael@0: void ClearDescendantOfCommonAncestorForRangeInSelection() michael@0: { ClearBoolFlag(NodeIsDescendantOfCommonAncestorForRangeInSelection); } michael@0: michael@0: void SetCCMarkedRoot(bool aValue) michael@0: { SetBoolFlag(NodeIsCCMarkedRoot, aValue); } michael@0: bool CCMarkedRoot() const { return GetBoolFlag(NodeIsCCMarkedRoot); } michael@0: void SetInCCBlackTree(bool aValue) michael@0: { SetBoolFlag(NodeIsCCBlackTree, aValue); } michael@0: bool InCCBlackTree() const { return GetBoolFlag(NodeIsCCBlackTree); } michael@0: void SetIsPurpleRoot(bool aValue) michael@0: { SetBoolFlag(NodeIsPurpleRoot, aValue); } michael@0: bool IsPurpleRoot() const { return GetBoolFlag(NodeIsPurpleRoot); } michael@0: bool MayHaveDOMMutationObserver() michael@0: { return GetBoolFlag(NodeMayHaveDOMMutationObserver); } michael@0: void SetMayHaveDOMMutationObserver() michael@0: { SetBoolFlag(NodeMayHaveDOMMutationObserver, true); } michael@0: bool HasListenerManager() { return HasFlag(NODE_HAS_LISTENERMANAGER); } michael@0: bool HasPointerLock() const { return GetBoolFlag(ElementHasPointerLock); } michael@0: void SetPointerLock() { SetBoolFlag(ElementHasPointerLock); } michael@0: void ClearPointerLock() { ClearBoolFlag(ElementHasPointerLock); } michael@0: bool MayHaveAnimations() { return GetBoolFlag(ElementHasAnimations); } michael@0: void SetMayHaveAnimations() { SetBoolFlag(ElementHasAnimations); } michael@0: void SetHasValidDir() { SetBoolFlag(NodeHasValidDirAttribute); } michael@0: void ClearHasValidDir() { ClearBoolFlag(NodeHasValidDirAttribute); } michael@0: bool HasValidDir() const { return GetBoolFlag(NodeHasValidDirAttribute); } michael@0: void SetHasFixedDir() { michael@0: MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE, michael@0: "SetHasFixedDir on text node"); michael@0: SetBoolFlag(NodeHasFixedDir); michael@0: } michael@0: void ClearHasFixedDir() { michael@0: MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE, michael@0: "ClearHasFixedDir on text node"); michael@0: ClearBoolFlag(NodeHasFixedDir); michael@0: } michael@0: bool HasFixedDir() const { return GetBoolFlag(NodeHasFixedDir); } michael@0: void SetHasDirAutoSet() { michael@0: MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE, michael@0: "SetHasDirAutoSet on text node"); michael@0: SetBoolFlag(NodeHasDirAutoSet); michael@0: } michael@0: void ClearHasDirAutoSet() { michael@0: MOZ_ASSERT(NodeType() != nsIDOMNode::TEXT_NODE, michael@0: "ClearHasDirAutoSet on text node"); michael@0: ClearBoolFlag(NodeHasDirAutoSet); michael@0: } michael@0: bool HasDirAutoSet() const michael@0: { return GetBoolFlag(NodeHasDirAutoSet); } michael@0: void SetHasTextNodeDirectionalityMap() { michael@0: MOZ_ASSERT(NodeType() == nsIDOMNode::TEXT_NODE, michael@0: "SetHasTextNodeDirectionalityMap on non-text node"); michael@0: SetBoolFlag(NodeHasTextNodeDirectionalityMap); michael@0: } michael@0: void ClearHasTextNodeDirectionalityMap() { michael@0: MOZ_ASSERT(NodeType() == nsIDOMNode::TEXT_NODE, michael@0: "ClearHasTextNodeDirectionalityMap on non-text node"); michael@0: ClearBoolFlag(NodeHasTextNodeDirectionalityMap); michael@0: } michael@0: bool HasTextNodeDirectionalityMap() const michael@0: { return GetBoolFlag(NodeHasTextNodeDirectionalityMap); } michael@0: michael@0: void SetHasDirAuto() { SetBoolFlag(NodeHasDirAuto); } michael@0: void ClearHasDirAuto() { ClearBoolFlag(NodeHasDirAuto); } michael@0: bool HasDirAuto() const { return GetBoolFlag(NodeHasDirAuto); } michael@0: michael@0: void SetAncestorHasDirAuto() { SetBoolFlag(NodeAncestorHasDirAuto); } michael@0: void ClearAncestorHasDirAuto() { ClearBoolFlag(NodeAncestorHasDirAuto); } michael@0: bool AncestorHasDirAuto() const { return GetBoolFlag(NodeAncestorHasDirAuto); } michael@0: michael@0: bool NodeOrAncestorHasDirAuto() const michael@0: { return HasDirAuto() || AncestorHasDirAuto(); } michael@0: michael@0: void SetIsElementInStyleScope(bool aValue) { michael@0: MOZ_ASSERT(IsElement(), "SetIsInStyleScope on a non-Element node"); michael@0: SetBoolFlag(ElementIsInStyleScope, aValue); michael@0: } michael@0: void SetIsElementInStyleScope() { michael@0: MOZ_ASSERT(IsElement(), "SetIsInStyleScope on a non-Element node"); michael@0: SetBoolFlag(ElementIsInStyleScope); michael@0: } michael@0: void ClearIsElementInStyleScope() { michael@0: MOZ_ASSERT(IsElement(), "ClearIsInStyleScope on a non-Element node"); michael@0: ClearBoolFlag(ElementIsInStyleScope); michael@0: } michael@0: bool IsElementInStyleScope() const { return GetBoolFlag(ElementIsInStyleScope); } michael@0: michael@0: void SetIsScopedStyleRoot() { SetBoolFlag(ElementIsScopedStyleRoot); } michael@0: void ClearIsScopedStyleRoot() { ClearBoolFlag(ElementIsScopedStyleRoot); } michael@0: bool IsScopedStyleRoot() { return GetBoolFlag(ElementIsScopedStyleRoot); } michael@0: bool HasRelevantHoverRules() const { return GetBoolFlag(NodeHasRelevantHoverRules); } michael@0: void SetHasRelevantHoverRules() { SetBoolFlag(NodeHasRelevantHoverRules); } michael@0: protected: michael@0: void SetParentIsContent(bool aValue) { SetBoolFlag(ParentIsContent, aValue); } michael@0: void SetInDocument() { SetBoolFlag(IsInDocument); } michael@0: void SetNodeIsContent() { SetBoolFlag(NodeIsContent); } michael@0: void ClearInDocument() { ClearBoolFlag(IsInDocument); } michael@0: void SetIsElement() { SetBoolFlag(NodeIsElement); } michael@0: void SetHasID() { SetBoolFlag(ElementHasID); } michael@0: void ClearHasID() { ClearBoolFlag(ElementHasID); } michael@0: void SetMayHaveStyle() { SetBoolFlag(ElementMayHaveStyle); } michael@0: void SetHasName() { SetBoolFlag(ElementHasName); } michael@0: void ClearHasName() { ClearBoolFlag(ElementHasName); } michael@0: void SetMayHaveContentEditableAttr() michael@0: { SetBoolFlag(ElementMayHaveContentEditableAttr); } michael@0: bool HasExplicitBaseURI() const { return GetBoolFlag(NodeHasExplicitBaseURI); } michael@0: void SetHasExplicitBaseURI() { SetBoolFlag(NodeHasExplicitBaseURI); } michael@0: void SetHasLockedStyleStates() { SetBoolFlag(ElementHasLockedStyleStates); } michael@0: void ClearHasLockedStyleStates() { ClearBoolFlag(ElementHasLockedStyleStates); } michael@0: bool HasLockedStyleStates() const michael@0: { return GetBoolFlag(ElementHasLockedStyleStates); } michael@0: void SetHasWeirdParserInsertionMode() { SetBoolFlag(ElementHasWeirdParserInsertionMode); } michael@0: bool HasWeirdParserInsertionMode() const michael@0: { return GetBoolFlag(ElementHasWeirdParserInsertionMode); } michael@0: bool HandlingClick() const { return GetBoolFlag(NodeHandlingClick); } michael@0: void SetHandlingClick() { SetBoolFlag(NodeHandlingClick); } michael@0: void ClearHandlingClick() { ClearBoolFlag(NodeHandlingClick); } michael@0: michael@0: void SetSubtreeRootPointer(nsINode* aSubtreeRoot) michael@0: { michael@0: NS_ASSERTION(aSubtreeRoot, "aSubtreeRoot can never be null!"); michael@0: NS_ASSERTION(!(IsNodeOfType(eCONTENT) && IsInDoc()), "Shouldn't be here!"); michael@0: mSubtreeRoot = aSubtreeRoot; michael@0: } michael@0: michael@0: void ClearSubtreeRootPointer() michael@0: { michael@0: mSubtreeRoot = nullptr; michael@0: } michael@0: michael@0: public: michael@0: // Makes nsINode object to keep aObject alive. michael@0: void BindObject(nsISupports* aObject); michael@0: // After calling UnbindObject nsINode object doesn't keep michael@0: // aObject alive anymore. michael@0: void UnbindObject(nsISupports* aObject); michael@0: michael@0: void GetBoundMutationObservers(nsTArray >& aResult); michael@0: michael@0: /** michael@0: * Returns the length of this node, as specified at michael@0: * michael@0: */ michael@0: uint32_t Length() const; michael@0: michael@0: void GetNodeName(mozilla::dom::DOMString& aNodeName) michael@0: { michael@0: const nsString& nodeName = NodeName(); michael@0: aNodeName.SetStringBuffer(nsStringBuffer::FromString(nodeName), michael@0: nodeName.Length()); michael@0: } michael@0: void GetBaseURI(nsAString& aBaseURI) const; michael@0: // Return the base URI for the document. michael@0: // The returned value may differ if the document is loaded via XHR, and michael@0: // when accessed from chrome privileged script and michael@0: // from content privileged script for compatibility. michael@0: void GetBaseURIFromJS(nsAString& aBaseURI) const; michael@0: bool HasChildNodes() const michael@0: { michael@0: return HasChildren(); michael@0: } michael@0: uint16_t CompareDocumentPosition(nsINode& aOther) const; michael@0: void GetNodeValue(nsAString& aNodeValue) michael@0: { michael@0: GetNodeValueInternal(aNodeValue); michael@0: } michael@0: void SetNodeValue(const nsAString& aNodeValue, michael@0: mozilla::ErrorResult& aError) michael@0: { michael@0: SetNodeValueInternal(aNodeValue, aError); michael@0: } michael@0: virtual void GetNodeValueInternal(nsAString& aNodeValue); michael@0: virtual void SetNodeValueInternal(const nsAString& aNodeValue, michael@0: mozilla::ErrorResult& aError) michael@0: { michael@0: // The DOM spec says that when nodeValue is defined to be null "setting it michael@0: // has no effect", so we don't throw an exception. michael@0: } michael@0: nsINode* InsertBefore(nsINode& aNode, nsINode* aChild, michael@0: mozilla::ErrorResult& aError) michael@0: { michael@0: return ReplaceOrInsertBefore(false, &aNode, aChild, aError); michael@0: } michael@0: nsINode* AppendChild(nsINode& aNode, mozilla::ErrorResult& aError) michael@0: { michael@0: return InsertBefore(aNode, nullptr, aError); michael@0: } michael@0: nsINode* ReplaceChild(nsINode& aNode, nsINode& aChild, michael@0: mozilla::ErrorResult& aError) michael@0: { michael@0: return ReplaceOrInsertBefore(true, &aNode, &aChild, aError); michael@0: } michael@0: nsINode* RemoveChild(nsINode& aChild, mozilla::ErrorResult& aError); michael@0: already_AddRefed CloneNode(bool aDeep, mozilla::ErrorResult& aError); michael@0: bool IsEqualNode(nsINode* aNode); michael@0: void GetNamespaceURI(nsAString& aNamespaceURI) const michael@0: { michael@0: mNodeInfo->GetNamespaceURI(aNamespaceURI); michael@0: } michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: void GetPrefix(nsAString& aPrefix) michael@0: { michael@0: mNodeInfo->GetPrefix(aPrefix); michael@0: } michael@0: #endif michael@0: void GetLocalName(mozilla::dom::DOMString& aLocalName) michael@0: { michael@0: const nsString& localName = LocalName(); michael@0: if (localName.IsVoid()) { michael@0: aLocalName.SetNull(); michael@0: } else { michael@0: aLocalName.SetStringBuffer(nsStringBuffer::FromString(localName), michael@0: localName.Length()); michael@0: } michael@0: } michael@0: // HasAttributes is defined inline in Element.h. michael@0: bool HasAttributes() const; michael@0: nsDOMAttributeMap* GetAttributes(); michael@0: void SetUserData(JSContext* aCx, const nsAString& aKey, michael@0: JS::Handle aData, michael@0: nsIDOMUserDataHandler* aHandler, michael@0: JS::MutableHandle aRetval, michael@0: mozilla::ErrorResult& aError); michael@0: void GetUserData(JSContext* aCx, const nsAString& aKey, michael@0: JS::MutableHandle aRetval, michael@0: mozilla::ErrorResult& aError); michael@0: michael@0: // Helper method to remove this node from its parent. This is not exposed michael@0: // through WebIDL. michael@0: // Only call this if the node has a parent node. michael@0: nsresult RemoveFromParent() michael@0: { michael@0: nsINode* parent = GetParentNode(); michael@0: mozilla::ErrorResult rv; michael@0: parent->RemoveChild(*this, rv); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: // ChildNode methods michael@0: mozilla::dom::Element* GetPreviousElementSibling() const; michael@0: mozilla::dom::Element* GetNextElementSibling() const; michael@0: /** michael@0: * Remove this node from its parent, if any. michael@0: */ michael@0: void Remove(); michael@0: michael@0: // ParentNode methods michael@0: mozilla::dom::Element* GetFirstElementChild() const; michael@0: mozilla::dom::Element* GetLastElementChild() const; michael@0: michael@0: void GetBoxQuads(const BoxQuadOptions& aOptions, michael@0: nsTArray >& aResult, michael@0: mozilla::ErrorResult& aRv); michael@0: michael@0: already_AddRefed ConvertQuadFromNode(DOMQuad& aQuad, michael@0: const TextOrElementOrDocument& aFrom, michael@0: const ConvertCoordinateOptions& aOptions, michael@0: ErrorResult& aRv); michael@0: already_AddRefed ConvertRectFromNode(DOMRectReadOnly& aRect, michael@0: const TextOrElementOrDocument& aFrom, michael@0: const ConvertCoordinateOptions& aOptions, michael@0: ErrorResult& aRv); michael@0: already_AddRefed ConvertPointFromNode(const DOMPointInit& aPoint, michael@0: const TextOrElementOrDocument& aFrom, michael@0: const ConvertCoordinateOptions& aOptions, michael@0: ErrorResult& aRv); michael@0: michael@0: protected: michael@0: michael@0: // Override this function to create a custom slots class. michael@0: // Must not return null. michael@0: virtual nsINode::nsSlots* CreateSlots(); michael@0: michael@0: bool HasSlots() const michael@0: { michael@0: return mSlots != nullptr; michael@0: } michael@0: michael@0: nsSlots* GetExistingSlots() const michael@0: { michael@0: return mSlots; michael@0: } michael@0: michael@0: nsSlots* Slots() michael@0: { michael@0: if (!HasSlots()) { michael@0: mSlots = CreateSlots(); michael@0: MOZ_ASSERT(mSlots); michael@0: } michael@0: return GetExistingSlots(); michael@0: } michael@0: michael@0: nsTObserverArray *GetMutationObservers() michael@0: { michael@0: return HasSlots() ? &GetExistingSlots()->mMutationObservers : nullptr; michael@0: } michael@0: michael@0: bool IsEditableInternal() const; michael@0: virtual bool IsEditableExternal() const michael@0: { michael@0: return IsEditableInternal(); michael@0: } michael@0: michael@0: virtual void GetTextContentInternal(nsAString& aTextContent); michael@0: virtual void SetTextContentInternal(const nsAString& aTextContent, michael@0: mozilla::ErrorResult& aError) michael@0: { michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: // Note: virtual so that IsInNativeAnonymousSubtree can be called accross michael@0: // module boundaries. michael@0: virtual void CheckNotNativeAnonymous() const; michael@0: #endif michael@0: michael@0: // These are just used to implement nsIDOMNode using michael@0: // NS_FORWARD_NSIDOMNODE_TO_NSINODE_HELPER and for quickstubs. michael@0: nsresult GetParentNode(nsIDOMNode** aParentNode); michael@0: nsresult GetParentElement(nsIDOMElement** aParentElement); michael@0: nsresult GetChildNodes(nsIDOMNodeList** aChildNodes); michael@0: nsresult GetFirstChild(nsIDOMNode** aFirstChild); michael@0: nsresult GetLastChild(nsIDOMNode** aLastChild); michael@0: nsresult GetPreviousSibling(nsIDOMNode** aPrevSibling); michael@0: nsresult GetNextSibling(nsIDOMNode** aNextSibling); michael@0: nsresult GetOwnerDocument(nsIDOMDocument** aOwnerDocument); michael@0: nsresult CompareDocumentPosition(nsIDOMNode* aOther, michael@0: uint16_t* aReturn); michael@0: michael@0: nsresult ReplaceOrInsertBefore(bool aReplace, nsIDOMNode *aNewChild, michael@0: nsIDOMNode *aRefChild, nsIDOMNode **aReturn); michael@0: nsINode* ReplaceOrInsertBefore(bool aReplace, nsINode* aNewChild, michael@0: nsINode* aRefChild, michael@0: mozilla::ErrorResult& aError); michael@0: nsresult RemoveChild(nsIDOMNode* aOldChild, nsIDOMNode** aReturn); michael@0: michael@0: /** michael@0: * Returns the Element that should be used for resolving namespaces michael@0: * on this node (ie the ownerElement for attributes, the documentElement for michael@0: * documents, the node itself for elements and for other nodes the parentNode michael@0: * if it is an element). michael@0: */ michael@0: virtual mozilla::dom::Element* GetNameSpaceElement() = 0; michael@0: michael@0: /** michael@0: * Most of the implementation of the nsINode RemoveChildAt method. michael@0: * Should only be called on document, element, and document fragment michael@0: * nodes. The aChildArray passed in should be the one for |this|. michael@0: * michael@0: * @param aIndex The index to remove at. michael@0: * @param aNotify Whether to notify. michael@0: * @param aKid The kid at aIndex. Must not be null. michael@0: * @param aChildArray The child array to work with. michael@0: * @param aMutationEvent whether to fire a mutation event for this removal. michael@0: */ michael@0: void doRemoveChildAt(uint32_t aIndex, bool aNotify, nsIContent* aKid, michael@0: nsAttrAndChildArray& aChildArray); michael@0: michael@0: /** michael@0: * Most of the implementation of the nsINode InsertChildAt method. michael@0: * Should only be called on document, element, and document fragment michael@0: * nodes. The aChildArray passed in should be the one for |this|. michael@0: * michael@0: * @param aKid The child to insert. michael@0: * @param aIndex The index to insert at. michael@0: * @param aNotify Whether to notify. michael@0: * @param aChildArray The child array to work with michael@0: */ michael@0: nsresult doInsertChildAt(nsIContent* aKid, uint32_t aIndex, michael@0: bool aNotify, nsAttrAndChildArray& aChildArray); michael@0: michael@0: /** michael@0: * Parse the given selector string into an nsCSSSelectorList. michael@0: * michael@0: * A null return value with a non-failing aRv means the string only michael@0: * contained pseudo-element selectors. michael@0: * michael@0: * A failing aRv means the string was not a valid selector. michael@0: */ michael@0: nsCSSSelectorList* ParseSelectorList(const nsAString& aSelectorString, michael@0: mozilla::ErrorResult& aRv); michael@0: michael@0: public: michael@0: /* Event stuff that documents and elements share. This needs to be michael@0: NS_IMETHOD because some subclasses implement DOM methods with michael@0: this exact name and signature and then the calling convention michael@0: needs to match. michael@0: michael@0: Note that we include DOCUMENT_ONLY_EVENT events here so that we michael@0: can forward all the document stuff to this implementation. michael@0: */ michael@0: #define EVENT(name_, id_, type_, struct_) \ michael@0: mozilla::dom::EventHandlerNonNull* GetOn##name_(); \ michael@0: void SetOn##name_(mozilla::dom::EventHandlerNonNull* listener); michael@0: #define TOUCH_EVENT EVENT michael@0: #define DOCUMENT_ONLY_EVENT EVENT michael@0: #include "mozilla/EventNameList.h" michael@0: #undef DOCUMENT_ONLY_EVENT michael@0: #undef TOUCH_EVENT michael@0: #undef EVENT michael@0: michael@0: protected: michael@0: static bool Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb); michael@0: static void Unlink(nsINode *tmp); michael@0: michael@0: nsCOMPtr mNodeInfo; michael@0: michael@0: nsINode* mParent; michael@0: michael@0: private: michael@0: // Boolean flags. michael@0: uint32_t mBoolFlags; michael@0: michael@0: protected: michael@0: nsIContent* mNextSibling; michael@0: nsIContent* mPreviousSibling; michael@0: nsIContent* mFirstChild; michael@0: michael@0: union { michael@0: // Pointer to our primary frame. Might be null. michael@0: nsIFrame* mPrimaryFrame; michael@0: michael@0: // Pointer to the root of our subtree. Might be null. michael@0: nsINode* mSubtreeRoot; michael@0: }; michael@0: michael@0: // Storage for more members that are usually not needed; allocated lazily. michael@0: nsSlots* mSlots; michael@0: }; michael@0: michael@0: // Useful inline function for getting a node given an nsIContent and an michael@0: // nsIDocument. Returns the first argument cast to nsINode if it is non-null, michael@0: // otherwise returns the second (which may be null). We use type variables michael@0: // instead of nsIContent* and nsIDocument* because the actual types must be michael@0: // known for the cast to work. michael@0: template michael@0: inline nsINode* NODE_FROM(C& aContent, D& aDocument) michael@0: { michael@0: if (aContent) michael@0: return static_cast(aContent); michael@0: return static_cast(aDocument); michael@0: } michael@0: michael@0: NS_DEFINE_STATIC_IID_ACCESSOR(nsINode, NS_INODE_IID) michael@0: michael@0: inline nsISupports* michael@0: ToSupports(nsINode* aPointer) michael@0: { michael@0: return aPointer; michael@0: } michael@0: michael@0: inline nsISupports* michael@0: ToCanonicalSupports(nsINode* aPointer) michael@0: { michael@0: return aPointer; michael@0: } michael@0: michael@0: #define NS_FORWARD_NSIDOMNODE_TO_NSINODE_HELPER(...) \ michael@0: NS_IMETHOD GetNodeName(nsAString& aNodeName) __VA_ARGS__ \ michael@0: { \ michael@0: aNodeName = nsINode::NodeName(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetNodeValue(nsAString& aNodeValue) __VA_ARGS__ \ michael@0: { \ michael@0: nsINode::GetNodeValue(aNodeValue); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD SetNodeValue(const nsAString& aNodeValue) __VA_ARGS__ \ michael@0: { \ michael@0: mozilla::ErrorResult rv; \ michael@0: nsINode::SetNodeValue(aNodeValue, rv); \ michael@0: return rv.ErrorCode(); \ michael@0: } \ michael@0: NS_IMETHOD GetNodeType(uint16_t* aNodeType) __VA_ARGS__ \ michael@0: { \ michael@0: *aNodeType = nsINode::NodeType(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetParentNode(nsIDOMNode** aParentNode) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::GetParentNode(aParentNode); \ michael@0: } \ michael@0: NS_IMETHOD GetParentElement(nsIDOMElement** aParentElement) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::GetParentElement(aParentElement); \ michael@0: } \ michael@0: NS_IMETHOD GetChildNodes(nsIDOMNodeList** aChildNodes) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::GetChildNodes(aChildNodes); \ michael@0: } \ michael@0: NS_IMETHOD GetFirstChild(nsIDOMNode** aFirstChild) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::GetFirstChild(aFirstChild); \ michael@0: } \ michael@0: NS_IMETHOD GetLastChild(nsIDOMNode** aLastChild) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::GetLastChild(aLastChild); \ michael@0: } \ michael@0: NS_IMETHOD GetPreviousSibling(nsIDOMNode** aPreviousSibling) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::GetPreviousSibling(aPreviousSibling); \ michael@0: } \ michael@0: NS_IMETHOD GetNextSibling(nsIDOMNode** aNextSibling) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::GetNextSibling(aNextSibling); \ michael@0: } \ michael@0: NS_IMETHOD GetOwnerDocument(nsIDOMDocument** aOwnerDocument) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::GetOwnerDocument(aOwnerDocument); \ michael@0: } \ michael@0: NS_IMETHOD InsertBefore(nsIDOMNode* aNewChild, nsIDOMNode* aRefChild, nsIDOMNode** aResult) __VA_ARGS__ \ michael@0: { \ michael@0: return ReplaceOrInsertBefore(false, aNewChild, aRefChild, aResult); \ michael@0: } \ michael@0: NS_IMETHOD ReplaceChild(nsIDOMNode* aNewChild, nsIDOMNode* aOldChild, nsIDOMNode** aResult) __VA_ARGS__ \ michael@0: { \ michael@0: return ReplaceOrInsertBefore(true, aNewChild, aOldChild, aResult); \ michael@0: } \ michael@0: NS_IMETHOD RemoveChild(nsIDOMNode* aOldChild, nsIDOMNode** aResult) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::RemoveChild(aOldChild, aResult); \ michael@0: } \ michael@0: NS_IMETHOD AppendChild(nsIDOMNode* aNewChild, nsIDOMNode** aResult) __VA_ARGS__ \ michael@0: { \ michael@0: return InsertBefore(aNewChild, nullptr, aResult); \ michael@0: } \ michael@0: NS_IMETHOD HasChildNodes(bool* aResult) __VA_ARGS__ \ michael@0: { \ michael@0: *aResult = nsINode::HasChildNodes(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD CloneNode(bool aDeep, uint8_t aArgc, nsIDOMNode** aResult) __VA_ARGS__ \ michael@0: { \ michael@0: if (aArgc == 0) { \ michael@0: aDeep = true; \ michael@0: } \ michael@0: mozilla::ErrorResult rv; \ michael@0: nsCOMPtr clone = nsINode::CloneNode(aDeep, rv); \ michael@0: if (rv.Failed()) { \ michael@0: return rv.ErrorCode(); \ michael@0: } \ michael@0: *aResult = clone.forget().take()->AsDOMNode(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD Normalize() __VA_ARGS__ \ michael@0: { \ michael@0: nsINode::Normalize(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetNamespaceURI(nsAString& aNamespaceURI) __VA_ARGS__ \ michael@0: { \ michael@0: nsINode::GetNamespaceURI(aNamespaceURI); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetPrefix(nsAString& aPrefix) __VA_ARGS__ \ michael@0: { \ michael@0: nsINode::GetPrefix(aPrefix); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetLocalName(nsAString& aLocalName) __VA_ARGS__ \ michael@0: { \ michael@0: aLocalName = nsINode::LocalName(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: using nsINode::HasAttributes; \ michael@0: NS_IMETHOD HasAttributes(bool* aResult) __VA_ARGS__ \ michael@0: { \ michael@0: *aResult = nsINode::HasAttributes(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetDOMBaseURI(nsAString& aBaseURI) __VA_ARGS__ \ michael@0: { \ michael@0: nsINode::GetBaseURI(aBaseURI); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD CompareDocumentPosition(nsIDOMNode* aOther, uint16_t* aResult) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::CompareDocumentPosition(aOther, aResult); \ michael@0: } \ michael@0: NS_IMETHOD GetTextContent(nsAString& aTextContent) __VA_ARGS__ \ michael@0: { \ michael@0: nsINode::GetTextContent(aTextContent); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD SetTextContent(const nsAString& aTextContent) __VA_ARGS__ \ michael@0: { \ michael@0: mozilla::ErrorResult rv; \ michael@0: nsINode::SetTextContent(aTextContent, rv); \ michael@0: return rv.ErrorCode(); \ michael@0: } \ michael@0: NS_IMETHOD LookupPrefix(const nsAString& aNamespaceURI, nsAString& aResult) __VA_ARGS__ \ michael@0: { \ michael@0: nsINode::LookupPrefix(aNamespaceURI, aResult); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD IsDefaultNamespace(const nsAString& aNamespaceURI, bool* aResult) __VA_ARGS__ \ michael@0: { \ michael@0: *aResult = nsINode::IsDefaultNamespace(aNamespaceURI); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD LookupNamespaceURI(const nsAString& aPrefix, nsAString& aResult) __VA_ARGS__ \ michael@0: { \ michael@0: nsINode::LookupNamespaceURI(aPrefix, aResult); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD IsEqualNode(nsIDOMNode* aArg, bool* aResult) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::IsEqualNode(aArg, aResult); \ michael@0: } \ michael@0: NS_IMETHOD SetUserData(const nsAString& aKey, nsIVariant* aData, nsIDOMUserDataHandler* aHandler, nsIVariant** aResult) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::SetUserData(aKey, aData, aHandler, aResult); \ michael@0: } \ michael@0: NS_IMETHOD GetUserData(const nsAString& aKey, nsIVariant** aResult) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::GetUserData(aKey, aResult); \ michael@0: } \ michael@0: NS_IMETHOD Contains(nsIDOMNode* aOther, bool* aResult) __VA_ARGS__ \ michael@0: { \ michael@0: return nsINode::Contains(aOther, aResult); \ michael@0: } michael@0: michael@0: #define NS_FORWARD_NSIDOMNODE_TO_NSINODE \ michael@0: NS_FORWARD_NSIDOMNODE_TO_NSINODE_HELPER(MOZ_FINAL) michael@0: michael@0: #define NS_FORWARD_NSIDOMNODE_TO_NSINODE_OVERRIDABLE \ michael@0: NS_FORWARD_NSIDOMNODE_TO_NSINODE_HELPER() michael@0: michael@0: #endif /* nsINode_h___ */