michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: * vim: sw=2 ts=2 et : 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: /* michael@0: * Base class for all element classes; this provides an implementation michael@0: * of DOM Core's nsIDOMElement, implements nsIContent, provides michael@0: * utility methods for subclasses, and so forth. michael@0: */ michael@0: michael@0: #ifndef mozilla_dom_Element_h__ michael@0: #define mozilla_dom_Element_h__ michael@0: michael@0: #include "mozilla/dom/FragmentOrElement.h" // for base class michael@0: #include "nsChangeHint.h" // for enum michael@0: #include "mozilla/EventStates.h" // for member michael@0: #include "mozilla/dom/DirectionalityUtils.h" michael@0: #include "nsIDOMElement.h" michael@0: #include "nsILinkHandler.h" michael@0: #include "nsNodeUtils.h" michael@0: #include "nsAttrAndChildArray.h" michael@0: #include "mozFlushType.h" michael@0: #include "nsDOMAttributeMap.h" michael@0: #include "nsIDOMXPathNSResolver.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsDOMClassInfoID.h" // DOMCI_DATA michael@0: #include "mozilla/CORSMode.h" michael@0: #include "mozilla/Attributes.h" michael@0: #include "nsIScrollableFrame.h" michael@0: #include "mozilla/dom/Attr.h" michael@0: #include "nsISMILAttr.h" michael@0: #include "mozilla/dom/DOMRect.h" michael@0: #include "nsAttrValue.h" michael@0: #include "mozilla/EventForwards.h" michael@0: #include "mozilla/dom/BindingDeclarations.h" michael@0: #include "Units.h" michael@0: michael@0: class nsIDOMEventListener; michael@0: class nsIFrame; michael@0: class nsIDOMMozNamedAttrMap; michael@0: class nsIDOMCSSStyleDeclaration; michael@0: class nsIURI; michael@0: class nsINodeInfo; michael@0: class nsIControllers; michael@0: class nsEventChainVisitor; michael@0: class nsIScrollableFrame; michael@0: class nsAttrValueOrString; michael@0: class ContentUnbinder; michael@0: class nsContentList; michael@0: class nsDOMTokenList; michael@0: struct nsRect; michael@0: class nsFocusManager; michael@0: class nsGlobalWindow; michael@0: class nsICSSDeclaration; michael@0: class nsISMILAttr; michael@0: michael@0: already_AddRefed michael@0: NS_GetContentList(nsINode* aRootNode, michael@0: int32_t aMatchNameSpaceId, michael@0: const nsAString& aTagname); michael@0: michael@0: #define ELEMENT_FLAG_BIT(n_) NODE_FLAG_BIT(NODE_TYPE_SPECIFIC_BITS_OFFSET + (n_)) michael@0: michael@0: // Element-specific flags michael@0: enum { michael@0: // Set if the element has a pending style change. michael@0: ELEMENT_HAS_PENDING_RESTYLE = ELEMENT_FLAG_BIT(0), michael@0: michael@0: // Set if the element is a potential restyle root (that is, has a style michael@0: // change pending _and_ that style change will attempt to restyle michael@0: // descendants). michael@0: ELEMENT_IS_POTENTIAL_RESTYLE_ROOT = ELEMENT_FLAG_BIT(1), michael@0: michael@0: // Set if the element has a pending animation style change. michael@0: ELEMENT_HAS_PENDING_ANIMATION_RESTYLE = ELEMENT_FLAG_BIT(2), michael@0: michael@0: // Set if the element is a potential animation restyle root (that is, michael@0: // has an animation style change pending _and_ that style change michael@0: // will attempt to restyle descendants). michael@0: ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT = ELEMENT_FLAG_BIT(3), michael@0: michael@0: // All of those bits together, for convenience. michael@0: ELEMENT_ALL_RESTYLE_FLAGS = ELEMENT_HAS_PENDING_RESTYLE | michael@0: ELEMENT_IS_POTENTIAL_RESTYLE_ROOT | michael@0: ELEMENT_HAS_PENDING_ANIMATION_RESTYLE | michael@0: ELEMENT_IS_POTENTIAL_ANIMATION_RESTYLE_ROOT, michael@0: michael@0: // Just the HAS_PENDING bits, for convenience michael@0: ELEMENT_PENDING_RESTYLE_FLAGS = ELEMENT_HAS_PENDING_RESTYLE | michael@0: ELEMENT_HAS_PENDING_ANIMATION_RESTYLE, michael@0: michael@0: // Remaining bits are for subclasses michael@0: ELEMENT_TYPE_SPECIFIC_BITS_OFFSET = NODE_TYPE_SPECIFIC_BITS_OFFSET + 4 michael@0: }; michael@0: michael@0: #undef ELEMENT_FLAG_BIT michael@0: michael@0: // Make sure we have space for our bits michael@0: ASSERT_NODE_FLAGS_SPACE(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET); michael@0: michael@0: namespace mozilla { michael@0: class EventChainPostVisitor; michael@0: class EventChainPreVisitor; michael@0: class EventChainVisitor; michael@0: class EventListenerManager; michael@0: class EventStateManager; michael@0: michael@0: namespace dom { michael@0: michael@0: class Link; michael@0: class UndoManager; michael@0: class DOMRect; michael@0: class DOMRectList; michael@0: michael@0: // IID for the dom::Element interface michael@0: #define NS_ELEMENT_IID \ michael@0: { 0xf7c18f0f, 0xa8fd, 0x4a95, \ michael@0: { 0x91, 0x72, 0xd3, 0xa7, 0x4a, 0xb8, 0xc4, 0xbe } } michael@0: michael@0: class Element : public FragmentOrElement michael@0: { michael@0: public: michael@0: #ifdef MOZILLA_INTERNAL_API michael@0: Element(already_AddRefed& aNodeInfo) : michael@0: FragmentOrElement(aNodeInfo), michael@0: mState(NS_EVENT_STATE_MOZ_READONLY) michael@0: { michael@0: NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::ELEMENT_NODE, michael@0: "Bad NodeType in aNodeInfo"); michael@0: SetIsElement(); michael@0: } michael@0: #endif // MOZILLA_INTERNAL_API michael@0: michael@0: NS_DECLARE_STATIC_IID_ACCESSOR(NS_ELEMENT_IID) michael@0: michael@0: NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr); michael@0: michael@0: /** michael@0: * Method to get the full state of this element. See mozilla/EventStates.h michael@0: * for the possible bits that could be set here. michael@0: */ michael@0: EventStates State() const michael@0: { michael@0: // mState is maintained by having whoever might have changed it michael@0: // call UpdateState() or one of the other mState mutators. michael@0: return mState; michael@0: } michael@0: michael@0: /** michael@0: * Ask this element to update its state. If aNotify is false, then michael@0: * state change notifications will not be dispatched; in that michael@0: * situation it is the caller's responsibility to dispatch them. michael@0: * michael@0: * In general, aNotify should only be false if we're guaranteed that michael@0: * the element can't have a frame no matter what its style is michael@0: * (e.g. if we're in the middle of adding it to the document or michael@0: * removing it from the document). michael@0: */ michael@0: void UpdateState(bool aNotify); michael@0: michael@0: /** michael@0: * Method to update mState with link state information. This does not notify. michael@0: */ michael@0: void UpdateLinkState(EventStates aState); michael@0: michael@0: /** michael@0: * Returns true if this element is either a full-screen element or an michael@0: * ancestor of the full-screen element. michael@0: */ michael@0: bool IsFullScreenAncestor() const { michael@0: return mState.HasAtLeastOneOfStates(NS_EVENT_STATE_FULL_SCREEN_ANCESTOR | michael@0: NS_EVENT_STATE_FULL_SCREEN); michael@0: } michael@0: michael@0: /** michael@0: * The style state of this element. This is the real state of the element michael@0: * with any style locks applied for pseudo-class inspecting. michael@0: */ michael@0: EventStates StyleState() const michael@0: { michael@0: if (!HasLockedStyleStates()) { michael@0: return mState; michael@0: } michael@0: return StyleStateFromLocks(); michael@0: } michael@0: michael@0: /** michael@0: * The style state locks applied to this element. michael@0: */ michael@0: EventStates LockedStyleStates() const; michael@0: michael@0: /** michael@0: * Add a style state lock on this element. michael@0: */ michael@0: void LockStyleStates(EventStates aStates); michael@0: michael@0: /** michael@0: * Remove a style state lock on this element. michael@0: */ michael@0: void UnlockStyleStates(EventStates aStates); michael@0: michael@0: /** michael@0: * Clear all style state locks on this element. michael@0: */ michael@0: void ClearStyleStateLocks(); michael@0: michael@0: /** michael@0: * Get the inline style rule, if any, for this element. michael@0: */ michael@0: virtual css::StyleRule* GetInlineStyleRule(); michael@0: michael@0: /** michael@0: * Set the inline style rule for this element. This will send an appropriate michael@0: * AttributeChanged notification if aNotify is true. michael@0: */ michael@0: virtual nsresult SetInlineStyleRule(css::StyleRule* aStyleRule, michael@0: const nsAString* aSerialized, michael@0: bool aNotify); michael@0: michael@0: /** michael@0: * Get the SMIL override style rule for this element. If the rule hasn't been michael@0: * created, this method simply returns null. michael@0: */ michael@0: virtual css::StyleRule* GetSMILOverrideStyleRule(); michael@0: michael@0: /** michael@0: * Set the SMIL override style rule for this element. If aNotify is true, this michael@0: * method will notify the document's pres context, so that the style changes michael@0: * will be noticed. michael@0: */ michael@0: virtual nsresult SetSMILOverrideStyleRule(css::StyleRule* aStyleRule, michael@0: bool aNotify); michael@0: michael@0: /** michael@0: * Returns a new nsISMILAttr that allows the caller to animate the given michael@0: * attribute on this element. michael@0: * michael@0: * The CALLER OWNS the result and is responsible for deleting it. michael@0: */ michael@0: virtual nsISMILAttr* GetAnimatedAttr(int32_t aNamespaceID, nsIAtom* aName) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: /** michael@0: * Get the SMIL override style for this element. This is a style declaration michael@0: * that is applied *after* the inline style, and it can be used e.g. to store michael@0: * animated style values. michael@0: * michael@0: * Note: This method is analogous to the 'GetStyle' method in michael@0: * nsGenericHTMLElement and nsStyledElement. michael@0: */ michael@0: virtual nsICSSDeclaration* GetSMILOverrideStyle(); michael@0: michael@0: /** michael@0: * Returns if the element is labelable as per HTML specification. michael@0: */ michael@0: virtual bool IsLabelable() const; michael@0: michael@0: /** michael@0: * Is the attribute named stored in the mapped attributes? michael@0: * michael@0: * // XXXbz we use this method in HasAttributeDependentStyle, so svg michael@0: * returns true here even though it stores nothing in the mapped michael@0: * attributes. michael@0: */ michael@0: NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const; michael@0: michael@0: /** michael@0: * Get a hint that tells the style system what to do when michael@0: * an attribute on this node changes, if something needs to happen michael@0: * in response to the change *other* than the result of what is michael@0: * mapped into style data via any type of style rule. michael@0: */ michael@0: virtual nsChangeHint GetAttributeChangeHint(const nsIAtom* aAttribute, michael@0: int32_t aModType) const; michael@0: michael@0: /** michael@0: * Returns an atom holding the name of the "class" attribute on this michael@0: * content node (if applicable). Returns null if there is no michael@0: * "class" attribute for this type of content node. michael@0: */ michael@0: virtual nsIAtom *GetClassAttributeName() const; michael@0: michael@0: inline Directionality GetDirectionality() const { michael@0: if (HasFlag(NODE_HAS_DIRECTION_RTL)) { michael@0: return eDir_RTL; michael@0: } michael@0: michael@0: if (HasFlag(NODE_HAS_DIRECTION_LTR)) { michael@0: return eDir_LTR; michael@0: } michael@0: michael@0: return eDir_NotSet; michael@0: } michael@0: michael@0: inline void SetDirectionality(Directionality aDir, bool aNotify) { michael@0: UnsetFlags(NODE_ALL_DIRECTION_FLAGS); michael@0: if (!aNotify) { michael@0: RemoveStatesSilently(DIRECTION_STATES); michael@0: } michael@0: michael@0: switch (aDir) { michael@0: case (eDir_RTL): michael@0: SetFlags(NODE_HAS_DIRECTION_RTL); michael@0: if (!aNotify) { michael@0: AddStatesSilently(NS_EVENT_STATE_RTL); michael@0: } michael@0: break; michael@0: michael@0: case(eDir_LTR): michael@0: SetFlags(NODE_HAS_DIRECTION_LTR); michael@0: if (!aNotify) { michael@0: AddStatesSilently(NS_EVENT_STATE_LTR); michael@0: } michael@0: break; michael@0: michael@0: default: michael@0: break; michael@0: } michael@0: michael@0: /* michael@0: * Only call UpdateState if we need to notify, because we call michael@0: * SetDirectionality for every element, and UpdateState is very very slow michael@0: * for some elements. michael@0: */ michael@0: if (aNotify) { michael@0: UpdateState(true); michael@0: } michael@0: } michael@0: michael@0: bool GetBindingURL(nsIDocument *aDocument, css::URLValue **aResult); michael@0: michael@0: // The bdi element defaults to dir=auto if it has no dir attribute set. michael@0: // Other elements will only have dir=auto if they have an explicit dir=auto, michael@0: // which will mean that HasValidDir() returns true but HasFixedDir() returns michael@0: // false michael@0: inline bool HasDirAuto() const { michael@0: return (!HasFixedDir() && michael@0: (HasValidDir() || IsHTML(nsGkAtoms::bdi))); michael@0: } michael@0: michael@0: Directionality GetComputedDirectionality() const; michael@0: michael@0: protected: michael@0: /** michael@0: * Method to get the _intrinsic_ content state of this element. This is the michael@0: * state that is independent of the element's presentation. To get the full michael@0: * content state, use State(). See mozilla/EventStates.h for michael@0: * the possible bits that could be set here. michael@0: */ michael@0: virtual EventStates IntrinsicState() const; michael@0: michael@0: /** michael@0: * Method to add state bits. This should be called from subclass michael@0: * constructors to set up our event state correctly at construction michael@0: * time and other places where we don't want to notify a state michael@0: * change. michael@0: */ michael@0: void AddStatesSilently(EventStates aStates) michael@0: { michael@0: mState |= aStates; michael@0: } michael@0: michael@0: /** michael@0: * Method to remove state bits. This should be called from subclass michael@0: * constructors to set up our event state correctly at construction michael@0: * time and other places where we don't want to notify a state michael@0: * change. michael@0: */ michael@0: void RemoveStatesSilently(EventStates aStates) michael@0: { michael@0: mState &= ~aStates; michael@0: } michael@0: michael@0: private: michael@0: // Need to allow the ESM, nsGlobalWindow, and the focus manager to michael@0: // set our state michael@0: friend class mozilla::EventStateManager; michael@0: friend class ::nsGlobalWindow; michael@0: friend class ::nsFocusManager; michael@0: michael@0: // Also need to allow Link to call UpdateLinkState. michael@0: friend class Link; michael@0: michael@0: void NotifyStateChange(EventStates aStates); michael@0: michael@0: void NotifyStyleStateChange(EventStates aStates); michael@0: michael@0: // Style state computed from element's state and style locks. michael@0: EventStates StyleStateFromLocks() const; michael@0: michael@0: protected: michael@0: // Methods for the ESM to manage state bits. These will handle michael@0: // setting up script blockers when they notify, so no need to do it michael@0: // in the callers unless desired. michael@0: virtual void AddStates(EventStates aStates) michael@0: { michael@0: NS_PRECONDITION(!aStates.HasAtLeastOneOfStates(INTRINSIC_STATES), michael@0: "Should only be adding ESM-managed states here"); michael@0: AddStatesSilently(aStates); michael@0: NotifyStateChange(aStates); michael@0: } michael@0: virtual void RemoveStates(EventStates aStates) michael@0: { michael@0: NS_PRECONDITION(!aStates.HasAtLeastOneOfStates(INTRINSIC_STATES), michael@0: "Should only be removing ESM-managed states here"); michael@0: RemoveStatesSilently(aStates); michael@0: NotifyStateChange(aStates); michael@0: } michael@0: public: michael@0: virtual void UpdateEditableState(bool aNotify) MOZ_OVERRIDE; michael@0: michael@0: virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent, michael@0: nsIContent* aBindingParent, michael@0: bool aCompileEventHandlers) MOZ_OVERRIDE; michael@0: virtual void UnbindFromTree(bool aDeep = true, michael@0: bool aNullParent = true) MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Normalizes an attribute name and returns it as a nodeinfo if an attribute michael@0: * with that name exists. This method is intended for character case michael@0: * conversion if the content object is case insensitive (e.g. HTML). Returns michael@0: * the nodeinfo of the attribute with the specified name if one exists or michael@0: * null otherwise. michael@0: * michael@0: * @param aStr the unparsed attribute string michael@0: * @return the node info. May be nullptr. michael@0: */ michael@0: already_AddRefed michael@0: GetExistingAttrNameFromQName(const nsAString& aStr) const; michael@0: michael@0: nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, michael@0: const nsAString& aValue, bool aNotify) michael@0: { michael@0: return SetAttr(aNameSpaceID, aName, nullptr, aValue, aNotify); michael@0: } michael@0: michael@0: /** michael@0: * Helper for SetAttr/SetParsedAttr. This method will return true if aNotify michael@0: * is true or there are mutation listeners that must be triggered, the michael@0: * attribute is currently set, and the new value that is about to be set is michael@0: * different to the current value. As a perf optimization the new and old michael@0: * values will not actually be compared if we aren't notifying and we don't michael@0: * have mutation listeners (in which case it's cheap to just return false michael@0: * and let the caller go ahead and set the value). michael@0: * @param aOldValue Set to the old value of the attribute, but only if there michael@0: * are event listeners. If set, the type of aOldValue will be either michael@0: * nsAttrValue::eString or nsAttrValue::eAtom. michael@0: * @param aModType Set to nsIDOMMutationEvent::MODIFICATION or to michael@0: * nsIDOMMutationEvent::ADDITION, but only if this helper returns true michael@0: * @param aHasListeners Set to true if there are mutation event listeners michael@0: * listening for NS_EVENT_BITS_MUTATION_ATTRMODIFIED michael@0: */ michael@0: bool MaybeCheckSameAttrVal(int32_t aNamespaceID, nsIAtom* aName, michael@0: nsIAtom* aPrefix, michael@0: const nsAttrValueOrString& aValue, michael@0: bool aNotify, nsAttrValue& aOldValue, michael@0: uint8_t* aModType, bool* aHasListeners); michael@0: michael@0: bool OnlyNotifySameValueSet(int32_t aNamespaceID, nsIAtom* aName, michael@0: nsIAtom* aPrefix, michael@0: const nsAttrValueOrString& aValue, michael@0: bool aNotify, nsAttrValue& aOldValue, michael@0: uint8_t* aModType, bool* aHasListeners); michael@0: michael@0: virtual nsresult SetAttr(int32_t aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix, michael@0: const nsAString& aValue, bool aNotify) MOZ_OVERRIDE; michael@0: nsresult SetParsedAttr(int32_t aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix, michael@0: nsAttrValue& aParsedValue, bool aNotify); michael@0: // GetAttr is not inlined on purpose, to keep down codesize from all michael@0: // the inlined nsAttrValue bits for C++ callers. michael@0: bool GetAttr(int32_t aNameSpaceID, nsIAtom* aName, michael@0: nsAString& aResult) const; michael@0: inline bool HasAttr(int32_t aNameSpaceID, nsIAtom* aName) const; michael@0: // aCaseSensitive == eIgnoreCaase means ASCII case-insensitive matching. michael@0: inline bool AttrValueIs(int32_t aNameSpaceID, nsIAtom* aName, michael@0: const nsAString& aValue, michael@0: nsCaseTreatment aCaseSensitive) const; michael@0: inline bool AttrValueIs(int32_t aNameSpaceID, nsIAtom* aName, michael@0: nsIAtom* aValue, michael@0: nsCaseTreatment aCaseSensitive) const; michael@0: virtual int32_t FindAttrValueIn(int32_t aNameSpaceID, michael@0: nsIAtom* aName, michael@0: AttrValuesArray* aValues, michael@0: nsCaseTreatment aCaseSensitive) const MOZ_OVERRIDE; michael@0: virtual nsresult UnsetAttr(int32_t aNameSpaceID, nsIAtom* aAttribute, michael@0: bool aNotify) MOZ_OVERRIDE; michael@0: virtual const nsAttrName* GetAttrNameAt(uint32_t aIndex) const MOZ_OVERRIDE; michael@0: virtual uint32_t GetAttrCount() const MOZ_OVERRIDE; michael@0: virtual bool IsNodeOfType(uint32_t aFlags) const MOZ_OVERRIDE; michael@0: michael@0: #ifdef DEBUG michael@0: virtual void List(FILE* out = stdout, int32_t aIndent = 0) const MOZ_OVERRIDE michael@0: { michael@0: List(out, aIndent, EmptyCString()); michael@0: } michael@0: virtual void DumpContent(FILE* out, int32_t aIndent, bool aDumpAll) const MOZ_OVERRIDE; michael@0: void List(FILE* out, int32_t aIndent, const nsCString& aPrefix) const; michael@0: void ListAttributes(FILE* out) const; michael@0: #endif michael@0: michael@0: void Describe(nsAString& aOutDescription) const MOZ_OVERRIDE; michael@0: michael@0: /* michael@0: * Attribute Mapping Helpers michael@0: */ michael@0: struct MappedAttributeEntry { michael@0: nsIAtom** attribute; michael@0: }; michael@0: michael@0: /** michael@0: * A common method where you can just pass in a list of maps to check michael@0: * for attribute dependence. Most implementations of michael@0: * IsAttributeMapped should use this function as a default michael@0: * handler. michael@0: */ michael@0: template michael@0: static bool michael@0: FindAttributeDependence(const nsIAtom* aAttribute, michael@0: const MappedAttributeEntry* const (&aMaps)[N]) michael@0: { michael@0: return FindAttributeDependence(aAttribute, aMaps, N); michael@0: } michael@0: michael@0: private: michael@0: void DescribeAttribute(uint32_t index, nsAString& aOutDescription) const; michael@0: michael@0: static bool michael@0: FindAttributeDependence(const nsIAtom* aAttribute, michael@0: const MappedAttributeEntry* const aMaps[], michael@0: uint32_t aMapCount); michael@0: michael@0: protected: michael@0: inline bool GetAttr(int32_t aNameSpaceID, nsIAtom* aName, michael@0: DOMString& aResult) const michael@0: { michael@0: NS_ASSERTION(nullptr != aName, "must have attribute name"); michael@0: NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown, michael@0: "must have a real namespace ID!"); michael@0: MOZ_ASSERT(aResult.HasStringBuffer() && aResult.StringBufferLength() == 0, michael@0: "Should have empty string coming in"); michael@0: const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName, aNameSpaceID); michael@0: if (val) { michael@0: val->ToString(aResult); michael@0: return true; michael@0: } michael@0: // else DOMString comes pre-emptied. michael@0: return false; michael@0: } michael@0: michael@0: public: michael@0: bool HasAttrs() const { return mAttrsAndChildren.HasAttrs(); } michael@0: michael@0: inline bool GetAttr(const nsAString& aName, DOMString& aResult) const michael@0: { michael@0: MOZ_ASSERT(aResult.HasStringBuffer() && aResult.StringBufferLength() == 0, michael@0: "Should have empty string coming in"); michael@0: const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName); michael@0: if (val) { michael@0: val->ToString(aResult); michael@0: return true; michael@0: } michael@0: // else DOMString comes pre-emptied. michael@0: return false; michael@0: } michael@0: michael@0: void GetTagName(nsAString& aTagName) const michael@0: { michael@0: aTagName = NodeName(); michael@0: } michael@0: void GetId(nsAString& aId) const michael@0: { michael@0: GetAttr(kNameSpaceID_None, nsGkAtoms::id, aId); michael@0: } michael@0: void GetId(DOMString& aId) const michael@0: { michael@0: GetAttr(kNameSpaceID_None, nsGkAtoms::id, aId); michael@0: } michael@0: void SetId(const nsAString& aId) michael@0: { michael@0: SetAttr(kNameSpaceID_None, nsGkAtoms::id, aId, true); michael@0: } michael@0: michael@0: nsDOMTokenList* GetClassList(); michael@0: nsDOMAttributeMap* Attributes() michael@0: { michael@0: nsDOMSlots* slots = DOMSlots(); michael@0: if (!slots->mAttributeMap) { michael@0: slots->mAttributeMap = new nsDOMAttributeMap(this); michael@0: } michael@0: michael@0: return slots->mAttributeMap; michael@0: } michael@0: void GetAttribute(const nsAString& aName, nsString& aReturn) michael@0: { michael@0: DOMString str; michael@0: GetAttribute(aName, str); michael@0: str.ToString(aReturn); michael@0: } michael@0: michael@0: void GetAttribute(const nsAString& aName, DOMString& aReturn); michael@0: void GetAttributeNS(const nsAString& aNamespaceURI, michael@0: const nsAString& aLocalName, michael@0: nsAString& aReturn); michael@0: void SetAttribute(const nsAString& aName, const nsAString& aValue, michael@0: ErrorResult& aError); michael@0: void SetAttributeNS(const nsAString& aNamespaceURI, michael@0: const nsAString& aLocalName, michael@0: const nsAString& aValue, michael@0: ErrorResult& aError); michael@0: void RemoveAttribute(const nsAString& aName, michael@0: ErrorResult& aError); michael@0: void RemoveAttributeNS(const nsAString& aNamespaceURI, michael@0: const nsAString& aLocalName, michael@0: ErrorResult& aError); michael@0: bool HasAttribute(const nsAString& aName) const michael@0: { michael@0: return InternalGetExistingAttrNameFromQName(aName) != nullptr; michael@0: } michael@0: bool HasAttributeNS(const nsAString& aNamespaceURI, michael@0: const nsAString& aLocalName) const; michael@0: already_AddRefed michael@0: GetElementsByTagName(const nsAString& aQualifiedName); michael@0: already_AddRefed michael@0: GetElementsByTagNameNS(const nsAString& aNamespaceURI, michael@0: const nsAString& aLocalName, michael@0: ErrorResult& aError); michael@0: already_AddRefed michael@0: GetElementsByClassName(const nsAString& aClassNames); michael@0: bool MozMatchesSelector(const nsAString& aSelector, michael@0: ErrorResult& aError); michael@0: void SetPointerCapture(int32_t aPointerId, ErrorResult& aError) michael@0: { michael@0: bool activeState = false; michael@0: if (!nsIPresShell::GetPointerInfo(aPointerId, activeState)) { michael@0: aError.Throw(NS_ERROR_DOM_INVALID_POINTER_ERR); michael@0: return; michael@0: } michael@0: if (!activeState) { michael@0: return; michael@0: } michael@0: nsIPresShell::SetPointerCapturingContent(aPointerId, this); michael@0: } michael@0: void ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError) michael@0: { michael@0: bool activeState = false; michael@0: if (!nsIPresShell::GetPointerInfo(aPointerId, activeState)) { michael@0: aError.Throw(NS_ERROR_DOM_INVALID_POINTER_ERR); michael@0: return; michael@0: } michael@0: michael@0: // Ignoring ReleasePointerCapture call on incorrect element (on element michael@0: // that didn't have capture before). michael@0: if (nsIPresShell::GetPointerCapturingContent(aPointerId) == this) { michael@0: nsIPresShell::ReleasePointerCapturingContent(aPointerId, this); michael@0: } michael@0: } michael@0: void SetCapture(bool aRetargetToElement) michael@0: { michael@0: // If there is already an active capture, ignore this request. This would michael@0: // occur if a splitter, frame resizer, etc had already captured and we don't michael@0: // want to override those. michael@0: if (!nsIPresShell::GetCapturingContent()) { michael@0: nsIPresShell::SetCapturingContent(this, CAPTURE_PREVENTDRAG | michael@0: (aRetargetToElement ? CAPTURE_RETARGETTOELEMENT : 0)); michael@0: } michael@0: } michael@0: void ReleaseCapture() michael@0: { michael@0: if (nsIPresShell::GetCapturingContent() == this) { michael@0: nsIPresShell::SetCapturingContent(nullptr, 0); michael@0: } michael@0: } michael@0: void MozRequestFullScreen(); michael@0: void MozRequestPointerLock(); michael@0: Attr* GetAttributeNode(const nsAString& aName); michael@0: already_AddRefed SetAttributeNode(Attr& aNewAttr, michael@0: ErrorResult& aError); michael@0: already_AddRefed RemoveAttributeNode(Attr& aOldAttr, michael@0: ErrorResult& aError); michael@0: Attr* GetAttributeNodeNS(const nsAString& aNamespaceURI, michael@0: const nsAString& aLocalName); michael@0: already_AddRefed SetAttributeNodeNS(Attr& aNewAttr, michael@0: ErrorResult& aError); michael@0: michael@0: already_AddRefed GetClientRects(); michael@0: already_AddRefed GetBoundingClientRect(); michael@0: michael@0: already_AddRefed CreateShadowRoot(ErrorResult& aError); michael@0: michael@0: void ScrollIntoView() michael@0: { michael@0: ScrollIntoView(true); michael@0: } michael@0: void ScrollIntoView(bool aTop); michael@0: int32_t ScrollTop() michael@0: { michael@0: nsIScrollableFrame* sf = GetScrollFrame(); michael@0: return sf ? sf->GetScrollPositionCSSPixels().y : 0; michael@0: } michael@0: void SetScrollTop(int32_t aScrollTop) michael@0: { michael@0: nsIScrollableFrame* sf = GetScrollFrame(); michael@0: if (sf) { michael@0: sf->ScrollToCSSPixels(CSSIntPoint(sf->GetScrollPositionCSSPixels().x, michael@0: aScrollTop)); michael@0: } michael@0: } michael@0: int32_t ScrollLeft() michael@0: { michael@0: nsIScrollableFrame* sf = GetScrollFrame(); michael@0: return sf ? sf->GetScrollPositionCSSPixels().x : 0; michael@0: } michael@0: void SetScrollLeft(int32_t aScrollLeft) michael@0: { michael@0: nsIScrollableFrame* sf = GetScrollFrame(); michael@0: if (sf) { michael@0: sf->ScrollToCSSPixels(CSSIntPoint(aScrollLeft, michael@0: sf->GetScrollPositionCSSPixels().y)); michael@0: } michael@0: } michael@0: /* Scrolls without flushing the layout. michael@0: * aDx is the x offset, aDy the y offset in CSS pixels. michael@0: * Returns true if we actually scrolled. michael@0: */ michael@0: bool ScrollByNoFlush(int32_t aDx, int32_t aDy); michael@0: int32_t ScrollWidth(); michael@0: int32_t ScrollHeight(); michael@0: int32_t ClientTop() michael@0: { michael@0: return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().y); michael@0: } michael@0: int32_t ClientLeft() michael@0: { michael@0: return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().x); michael@0: } michael@0: int32_t ClientWidth() michael@0: { michael@0: return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().width); michael@0: } michael@0: int32_t ClientHeight() michael@0: { michael@0: return nsPresContext::AppUnitsToIntCSSPixels(GetClientAreaRect().height); michael@0: } michael@0: int32_t ScrollTopMax() michael@0: { michael@0: nsIScrollableFrame* sf = GetScrollFrame(); michael@0: return sf ? michael@0: nsPresContext::AppUnitsToIntCSSPixels(sf->GetScrollRange().YMost()) : michael@0: 0; michael@0: } michael@0: int32_t ScrollLeftMax() michael@0: { michael@0: nsIScrollableFrame* sf = GetScrollFrame(); michael@0: return sf ? michael@0: nsPresContext::AppUnitsToIntCSSPixels(sf->GetScrollRange().XMost()) : michael@0: 0; michael@0: } michael@0: michael@0: virtual already_AddRefed GetUndoManager() michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: virtual bool UndoScope() michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: virtual void SetUndoScope(bool aUndoScope, ErrorResult& aError) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHOD GetInnerHTML(nsAString& aInnerHTML); michael@0: virtual void SetInnerHTML(const nsAString& aInnerHTML, ErrorResult& aError); michael@0: void GetOuterHTML(nsAString& aOuterHTML); michael@0: void SetOuterHTML(const nsAString& aOuterHTML, ErrorResult& aError); michael@0: void InsertAdjacentHTML(const nsAString& aPosition, const nsAString& aText, michael@0: ErrorResult& aError); michael@0: michael@0: //---------------------------------------- michael@0: michael@0: /** michael@0: * Add a script event listener with the given event handler name michael@0: * (like onclick) and with the value as JS michael@0: * @param aEventName the event listener name michael@0: * @param aValue the JS to attach michael@0: * @param aDefer indicates if deferred execution is allowed michael@0: */ michael@0: nsresult SetEventHandler(nsIAtom* aEventName, michael@0: const nsAString& aValue, michael@0: bool aDefer = true); michael@0: michael@0: /** michael@0: * Do whatever needs to be done when the mouse leaves a link michael@0: */ michael@0: nsresult LeaveLink(nsPresContext* aPresContext); michael@0: michael@0: static bool ShouldBlur(nsIContent *aContent); michael@0: michael@0: /** michael@0: * Method to create and dispatch a left-click event loosely based on michael@0: * aSourceEvent. If aFullDispatch is true, the event will be dispatched michael@0: * through the full dispatching of the presshell of the aPresContext; if it's michael@0: * false the event will be dispatched only as a DOM event. michael@0: * If aPresContext is nullptr, this does nothing. michael@0: * michael@0: * @param aFlags Extra flags for the dispatching event. The true flags michael@0: * will be respected. michael@0: */ michael@0: static nsresult DispatchClickEvent(nsPresContext* aPresContext, michael@0: WidgetInputEvent* aSourceEvent, michael@0: nsIContent* aTarget, michael@0: bool aFullDispatch, michael@0: const EventFlags* aFlags, michael@0: nsEventStatus* aStatus); michael@0: michael@0: /** michael@0: * Method to dispatch aEvent to aTarget. If aFullDispatch is true, the event michael@0: * will be dispatched through the full dispatching of the presshell of the michael@0: * aPresContext; if it's false the event will be dispatched only as a DOM michael@0: * event. michael@0: * If aPresContext is nullptr, this does nothing. michael@0: */ michael@0: using nsIContent::DispatchEvent; michael@0: static nsresult DispatchEvent(nsPresContext* aPresContext, michael@0: WidgetEvent* aEvent, michael@0: nsIContent* aTarget, michael@0: bool aFullDispatch, michael@0: nsEventStatus* aStatus); michael@0: michael@0: /** michael@0: * Get the primary frame for this content with flushing michael@0: * michael@0: * @param aType the kind of flush to do, typically Flush_Frames or michael@0: * Flush_Layout michael@0: * @return the primary frame michael@0: */ michael@0: nsIFrame* GetPrimaryFrame(mozFlushType aType); michael@0: // Work around silly C++ name hiding stuff michael@0: nsIFrame* GetPrimaryFrame() const { return nsIContent::GetPrimaryFrame(); } michael@0: michael@0: /** michael@0: * Struct that stores info on an attribute. The name and value must michael@0: * either both be null or both be non-null. michael@0: */ michael@0: struct nsAttrInfo { michael@0: nsAttrInfo(const nsAttrName* aName, const nsAttrValue* aValue) : michael@0: mName(aName), mValue(aValue) {} michael@0: nsAttrInfo(const nsAttrInfo& aOther) : michael@0: mName(aOther.mName), mValue(aOther.mValue) {} michael@0: michael@0: const nsAttrName* mName; michael@0: const nsAttrValue* mValue; michael@0: }; michael@0: michael@0: // Be careful when using this method. This does *NOT* handle michael@0: // XUL prototypes. You may want to use GetAttrInfo. michael@0: const nsAttrValue* GetParsedAttr(nsIAtom* aAttr) const michael@0: { michael@0: return mAttrsAndChildren.GetAttr(aAttr); michael@0: } michael@0: michael@0: /** michael@0: * Returns the attribute map, if there is one. michael@0: * michael@0: * @return existing attribute map or nullptr. michael@0: */ michael@0: nsDOMAttributeMap *GetAttributeMap() michael@0: { michael@0: nsDOMSlots *slots = GetExistingDOMSlots(); michael@0: michael@0: return slots ? slots->mAttributeMap.get() : nullptr; michael@0: } michael@0: michael@0: virtual void RecompileScriptEventListeners() michael@0: { michael@0: } michael@0: michael@0: /** michael@0: * Get the attr info for the given namespace ID and attribute name. The michael@0: * namespace ID must not be kNameSpaceID_Unknown and the name must not be michael@0: * null. Note that this can only return info on attributes that actually michael@0: * live on this element (and is only virtual to handle XUL prototypes). That michael@0: * is, this should only be called from methods that only care about attrs michael@0: * that effectively live in mAttrsAndChildren. michael@0: */ michael@0: virtual nsAttrInfo GetAttrInfo(int32_t aNamespaceID, nsIAtom* aName) const; michael@0: michael@0: virtual void NodeInfoChanged(nsINodeInfo* aOldNodeInfo) michael@0: { michael@0: } michael@0: michael@0: /** michael@0: * Parse a string into an nsAttrValue for a CORS attribute. This michael@0: * never fails. The resulting value is an enumerated value whose michael@0: * GetEnumValue() returns one of the above constants. michael@0: */ michael@0: static void ParseCORSValue(const nsAString& aValue, nsAttrValue& aResult); michael@0: michael@0: /** michael@0: * Return the CORS mode for a given string michael@0: */ michael@0: static CORSMode StringToCORSMode(const nsAString& aValue); michael@0: michael@0: /** michael@0: * Return the CORS mode for a given nsAttrValue (which may be null, michael@0: * but if not should have been parsed via ParseCORSValue). michael@0: */ michael@0: static CORSMode AttrValueToCORSMode(const nsAttrValue* aValue); michael@0: michael@0: // These are just used to implement nsIDOMElement using michael@0: // NS_FORWARD_NSIDOMELEMENT_TO_GENERIC and for quickstubs. michael@0: void michael@0: GetElementsByTagName(const nsAString& aQualifiedName, michael@0: nsIDOMHTMLCollection** aResult); michael@0: nsresult michael@0: GetElementsByTagNameNS(const nsAString& aNamespaceURI, michael@0: const nsAString& aLocalName, michael@0: nsIDOMHTMLCollection** aResult); michael@0: nsresult michael@0: GetElementsByClassName(const nsAString& aClassNames, michael@0: nsIDOMHTMLCollection** aResult); michael@0: void GetClassList(nsISupports** aClassList); michael@0: michael@0: virtual JSObject* WrapObject(JSContext *aCx) MOZ_FINAL MOZ_OVERRIDE; michael@0: michael@0: /** michael@0: * Locate an nsIEditor rooted at this content node, if there is one. michael@0: */ michael@0: nsIEditor* GetEditorInternal(); michael@0: michael@0: /** michael@0: * Helper method for NS_IMPL_BOOL_ATTR macro. michael@0: * Gets value of boolean attribute. Only works for attributes in null michael@0: * namespace. michael@0: * michael@0: * @param aAttr name of attribute. michael@0: * @param aValue Boolean value of attribute. michael@0: */ michael@0: NS_HIDDEN_(bool) GetBoolAttr(nsIAtom* aAttr) const michael@0: { michael@0: return HasAttr(kNameSpaceID_None, aAttr); michael@0: } michael@0: michael@0: /** michael@0: * Helper method for NS_IMPL_BOOL_ATTR macro. michael@0: * Sets value of boolean attribute by removing attribute or setting it to michael@0: * the empty string. Only works for attributes in null namespace. michael@0: * michael@0: * @param aAttr name of attribute. michael@0: * @param aValue Boolean value of attribute. michael@0: */ michael@0: NS_HIDDEN_(nsresult) SetBoolAttr(nsIAtom* aAttr, bool aValue); michael@0: michael@0: /** michael@0: * Retrieve the ratio of font-size-inflated text font size to computed font michael@0: * size for this element. This will query the element for its primary frame, michael@0: * and then use this to get font size inflation information about the frame. michael@0: * michael@0: * @returns The font size inflation ratio (inflated font size to uninflated michael@0: * font size) for the primary frame of this element. Returns 1.0 michael@0: * by default if font size inflation is not enabled. Returns -1 michael@0: * if the element does not have a primary frame. michael@0: * michael@0: * @note The font size inflation ratio that is returned is actually the michael@0: * font size inflation data for the element's _primary frame_, not the michael@0: * element itself, but for most purposes, this should be sufficient. michael@0: */ michael@0: float FontSizeInflation(); michael@0: michael@0: protected: michael@0: /* michael@0: * Named-bools for use with SetAttrAndNotify to make call sites easier to michael@0: * read. michael@0: */ michael@0: static const bool kFireMutationEvent = true; michael@0: static const bool kDontFireMutationEvent = false; michael@0: static const bool kNotifyDocumentObservers = true; michael@0: static const bool kDontNotifyDocumentObservers = false; michael@0: static const bool kCallAfterSetAttr = true; michael@0: static const bool kDontCallAfterSetAttr = false; michael@0: michael@0: /** michael@0: * Set attribute and (if needed) notify documentobservers and fire off michael@0: * mutation events. This will send the AttributeChanged notification. michael@0: * Callers of this method are responsible for calling AttributeWillChange, michael@0: * since that needs to happen before the new attr value has been set, and michael@0: * in particular before it has been parsed. michael@0: * michael@0: * For the boolean parameters, consider using the named bools above to aid michael@0: * code readability. michael@0: * michael@0: * @param aNamespaceID namespace of attribute michael@0: * @param aAttribute local-name of attribute michael@0: * @param aPrefix aPrefix of attribute michael@0: * @param aOldValue previous value of attribute. Only needed if michael@0: * aFireMutation is true or if the element is a michael@0: * custom element (in web components). michael@0: * @param aParsedValue parsed new value of attribute michael@0: * @param aModType nsIDOMMutationEvent::MODIFICATION or ADDITION. Only michael@0: * needed if aFireMutation or aNotify is true. michael@0: * @param aFireMutation should mutation-events be fired? michael@0: * @param aNotify should we notify document-observers? michael@0: * @param aCallAfterSetAttr should we call AfterSetAttr? michael@0: */ michael@0: nsresult SetAttrAndNotify(int32_t aNamespaceID, michael@0: nsIAtom* aName, michael@0: nsIAtom* aPrefix, michael@0: const nsAttrValue& aOldValue, michael@0: nsAttrValue& aParsedValue, michael@0: uint8_t aModType, michael@0: bool aFireMutation, michael@0: bool aNotify, michael@0: bool aCallAfterSetAttr); michael@0: michael@0: /** michael@0: * Convert an attribute string value to attribute type based on the type of michael@0: * attribute. Called by SetAttr(). Note that at the moment we only do this michael@0: * for attributes in the null namespace (kNameSpaceID_None). michael@0: * michael@0: * @param aNamespaceID the namespace of the attribute to convert michael@0: * @param aAttribute the attribute to convert michael@0: * @param aValue the string value to convert michael@0: * @param aResult the nsAttrValue [OUT] michael@0: * @return true if the parsing was successful, false otherwise michael@0: */ michael@0: virtual bool ParseAttribute(int32_t aNamespaceID, michael@0: nsIAtom* aAttribute, michael@0: const nsAString& aValue, michael@0: nsAttrValue& aResult); michael@0: michael@0: /** michael@0: * Try to set the attribute as a mapped attribute, if applicable. This will michael@0: * only be called for attributes that are in the null namespace and only on michael@0: * attributes that returned true when passed to IsAttributeMapped. The michael@0: * caller will not try to set the attr in any other way if this method michael@0: * returns true (the value of aRetval does not matter for that purpose). michael@0: * michael@0: * @param aDocument the current document of this node (an optimization) michael@0: * @param aName the name of the attribute michael@0: * @param aValue the nsAttrValue to set michael@0: * @param [out] aRetval the nsresult status of the operation, if any. michael@0: * @return true if the setting was attempted, false otherwise. michael@0: */ michael@0: virtual bool SetMappedAttribute(nsIDocument* aDocument, michael@0: nsIAtom* aName, michael@0: nsAttrValue& aValue, michael@0: nsresult* aRetval); michael@0: michael@0: /** michael@0: * Hook that is called by Element::SetAttr to allow subclasses to michael@0: * deal with attribute sets. This will only be called after we verify that michael@0: * we're actually doing an attr set and will be called before michael@0: * AttributeWillChange and before ParseAttribute and hence before we've set michael@0: * the new value. michael@0: * michael@0: * @param aNamespaceID the namespace of the attr being set michael@0: * @param aName the localname of the attribute being set michael@0: * @param aValue the value it's being set to represented as either a string or michael@0: * a parsed nsAttrValue. Alternatively, if the attr is being removed it michael@0: * will be null. michael@0: * @param aNotify Whether we plan to notify document observers. michael@0: */ michael@0: // Note that this is inlined so that when subclasses call it it gets michael@0: // inlined. Those calls don't go through a vtable. michael@0: virtual nsresult BeforeSetAttr(int32_t aNamespaceID, nsIAtom* aName, michael@0: const nsAttrValueOrString* aValue, michael@0: bool aNotify) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * Hook that is called by Element::SetAttr to allow subclasses to michael@0: * deal with attribute sets. This will only be called after we have called michael@0: * SetAndTakeAttr and AttributeChanged (that is, after we have actually set michael@0: * the attr). It will always be called under a scriptblocker. michael@0: * michael@0: * @param aNamespaceID the namespace of the attr being set michael@0: * @param aName the localname of the attribute being set michael@0: * @param aValue the value it's being set to. If null, the attr is being michael@0: * removed. michael@0: * @param aNotify Whether we plan to notify document observers. michael@0: */ michael@0: // Note that this is inlined so that when subclasses call it it gets michael@0: // inlined. Those calls don't go through a vtable. michael@0: virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom* aName, michael@0: const nsAttrValue* aValue, bool aNotify) michael@0: { michael@0: return NS_OK; michael@0: } michael@0: michael@0: /** michael@0: * Hook to allow subclasses to produce a different EventListenerManager if michael@0: * needed for attachment of attribute-defined handlers michael@0: */ michael@0: virtual EventListenerManager* michael@0: GetEventListenerManagerForAttr(nsIAtom* aAttrName, bool* aDefer); michael@0: michael@0: /** michael@0: * Internal hook for converting an attribute name-string to an atomized name michael@0: */ michael@0: virtual const nsAttrName* InternalGetExistingAttrNameFromQName(const nsAString& aStr) const; michael@0: michael@0: nsIFrame* GetStyledFrame(); michael@0: michael@0: virtual Element* GetNameSpaceElement() michael@0: { michael@0: return this; michael@0: } michael@0: michael@0: Attr* GetAttributeNodeNSInternal(const nsAString& aNamespaceURI, michael@0: const nsAString& aLocalName); michael@0: michael@0: inline void RegisterFreezableElement(); michael@0: inline void UnregisterFreezableElement(); michael@0: michael@0: /** michael@0: * Add/remove this element to the documents id cache michael@0: */ michael@0: void AddToIdTable(nsIAtom* aId); michael@0: void RemoveFromIdTable(); // checks HasID() and uses DoGetID() michael@0: void RemoveFromIdTable(nsIAtom* aId); michael@0: michael@0: /** michael@0: * Functions to carry out event default actions for links of all types michael@0: * (HTML links, XLinks, SVG "XLinks", etc.) michael@0: */ michael@0: michael@0: /** michael@0: * Check that we meet the conditions to handle a link event michael@0: * and that we are actually on a link. michael@0: * michael@0: * @param aVisitor event visitor michael@0: * @param aURI the uri of the link, set only if the return value is true [OUT] michael@0: * @return true if we can handle the link event, false otherwise michael@0: */ michael@0: bool CheckHandleEventForLinksPrecondition(EventChainVisitor& aVisitor, michael@0: nsIURI** aURI) const; michael@0: michael@0: /** michael@0: * Handle status bar updates before they can be cancelled. michael@0: */ michael@0: nsresult PreHandleEventForLinks(EventChainPreVisitor& aVisitor); michael@0: michael@0: /** michael@0: * Handle default actions for link event if the event isn't consumed yet. michael@0: */ michael@0: nsresult PostHandleEventForLinks(EventChainPostVisitor& aVisitor); michael@0: michael@0: /** michael@0: * Get the target of this link element. Consumers should established that michael@0: * this element is a link (probably using IsLink) before calling this michael@0: * function (or else why call it?) michael@0: * michael@0: * Note: for HTML this gets the value of the 'target' attribute; for XLink michael@0: * this gets the value of the xlink:_moz_target attribute, or failing that, michael@0: * the value of xlink:show, converted to a suitably equivalent named target michael@0: * (e.g. _blank). michael@0: */ michael@0: virtual void GetLinkTarget(nsAString& aTarget); michael@0: michael@0: private: michael@0: /** michael@0: * Get this element's client area rect in app units. michael@0: * @return the frame's client area michael@0: */ michael@0: nsRect GetClientAreaRect(); michael@0: michael@0: nsIScrollableFrame* GetScrollFrame(nsIFrame **aStyledFrame = nullptr, michael@0: bool aFlushLayout = true); michael@0: michael@0: // Data members michael@0: EventStates mState; michael@0: }; michael@0: michael@0: NS_DEFINE_STATIC_IID_ACCESSOR(Element, NS_ELEMENT_IID) michael@0: michael@0: inline bool michael@0: Element::HasAttr(int32_t aNameSpaceID, nsIAtom* aName) const michael@0: { michael@0: NS_ASSERTION(nullptr != aName, "must have attribute name"); michael@0: NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown, michael@0: "must have a real namespace ID!"); michael@0: michael@0: return mAttrsAndChildren.IndexOfAttr(aName, aNameSpaceID) >= 0; michael@0: } michael@0: michael@0: inline bool michael@0: Element::AttrValueIs(int32_t aNameSpaceID, michael@0: nsIAtom* aName, michael@0: const nsAString& aValue, michael@0: nsCaseTreatment aCaseSensitive) const michael@0: { michael@0: NS_ASSERTION(aName, "Must have attr name"); michael@0: NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown, "Must have namespace"); michael@0: michael@0: const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName, aNameSpaceID); michael@0: return val && val->Equals(aValue, aCaseSensitive); michael@0: } michael@0: michael@0: inline bool michael@0: Element::AttrValueIs(int32_t aNameSpaceID, michael@0: nsIAtom* aName, michael@0: nsIAtom* aValue, michael@0: nsCaseTreatment aCaseSensitive) const michael@0: { michael@0: NS_ASSERTION(aName, "Must have attr name"); michael@0: NS_ASSERTION(aNameSpaceID != kNameSpaceID_Unknown, "Must have namespace"); michael@0: NS_ASSERTION(aValue, "Null value atom"); michael@0: michael@0: const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName, aNameSpaceID); michael@0: return val && val->Equals(aValue, aCaseSensitive); michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla michael@0: michael@0: inline mozilla::dom::Element* nsINode::AsElement() michael@0: { michael@0: MOZ_ASSERT(IsElement()); michael@0: return static_cast(this); michael@0: } michael@0: michael@0: inline const mozilla::dom::Element* nsINode::AsElement() const michael@0: { michael@0: MOZ_ASSERT(IsElement()); michael@0: return static_cast(this); michael@0: } michael@0: michael@0: inline bool nsINode::HasAttributes() const michael@0: { michael@0: return IsElement() && AsElement()->HasAttrs(); michael@0: } michael@0: michael@0: /** michael@0: * Macros to implement Clone(). _elementName is the class for which to implement michael@0: * Clone. michael@0: */ michael@0: #define NS_IMPL_ELEMENT_CLONE(_elementName) \ michael@0: nsresult \ michael@0: _elementName::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const \ michael@0: { \ michael@0: *aResult = nullptr; \ michael@0: already_AddRefed ni = \ michael@0: nsCOMPtr(aNodeInfo).forget(); \ michael@0: _elementName *it = new _elementName(ni); \ michael@0: if (!it) { \ michael@0: return NS_ERROR_OUT_OF_MEMORY; \ michael@0: } \ michael@0: \ michael@0: nsCOMPtr kungFuDeathGrip = it; \ michael@0: nsresult rv = const_cast<_elementName*>(this)->CopyInnerTo(it); \ michael@0: if (NS_SUCCEEDED(rv)) { \ michael@0: kungFuDeathGrip.swap(*aResult); \ michael@0: } \ michael@0: \ michael@0: return rv; \ michael@0: } michael@0: michael@0: #define NS_IMPL_ELEMENT_CLONE_WITH_INIT(_elementName) \ michael@0: nsresult \ michael@0: _elementName::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const \ michael@0: { \ michael@0: *aResult = nullptr; \ michael@0: already_AddRefed ni = \ michael@0: nsCOMPtr(aNodeInfo).forget(); \ michael@0: _elementName *it = new _elementName(ni); \ michael@0: if (!it) { \ michael@0: return NS_ERROR_OUT_OF_MEMORY; \ michael@0: } \ michael@0: \ michael@0: nsCOMPtr kungFuDeathGrip = it; \ michael@0: nsresult rv = it->Init(); \ michael@0: nsresult rv2 = const_cast<_elementName*>(this)->CopyInnerTo(it); \ michael@0: if (NS_FAILED(rv2)) { \ michael@0: rv = rv2; \ michael@0: } \ michael@0: if (NS_SUCCEEDED(rv)) { \ michael@0: kungFuDeathGrip.swap(*aResult); \ michael@0: } \ michael@0: \ michael@0: return rv; \ michael@0: } michael@0: michael@0: /** michael@0: * A macro to implement the getter and setter for a given string michael@0: * valued content property. The method uses the generic GetAttr and michael@0: * SetAttr methods. We use the 5-argument form of SetAttr, because michael@0: * some consumers only implement that one, hiding superclass michael@0: * 4-argument forms. michael@0: */ michael@0: #define NS_IMPL_STRING_ATTR(_class, _method, _atom) \ michael@0: NS_IMETHODIMP \ michael@0: _class::Get##_method(nsAString& aValue) \ michael@0: { \ michael@0: GetAttr(kNameSpaceID_None, nsGkAtoms::_atom, aValue); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHODIMP \ michael@0: _class::Set##_method(const nsAString& aValue) \ michael@0: { \ michael@0: return SetAttr(kNameSpaceID_None, nsGkAtoms::_atom, nullptr, aValue, true); \ michael@0: } michael@0: michael@0: /** michael@0: * A macro to implement the getter and setter for a given boolean michael@0: * valued content property. The method uses the GetBoolAttr and michael@0: * SetBoolAttr methods. michael@0: */ michael@0: #define NS_IMPL_BOOL_ATTR(_class, _method, _atom) \ michael@0: NS_IMETHODIMP \ michael@0: _class::Get##_method(bool* aValue) \ michael@0: { \ michael@0: *aValue = GetBoolAttr(nsGkAtoms::_atom); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHODIMP \ michael@0: _class::Set##_method(bool aValue) \ michael@0: { \ michael@0: return SetBoolAttr(nsGkAtoms::_atom, aValue); \ michael@0: } michael@0: michael@0: #define NS_FORWARD_NSIDOMELEMENT_TO_GENERIC \ michael@0: typedef mozilla::dom::Element Element; \ michael@0: NS_IMETHOD GetTagName(nsAString& aTagName) MOZ_FINAL \ michael@0: { \ michael@0: Element::GetTagName(aTagName); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetClassList(nsISupports** aClassList) MOZ_FINAL \ michael@0: { \ michael@0: Element::GetClassList(aClassList); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetAttributes(nsIDOMMozNamedAttrMap** aAttributes) MOZ_FINAL \ michael@0: { \ michael@0: NS_ADDREF(*aAttributes = Attributes()); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: using Element::GetAttribute; \ michael@0: NS_IMETHOD GetAttribute(const nsAString& name, nsAString& _retval) MOZ_FINAL \ michael@0: { \ michael@0: nsString attr; \ michael@0: GetAttribute(name, attr); \ michael@0: _retval = attr; \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetAttributeNS(const nsAString& namespaceURI, \ michael@0: const nsAString& localName, \ michael@0: nsAString& _retval) MOZ_FINAL \ michael@0: { \ michael@0: Element::GetAttributeNS(namespaceURI, localName, _retval); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD SetAttribute(const nsAString& name, \ michael@0: const nsAString& value) \ michael@0: { \ michael@0: mozilla::ErrorResult rv; \ michael@0: Element::SetAttribute(name, value, rv); \ michael@0: return rv.ErrorCode(); \ michael@0: } \ michael@0: NS_IMETHOD SetAttributeNS(const nsAString& namespaceURI, \ michael@0: const nsAString& qualifiedName, \ michael@0: const nsAString& value) MOZ_FINAL \ michael@0: { \ michael@0: mozilla::ErrorResult rv; \ michael@0: Element::SetAttributeNS(namespaceURI, qualifiedName, value, rv); \ michael@0: return rv.ErrorCode(); \ michael@0: } \ michael@0: using Element::RemoveAttribute; \ michael@0: NS_IMETHOD RemoveAttribute(const nsAString& name) MOZ_FINAL \ michael@0: { \ michael@0: mozilla::ErrorResult rv; \ michael@0: RemoveAttribute(name, rv); \ michael@0: return rv.ErrorCode(); \ michael@0: } \ michael@0: NS_IMETHOD RemoveAttributeNS(const nsAString& namespaceURI, \ michael@0: const nsAString& localName) MOZ_FINAL \ michael@0: { \ michael@0: mozilla::ErrorResult rv; \ michael@0: Element::RemoveAttributeNS(namespaceURI, localName, rv); \ michael@0: return rv.ErrorCode(); \ michael@0: } \ michael@0: using Element::HasAttribute; \ michael@0: NS_IMETHOD HasAttribute(const nsAString& name, \ michael@0: bool* _retval) MOZ_FINAL \ michael@0: { \ michael@0: *_retval = HasAttribute(name); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD HasAttributeNS(const nsAString& namespaceURI, \ michael@0: const nsAString& localName, \ michael@0: bool* _retval) MOZ_FINAL \ michael@0: { \ michael@0: *_retval = Element::HasAttributeNS(namespaceURI, localName); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetAttributeNode(const nsAString& name, \ michael@0: nsIDOMAttr** _retval) MOZ_FINAL \ michael@0: { \ michael@0: NS_IF_ADDREF(*_retval = Element::GetAttributeNode(name)); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD SetAttributeNode(nsIDOMAttr* newAttr, \ michael@0: nsIDOMAttr** _retval) MOZ_FINAL \ michael@0: { \ michael@0: if (!newAttr) { \ michael@0: return NS_ERROR_INVALID_POINTER; \ michael@0: } \ michael@0: mozilla::ErrorResult rv; \ michael@0: mozilla::dom::Attr* attr = static_cast(newAttr); \ michael@0: *_retval = Element::SetAttributeNode(*attr, rv).take(); \ michael@0: return rv.ErrorCode(); \ michael@0: } \ michael@0: NS_IMETHOD RemoveAttributeNode(nsIDOMAttr* oldAttr, \ michael@0: nsIDOMAttr** _retval) MOZ_FINAL \ michael@0: { \ michael@0: if (!oldAttr) { \ michael@0: return NS_ERROR_INVALID_POINTER; \ michael@0: } \ michael@0: mozilla::ErrorResult rv; \ michael@0: mozilla::dom::Attr* attr = static_cast(oldAttr); \ michael@0: *_retval = Element::RemoveAttributeNode(*attr, rv).take(); \ michael@0: return rv.ErrorCode(); \ michael@0: } \ michael@0: NS_IMETHOD GetAttributeNodeNS(const nsAString& namespaceURI, \ michael@0: const nsAString& localName, \ michael@0: nsIDOMAttr** _retval) MOZ_FINAL \ michael@0: { \ michael@0: NS_IF_ADDREF(*_retval = Element::GetAttributeNodeNS(namespaceURI, \ michael@0: localName)); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD SetAttributeNodeNS(nsIDOMAttr* newAttr, \ michael@0: nsIDOMAttr** _retval) MOZ_FINAL \ michael@0: { \ michael@0: mozilla::ErrorResult rv; \ michael@0: mozilla::dom::Attr* attr = static_cast(newAttr); \ michael@0: *_retval = Element::SetAttributeNodeNS(*attr, rv).take(); \ michael@0: return rv.ErrorCode(); \ michael@0: } \ michael@0: NS_IMETHOD GetElementsByTagName(const nsAString& name, \ michael@0: nsIDOMHTMLCollection** _retval) MOZ_FINAL \ michael@0: { \ michael@0: Element::GetElementsByTagName(name, _retval); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetElementsByTagNameNS(const nsAString& namespaceURI, \ michael@0: const nsAString& localName, \ michael@0: nsIDOMHTMLCollection** _retval) MOZ_FINAL \ michael@0: { \ michael@0: return Element::GetElementsByTagNameNS(namespaceURI, localName, \ michael@0: _retval); \ michael@0: } \ michael@0: NS_IMETHOD GetElementsByClassName(const nsAString& classes, \ michael@0: nsIDOMHTMLCollection** _retval) MOZ_FINAL \ michael@0: { \ michael@0: return Element::GetElementsByClassName(classes, _retval); \ michael@0: } \ michael@0: NS_IMETHOD GetChildElements(nsIDOMNodeList** aChildElements) MOZ_FINAL \ michael@0: { \ michael@0: nsIHTMLCollection* list = FragmentOrElement::Children(); \ michael@0: return CallQueryInterface(list, aChildElements); \ michael@0: } \ michael@0: NS_IMETHOD GetFirstElementChild(nsIDOMElement** aFirstElementChild) MOZ_FINAL \ michael@0: { \ michael@0: Element* element = Element::GetFirstElementChild(); \ michael@0: if (!element) { \ michael@0: *aFirstElementChild = nullptr; \ michael@0: return NS_OK; \ michael@0: } \ michael@0: return CallQueryInterface(element, aFirstElementChild); \ michael@0: } \ michael@0: NS_IMETHOD GetLastElementChild(nsIDOMElement** aLastElementChild) MOZ_FINAL \ michael@0: { \ michael@0: Element* element = Element::GetLastElementChild(); \ michael@0: if (!element) { \ michael@0: *aLastElementChild = nullptr; \ michael@0: return NS_OK; \ michael@0: } \ michael@0: return CallQueryInterface(element, aLastElementChild); \ michael@0: } \ michael@0: NS_IMETHOD GetPreviousElementSibling(nsIDOMElement** aPreviousElementSibling) \ michael@0: MOZ_FINAL \ michael@0: { \ michael@0: Element* element = Element::GetPreviousElementSibling(); \ michael@0: if (!element) { \ michael@0: *aPreviousElementSibling = nullptr; \ michael@0: return NS_OK; \ michael@0: } \ michael@0: return CallQueryInterface(element, aPreviousElementSibling); \ michael@0: } \ michael@0: NS_IMETHOD GetNextElementSibling(nsIDOMElement** aNextElementSibling) \ michael@0: MOZ_FINAL \ michael@0: { \ michael@0: Element* element = Element::GetNextElementSibling(); \ michael@0: if (!element) { \ michael@0: *aNextElementSibling = nullptr; \ michael@0: return NS_OK; \ michael@0: } \ michael@0: return CallQueryInterface(element, aNextElementSibling); \ michael@0: } \ michael@0: NS_IMETHOD GetChildElementCount(uint32_t* aChildElementCount) MOZ_FINAL \ michael@0: { \ michael@0: *aChildElementCount = Element::ChildElementCount(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD MozRemove() MOZ_FINAL \ michael@0: { \ michael@0: nsINode::Remove(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetClientRects(nsIDOMClientRectList** _retval) MOZ_FINAL \ michael@0: { \ michael@0: *_retval = Element::GetClientRects().take(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetBoundingClientRect(nsIDOMClientRect** _retval) MOZ_FINAL \ michael@0: { \ michael@0: *_retval = Element::GetBoundingClientRect().take(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetScrollTop(int32_t* aScrollTop) MOZ_FINAL \ michael@0: { \ michael@0: *aScrollTop = Element::ScrollTop(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD SetScrollTop(int32_t aScrollTop) MOZ_FINAL \ michael@0: { \ michael@0: Element::SetScrollTop(aScrollTop); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetScrollLeft(int32_t* aScrollLeft) MOZ_FINAL \ michael@0: { \ michael@0: *aScrollLeft = Element::ScrollLeft(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD SetScrollLeft(int32_t aScrollLeft) MOZ_FINAL \ michael@0: { \ michael@0: Element::SetScrollLeft(aScrollLeft); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetScrollWidth(int32_t* aScrollWidth) MOZ_FINAL \ michael@0: { \ michael@0: *aScrollWidth = Element::ScrollWidth(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetScrollHeight(int32_t* aScrollHeight) MOZ_FINAL \ michael@0: { \ michael@0: *aScrollHeight = Element::ScrollHeight(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetClientTop(int32_t* aClientTop) MOZ_FINAL \ michael@0: { \ michael@0: *aClientTop = Element::ClientTop(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetClientLeft(int32_t* aClientLeft) MOZ_FINAL \ michael@0: { \ michael@0: *aClientLeft = Element::ClientLeft(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetClientWidth(int32_t* aClientWidth) MOZ_FINAL \ michael@0: { \ michael@0: *aClientWidth = Element::ClientWidth(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetClientHeight(int32_t* aClientHeight) MOZ_FINAL \ michael@0: { \ michael@0: *aClientHeight = Element::ClientHeight(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetScrollLeftMax(int32_t* aScrollLeftMax) MOZ_FINAL \ michael@0: { \ michael@0: *aScrollLeftMax = Element::ScrollLeftMax(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD GetScrollTopMax(int32_t* aScrollTopMax) MOZ_FINAL \ michael@0: { \ michael@0: *aScrollTopMax = Element::ScrollTopMax(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD MozMatchesSelector(const nsAString& selector, \ michael@0: bool* _retval) MOZ_FINAL \ michael@0: { \ michael@0: mozilla::ErrorResult rv; \ michael@0: *_retval = Element::MozMatchesSelector(selector, rv); \ michael@0: return rv.ErrorCode(); \ michael@0: } \ michael@0: NS_IMETHOD SetCapture(bool retargetToElement) MOZ_FINAL \ michael@0: { \ michael@0: Element::SetCapture(retargetToElement); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD ReleaseCapture(void) MOZ_FINAL \ michael@0: { \ michael@0: Element::ReleaseCapture(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD MozRequestFullScreen(void) MOZ_FINAL \ michael@0: { \ michael@0: Element::MozRequestFullScreen(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: NS_IMETHOD MozRequestPointerLock(void) MOZ_FINAL \ michael@0: { \ michael@0: Element::MozRequestPointerLock(); \ michael@0: return NS_OK; \ michael@0: } \ michael@0: using nsINode::QuerySelector; \ michael@0: NS_IMETHOD QuerySelector(const nsAString& aSelector, \ michael@0: nsIDOMElement **aReturn) MOZ_FINAL \ michael@0: { \ michael@0: return nsINode::QuerySelector(aSelector, aReturn); \ michael@0: } \ michael@0: using nsINode::QuerySelectorAll; \ michael@0: NS_IMETHOD QuerySelectorAll(const nsAString& aSelector, \ michael@0: nsIDOMNodeList **aReturn) MOZ_FINAL \ michael@0: { \ michael@0: return nsINode::QuerySelectorAll(aSelector, aReturn); \ michael@0: } michael@0: michael@0: #endif // mozilla_dom_Element_h__