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: /* michael@0: * Implementation of DOM Core's nsIDOMAttr node. michael@0: */ michael@0: michael@0: #include "mozilla/dom/Attr.h" michael@0: #include "mozilla/dom/AttrBinding.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "mozilla/InternalMutationEvent.h" michael@0: #include "nsContentCreatorFunctions.h" michael@0: #include "nsError.h" michael@0: #include "nsUnicharUtils.h" michael@0: #include "nsDOMString.h" michael@0: #include "nsIContentInlines.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIDOMUserDataHandler.h" michael@0: #include "nsGkAtoms.h" michael@0: #include "nsCOMArray.h" michael@0: #include "nsNameSpaceManager.h" michael@0: #include "nsNodeUtils.h" michael@0: #include "nsTextNode.h" michael@0: #include "mozAutoDocUpdate.h" michael@0: #include "nsWrapperCacheInlines.h" michael@0: michael@0: nsIAttribute::nsIAttribute(nsDOMAttributeMap* aAttrMap, michael@0: already_AddRefed& aNodeInfo, michael@0: bool aNsAware) michael@0: : nsINode(aNodeInfo), mAttrMap(aAttrMap), mNsAware(aNsAware) michael@0: { michael@0: } michael@0: michael@0: nsIAttribute::~nsIAttribute() michael@0: { michael@0: } michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: //---------------------------------------------------------------------- michael@0: bool Attr::sInitialized; michael@0: michael@0: Attr::Attr(nsDOMAttributeMap *aAttrMap, michael@0: already_AddRefed&& aNodeInfo, michael@0: const nsAString &aValue, bool aNsAware) michael@0: : nsIAttribute(aAttrMap, aNodeInfo, aNsAware), mValue(aValue) michael@0: { michael@0: NS_ABORT_IF_FALSE(mNodeInfo, "We must get a nodeinfo here!"); michael@0: NS_ABORT_IF_FALSE(mNodeInfo->NodeType() == nsIDOMNode::ATTRIBUTE_NODE, michael@0: "Wrong nodeType"); michael@0: michael@0: // We don't add a reference to our content. It will tell us michael@0: // to drop our reference when it goes away. michael@0: michael@0: SetIsDOMBinding(); michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(Attr) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Attr) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: michael@0: if (!nsINode::Traverse(tmp, cb)) { michael@0: return NS_SUCCESS_INTERRUPTED_TRAVERSE; michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAttrMap) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Attr) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Attr) michael@0: nsINode::Unlink(tmp); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mAttrMap) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Attr) michael@0: Element* ownerElement = tmp->GetElement(); michael@0: if (tmp->IsBlack()) { michael@0: if (ownerElement) { michael@0: // The attribute owns the element via attribute map so we can michael@0: // mark it when the attribute is certainly alive. michael@0: mozilla::dom::FragmentOrElement::MarkNodeChildren(ownerElement); michael@0: } michael@0: return true; michael@0: } michael@0: if (ownerElement && michael@0: mozilla::dom::FragmentOrElement::CanSkip(ownerElement, true)) { michael@0: return true; michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Attr) michael@0: return tmp->IsBlackAndDoesNotNeedTracing(static_cast(tmp)); michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Attr) michael@0: return tmp->IsBlack(); michael@0: NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END michael@0: michael@0: // QueryInterface implementation for Attr michael@0: NS_INTERFACE_TABLE_HEAD(Attr) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_TABLE(Attr, nsINode, nsIDOMAttr, nsIAttribute, nsIDOMNode, michael@0: nsIDOMEventTarget, EventTarget) michael@0: NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(Attr) michael@0: NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference, michael@0: new nsNodeSupportsWeakRefTearoff(this)) michael@0: NS_INTERFACE_MAP_ENTRY_TEAROFF(nsIDOMXPathNSResolver, michael@0: new nsNode3Tearoff(this)) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(Attr) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Attr, michael@0: nsNodeUtils::LastRelease(this)) michael@0: michael@0: void michael@0: Attr::SetMap(nsDOMAttributeMap *aMap) michael@0: { michael@0: if (mAttrMap && !aMap && sInitialized) { michael@0: // We're breaking a relationship with content and not getting a new one, michael@0: // need to locally cache value. GetValue() does that. michael@0: GetValue(mValue); michael@0: } michael@0: michael@0: mAttrMap = aMap; michael@0: } michael@0: michael@0: Element* michael@0: Attr::GetElement() const michael@0: { michael@0: if (!mAttrMap) { michael@0: return nullptr; michael@0: } michael@0: nsIContent* content = mAttrMap->GetContent(); michael@0: return content ? content->AsElement() : nullptr; michael@0: } michael@0: michael@0: nsresult michael@0: Attr::SetOwnerDocument(nsIDocument* aDocument) michael@0: { michael@0: NS_ASSERTION(aDocument, "Missing document"); michael@0: michael@0: nsIDocument *doc = OwnerDoc(); michael@0: NS_ASSERTION(doc != aDocument, "bad call to Attr::SetOwnerDocument"); michael@0: doc->DeleteAllPropertiesFor(this); michael@0: michael@0: nsCOMPtr newNodeInfo; michael@0: newNodeInfo = aDocument->NodeInfoManager()-> michael@0: GetNodeInfo(mNodeInfo->NameAtom(), mNodeInfo->GetPrefixAtom(), michael@0: mNodeInfo->NamespaceID(), michael@0: nsIDOMNode::ATTRIBUTE_NODE); michael@0: NS_ASSERTION(newNodeInfo, "GetNodeInfo lies"); michael@0: mNodeInfo.swap(newNodeInfo); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Attr::GetName(nsAString& aName) michael@0: { michael@0: aName = NodeName(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: Attr::GetNameAtom(nsIContent* aContent) michael@0: { michael@0: if (!mNsAware && michael@0: mNodeInfo->NamespaceID() == kNameSpaceID_None && michael@0: aContent->IsInHTMLDocument() && michael@0: aContent->IsHTML()) { michael@0: nsString name; michael@0: mNodeInfo->GetName(name); michael@0: nsAutoString lowercaseName; michael@0: nsContentUtils::ASCIIToLower(name, lowercaseName); michael@0: return do_GetAtom(lowercaseName); michael@0: } michael@0: nsCOMPtr nameAtom = mNodeInfo->NameAtom(); michael@0: return nameAtom.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Attr::GetValue(nsAString& aValue) michael@0: { michael@0: Element* element = GetElement(); michael@0: if (element) { michael@0: nsCOMPtr nameAtom = GetNameAtom(element); michael@0: element->GetAttr(mNodeInfo->NamespaceID(), nameAtom, aValue); michael@0: } michael@0: else { michael@0: aValue = mValue; michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: Attr::SetValue(const nsAString& aValue, ErrorResult& aRv) michael@0: { michael@0: Element* element = GetElement(); michael@0: if (!element) { michael@0: mValue = aValue; michael@0: return; michael@0: } michael@0: michael@0: nsCOMPtr nameAtom = GetNameAtom(element); michael@0: aRv = element->SetAttr(mNodeInfo->NamespaceID(), michael@0: nameAtom, michael@0: mNodeInfo->GetPrefixAtom(), michael@0: aValue, michael@0: true); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Attr::SetValue(const nsAString& aValue) michael@0: { michael@0: ErrorResult rv; michael@0: SetValue(aValue, rv); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: bool michael@0: Attr::Specified() const michael@0: { michael@0: return true; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Attr::GetSpecified(bool* aSpecified) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aSpecified); michael@0: *aSpecified = Specified(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: Element* michael@0: Attr::GetOwnerElement(ErrorResult& aRv) michael@0: { michael@0: OwnerDoc()->WarnOnceAbout(nsIDocument::eOwnerElement); michael@0: return GetElement(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Attr::GetOwnerElement(nsIDOMElement** aOwnerElement) michael@0: { michael@0: NS_ENSURE_ARG_POINTER(aOwnerElement); michael@0: OwnerDoc()->WarnOnceAbout(nsIDocument::eOwnerElement); michael@0: michael@0: Element* element = GetElement(); michael@0: if (element) { michael@0: return CallQueryInterface(element, aOwnerElement); michael@0: } michael@0: michael@0: *aOwnerElement = nullptr; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: Attr::GetNodeValueInternal(nsAString& aNodeValue) michael@0: { michael@0: OwnerDoc()->WarnOnceAbout(nsIDocument::eNodeValue); michael@0: michael@0: GetValue(aNodeValue); michael@0: } michael@0: michael@0: void michael@0: Attr::SetNodeValueInternal(const nsAString& aNodeValue, ErrorResult& aError) michael@0: { michael@0: OwnerDoc()->WarnOnceAbout(nsIDocument::eNodeValue); michael@0: michael@0: aError = SetValue(aNodeValue); michael@0: } michael@0: michael@0: nsresult michael@0: Attr::Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const michael@0: { michael@0: nsAutoString value; michael@0: const_cast(this)->GetValue(value); michael@0: michael@0: nsCOMPtr ni = aNodeInfo; michael@0: *aResult = new Attr(nullptr, ni.forget(), value, mNsAware); michael@0: if (!*aResult) { michael@0: return NS_ERROR_OUT_OF_MEMORY; michael@0: } michael@0: michael@0: NS_ADDREF(*aResult); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: Attr::GetBaseURI(bool aTryUseXHRDocBaseURI) const michael@0: { michael@0: Element* parent = GetElement(); michael@0: michael@0: return parent ? parent->GetBaseURI(aTryUseXHRDocBaseURI) : nullptr; michael@0: } michael@0: michael@0: void michael@0: Attr::GetTextContentInternal(nsAString& aTextContent) michael@0: { michael@0: OwnerDoc()->WarnOnceAbout(nsIDocument::eTextContent); michael@0: michael@0: GetValue(aTextContent); michael@0: } michael@0: michael@0: void michael@0: Attr::SetTextContentInternal(const nsAString& aTextContent, michael@0: ErrorResult& aError) michael@0: { michael@0: OwnerDoc()->WarnOnceAbout(nsIDocument::eTextContent); michael@0: michael@0: SetNodeValueInternal(aTextContent, aError); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: Attr::GetIsId(bool* aReturn) michael@0: { michael@0: Element* element = GetElement(); michael@0: if (!element) { michael@0: *aReturn = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIAtom* idAtom = element->GetIDAttributeName(); michael@0: if (!idAtom) { michael@0: *aReturn = false; michael@0: return NS_OK; michael@0: } michael@0: michael@0: *aReturn = mNodeInfo->Equals(idAtom, kNameSpaceID_None); michael@0: return NS_OK; michael@0: } michael@0: michael@0: bool michael@0: Attr::IsNodeOfType(uint32_t aFlags) const michael@0: { michael@0: return !(aFlags & ~eATTRIBUTE); michael@0: } michael@0: michael@0: uint32_t michael@0: Attr::GetChildCount() const michael@0: { michael@0: return 0; michael@0: } michael@0: michael@0: nsIContent * michael@0: Attr::GetChildAt(uint32_t aIndex) const michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsIContent * const * michael@0: Attr::GetChildArray(uint32_t* aChildCount) const michael@0: { michael@0: *aChildCount = 0; michael@0: return nullptr; michael@0: } michael@0: michael@0: int32_t michael@0: Attr::IndexOf(const nsINode* aPossibleChild) const michael@0: { michael@0: return -1; michael@0: } michael@0: michael@0: nsresult michael@0: Attr::InsertChildAt(nsIContent* aKid, uint32_t aIndex, michael@0: bool aNotify) michael@0: { michael@0: return NS_ERROR_NOT_IMPLEMENTED; michael@0: } michael@0: michael@0: void michael@0: Attr::RemoveChildAt(uint32_t aIndex, bool aNotify) michael@0: { michael@0: } michael@0: michael@0: nsresult michael@0: Attr::PreHandleEvent(EventChainPreVisitor& aVisitor) michael@0: { michael@0: aVisitor.mCanHandle = true; michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: Attr::Initialize() michael@0: { michael@0: sInitialized = true; michael@0: } michael@0: michael@0: void michael@0: Attr::Shutdown() michael@0: { michael@0: sInitialized = false; michael@0: } michael@0: michael@0: JSObject* michael@0: Attr::WrapObject(JSContext* aCx) michael@0: { michael@0: return AttrBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla