summaryrefslogtreecommitdiffstats
path: root/dom/base/Attr.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/base/Attr.cpp')
-rw-r--r--dom/base/Attr.cpp374
1 files changed, 374 insertions, 0 deletions
diff --git a/dom/base/Attr.cpp b/dom/base/Attr.cpp
new file mode 100644
index 000000000..6eb3b49fd
--- /dev/null
+++ b/dom/base/Attr.cpp
@@ -0,0 +1,374 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * Implementation of DOM Core's nsIDOMAttr node.
+ */
+
+#include "mozilla/dom/Attr.h"
+#include "mozilla/dom/AttrBinding.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/InternalMutationEvent.h"
+#include "nsContentCreatorFunctions.h"
+#include "nsError.h"
+#include "nsUnicharUtils.h"
+#include "nsDOMString.h"
+#include "nsIContentInlines.h"
+#include "nsIDocument.h"
+#include "nsGkAtoms.h"
+#include "nsCOMArray.h"
+#include "nsNameSpaceManager.h"
+#include "nsNodeUtils.h"
+#include "nsTextNode.h"
+#include "mozAutoDocUpdate.h"
+#include "nsWrapperCacheInlines.h"
+
+nsIAttribute::nsIAttribute(nsDOMAttributeMap* aAttrMap,
+ already_AddRefed<mozilla::dom::NodeInfo>& aNodeInfo)
+: nsINode(aNodeInfo), mAttrMap(aAttrMap)
+{
+}
+
+nsIAttribute::~nsIAttribute()
+{
+}
+
+namespace mozilla {
+namespace dom {
+
+//----------------------------------------------------------------------
+bool Attr::sInitialized;
+
+Attr::Attr(nsDOMAttributeMap *aAttrMap,
+ already_AddRefed<mozilla::dom::NodeInfo>&& aNodeInfo,
+ const nsAString &aValue)
+ : nsIAttribute(aAttrMap, aNodeInfo), mValue(aValue)
+{
+ MOZ_ASSERT(mNodeInfo, "We must get a nodeinfo here!");
+ MOZ_ASSERT(mNodeInfo->NodeType() == nsIDOMNode::ATTRIBUTE_NODE,
+ "Wrong nodeType");
+
+ // We don't add a reference to our content. It will tell us
+ // to drop our reference when it goes away.
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(Attr)
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Attr)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+
+ if (!nsINode::Traverse(tmp, cb)) {
+ return NS_SUCCESS_INTERRUPTED_TRAVERSE;
+ }
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAttrMap)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Attr)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Attr)
+ nsINode::Unlink(tmp);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mAttrMap)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(Attr)
+ Element* ownerElement = tmp->GetElement();
+ if (tmp->IsBlack()) {
+ if (ownerElement) {
+ // The attribute owns the element via attribute map so we can
+ // mark it when the attribute is certainly alive.
+ mozilla::dom::FragmentOrElement::MarkNodeChildren(ownerElement);
+ }
+ return true;
+ }
+ if (ownerElement &&
+ mozilla::dom::FragmentOrElement::CanSkip(ownerElement, true)) {
+ return true;
+ }
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(Attr)
+ return tmp->IsBlackAndDoesNotNeedTracing(static_cast<nsIAttribute*>(tmp));
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(Attr)
+ return tmp->IsBlack();
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
+
+// QueryInterface implementation for Attr
+NS_INTERFACE_TABLE_HEAD(Attr)
+ NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
+ NS_INTERFACE_TABLE(Attr, nsINode, nsIDOMAttr, nsIAttribute, nsIDOMNode,
+ nsIDOMEventTarget, EventTarget)
+ NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(Attr)
+ NS_INTERFACE_MAP_ENTRY_TEAROFF(nsISupportsWeakReference,
+ new nsNodeSupportsWeakRefTearoff(this))
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(Attr)
+NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(Attr,
+ nsNodeUtils::LastRelease(this))
+
+void
+Attr::SetMap(nsDOMAttributeMap *aMap)
+{
+ if (mAttrMap && !aMap && sInitialized) {
+ // We're breaking a relationship with content and not getting a new one,
+ // need to locally cache value. GetValue() does that.
+ GetValue(mValue);
+ }
+
+ mAttrMap = aMap;
+}
+
+Element*
+Attr::GetElement() const
+{
+ if (!mAttrMap) {
+ return nullptr;
+ }
+ nsIContent* content = mAttrMap->GetContent();
+ return content ? content->AsElement() : nullptr;
+}
+
+nsresult
+Attr::SetOwnerDocument(nsIDocument* aDocument)
+{
+ NS_ASSERTION(aDocument, "Missing document");
+
+ nsIDocument *doc = OwnerDoc();
+ NS_ASSERTION(doc != aDocument, "bad call to Attr::SetOwnerDocument");
+ doc->DeleteAllPropertiesFor(this);
+
+ RefPtr<mozilla::dom::NodeInfo> newNodeInfo;
+ newNodeInfo = aDocument->NodeInfoManager()->
+ GetNodeInfo(mNodeInfo->NameAtom(), mNodeInfo->GetPrefixAtom(),
+ mNodeInfo->NamespaceID(),
+ nsIDOMNode::ATTRIBUTE_NODE);
+ NS_ASSERTION(newNodeInfo, "GetNodeInfo lies");
+ mNodeInfo.swap(newNodeInfo);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Attr::GetName(nsAString& aName)
+{
+ aName = NodeName();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+Attr::GetValue(nsAString& aValue)
+{
+ Element* element = GetElement();
+ if (element) {
+ nsCOMPtr<nsIAtom> nameAtom = mNodeInfo->NameAtom();
+ element->GetAttr(mNodeInfo->NamespaceID(), nameAtom, aValue);
+ }
+ else {
+ aValue = mValue;
+ }
+
+ return NS_OK;
+}
+
+void
+Attr::SetValue(const nsAString& aValue, ErrorResult& aRv)
+{
+ Element* element = GetElement();
+ if (!element) {
+ mValue = aValue;
+ return;
+ }
+
+ nsCOMPtr<nsIAtom> nameAtom = mNodeInfo->NameAtom();
+ aRv = element->SetAttr(mNodeInfo->NamespaceID(),
+ nameAtom,
+ mNodeInfo->GetPrefixAtom(),
+ aValue,
+ true);
+}
+
+NS_IMETHODIMP
+Attr::SetValue(const nsAString& aValue)
+{
+ ErrorResult rv;
+ SetValue(aValue, rv);
+ return rv.StealNSResult();
+}
+
+bool
+Attr::Specified() const
+{
+ return true;
+}
+
+NS_IMETHODIMP
+Attr::GetSpecified(bool* aSpecified)
+{
+ NS_ENSURE_ARG_POINTER(aSpecified);
+ *aSpecified = Specified();
+ return NS_OK;
+}
+
+Element*
+Attr::GetOwnerElement(ErrorResult& aRv)
+{
+ return GetElement();
+}
+
+NS_IMETHODIMP
+Attr::GetOwnerElement(nsIDOMElement** aOwnerElement)
+{
+ NS_ENSURE_ARG_POINTER(aOwnerElement);
+
+ Element* element = GetElement();
+ if (element) {
+ return CallQueryInterface(element, aOwnerElement);
+ }
+
+ *aOwnerElement = nullptr;
+
+ return NS_OK;
+}
+
+void
+Attr::GetNodeValueInternal(nsAString& aNodeValue)
+{
+ OwnerDoc()->WarnOnceAbout(nsIDocument::eNodeValue);
+
+ GetValue(aNodeValue);
+}
+
+void
+Attr::SetNodeValueInternal(const nsAString& aNodeValue, ErrorResult& aError)
+{
+ OwnerDoc()->WarnOnceAbout(nsIDocument::eNodeValue);
+
+ aError = SetValue(aNodeValue);
+}
+
+nsresult
+Attr::Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult) const
+{
+ nsAutoString value;
+ const_cast<Attr*>(this)->GetValue(value);
+
+ RefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
+ *aResult = new Attr(nullptr, ni.forget(), value);
+ if (!*aResult) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ NS_ADDREF(*aResult);
+
+ return NS_OK;
+}
+
+already_AddRefed<nsIURI>
+Attr::GetBaseURI(bool aTryUseXHRDocBaseURI) const
+{
+ Element* parent = GetElement();
+
+ return parent ? parent->GetBaseURI(aTryUseXHRDocBaseURI) : nullptr;
+}
+
+void
+Attr::GetTextContentInternal(nsAString& aTextContent,
+ ErrorResult& aError)
+{
+ OwnerDoc()->WarnOnceAbout(nsIDocument::eTextContent);
+
+ GetValue(aTextContent);
+}
+
+void
+Attr::SetTextContentInternal(const nsAString& aTextContent,
+ ErrorResult& aError)
+{
+ OwnerDoc()->WarnOnceAbout(nsIDocument::eTextContent);
+
+ SetNodeValueInternal(aTextContent, aError);
+}
+
+NS_IMETHODIMP
+Attr::GetIsId(bool* aReturn)
+{
+ *aReturn = mNodeInfo->Equals(nsGkAtoms::id, kNameSpaceID_None);
+ return NS_OK;
+}
+
+bool
+Attr::IsNodeOfType(uint32_t aFlags) const
+{
+ return !(aFlags & ~eATTRIBUTE);
+}
+
+uint32_t
+Attr::GetChildCount() const
+{
+ return 0;
+}
+
+nsIContent *
+Attr::GetChildAt(uint32_t aIndex) const
+{
+ return nullptr;
+}
+
+nsIContent * const *
+Attr::GetChildArray(uint32_t* aChildCount) const
+{
+ *aChildCount = 0;
+ return nullptr;
+}
+
+int32_t
+Attr::IndexOf(const nsINode* aPossibleChild) const
+{
+ return -1;
+}
+
+nsresult
+Attr::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
+ bool aNotify)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+void
+Attr::RemoveChildAt(uint32_t aIndex, bool aNotify)
+{
+}
+
+nsresult
+Attr::PreHandleEvent(EventChainPreVisitor& aVisitor)
+{
+ aVisitor.mCanHandle = true;
+ return NS_OK;
+}
+
+void
+Attr::Initialize()
+{
+ sInitialized = true;
+}
+
+void
+Attr::Shutdown()
+{
+ sInitialized = false;
+}
+
+JSObject*
+Attr::WrapNode(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
+{
+ return AttrBinding::Wrap(aCx, this, aGivenProto);
+}
+
+} // namespace dom
+} // namespace mozilla