1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 * Implementation of the |attributes| property of DOM Core's Element object.
10 #include "nsDOMAttributeMap.h"
12 #include "mozilla/MemoryReporting.h"
13 #include "mozilla/dom/Attr.h"
14 #include "mozilla/dom/Element.h"
15 #include "mozilla/dom/MozNamedAttrMapBinding.h"
16 #include "nsAttrName.h"
17 #include "nsContentUtils.h"
19 #include "nsIContentInlines.h"
20 #include "nsIDocument.h"
21 #include "nsIDOMDocument.h"
22 #include "nsINameSpaceManager.h"
23 #include "nsNodeInfoManager.h"
24 #include "nsUnicharUtils.h"
25 #include "nsWrapperCacheInlines.h"
27 using namespace mozilla
;
28 using namespace mozilla::dom
;
30 //----------------------------------------------------------------------
32 nsDOMAttributeMap::nsDOMAttributeMap(Element
* aContent
)
35 // We don't add a reference to our content. If it goes away,
36 // we'll be told to drop our reference
41 * Clear map pointer for attributes.
44 RemoveMapRef(nsAttrHashKey::KeyType aKey
, nsRefPtr
<Attr
>& aData
,
47 aData
->SetMap(nullptr);
49 return PL_DHASH_REMOVE
;
52 nsDOMAttributeMap::~nsDOMAttributeMap()
54 mAttributeCache
.Enumerate(RemoveMapRef
, nullptr);
58 nsDOMAttributeMap::DropReference()
60 mAttributeCache
.Enumerate(RemoveMapRef
, nullptr);
64 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMAttributeMap
)
66 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttributeMap
)
68 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
69 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent
)
70 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
74 TraverseMapEntry(nsAttrHashKey::KeyType aKey
, nsRefPtr
<Attr
>& aData
,
77 nsCycleCollectionTraversalCallback
*cb
=
78 static_cast<nsCycleCollectionTraversalCallback
*>(aUserArg
);
80 cb
->NoteXPCOMChild(static_cast<nsINode
*>(aData
.get()));
85 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMAttributeMap
)
86 tmp
->mAttributeCache
.Enumerate(TraverseMapEntry
, &cb
);
87 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
88 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent
)
89 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
91 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDOMAttributeMap
)
93 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMAttributeMap
)
96 // The map owns the element so we can mark it when the
97 // map itself is certainly alive.
98 mozilla::dom::FragmentOrElement::MarkNodeChildren(tmp
->mContent
);
103 mozilla::dom::FragmentOrElement::CanSkip(tmp
->mContent
, true)) {
106 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
108 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDOMAttributeMap
)
109 return tmp
->IsBlackAndDoesNotNeedTracing(tmp
);
110 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
112 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDOMAttributeMap
)
113 return tmp
->IsBlack();
114 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
116 // QueryInterface implementation for nsDOMAttributeMap
117 NS_INTERFACE_TABLE_HEAD(nsDOMAttributeMap
)
118 NS_INTERFACE_TABLE1(nsDOMAttributeMap
, nsIDOMMozNamedAttrMap
)
119 NS_INTERFACE_TABLE_TO_MAP_SEGUE
120 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
121 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDOMAttributeMap
)
124 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMAttributeMap
)
125 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMAttributeMap
)
128 SetOwnerDocumentFunc(nsAttrHashKey::KeyType aKey
,
129 nsRefPtr
<Attr
>& aData
,
132 nsresult rv
= aData
->SetOwnerDocument(static_cast<nsIDocument
*>(aUserArg
));
134 return NS_FAILED(rv
) ? PL_DHASH_STOP
: PL_DHASH_NEXT
;
138 nsDOMAttributeMap::SetOwnerDocument(nsIDocument
* aDocument
)
140 uint32_t n
= mAttributeCache
.Enumerate(SetOwnerDocumentFunc
, aDocument
);
141 NS_ENSURE_TRUE(n
== mAttributeCache
.Count(), NS_ERROR_FAILURE
);
147 nsDOMAttributeMap::DropAttribute(int32_t aNamespaceID
, nsIAtom
* aLocalName
)
149 nsAttrKey
attr(aNamespaceID
, aLocalName
);
150 Attr
*node
= mAttributeCache
.GetWeak(attr
);
153 node
->SetMap(nullptr);
156 mAttributeCache
.Remove(attr
);
160 already_AddRefed
<Attr
>
161 nsDOMAttributeMap::RemoveAttribute(nsINodeInfo
* aNodeInfo
)
163 NS_ASSERTION(aNodeInfo
, "RemoveAttribute() called with aNodeInfo == nullptr!");
165 nsAttrKey
attr(aNodeInfo
->NamespaceID(), aNodeInfo
->NameAtom());
168 if (!mAttributeCache
.Get(attr
, getter_AddRefs(node
))) {
170 // As we are removing the attribute we need to set the current value in
171 // the attribute node.
172 mContent
->GetAttr(aNodeInfo
->NamespaceID(), aNodeInfo
->NameAtom(), value
);
173 nsCOMPtr
<nsINodeInfo
> ni
= aNodeInfo
;
174 node
= new Attr(nullptr, ni
.forget(), value
, true);
178 node
->SetMap(nullptr);
181 mAttributeCache
.Remove(attr
);
184 return node
.forget();
188 nsDOMAttributeMap::GetAttribute(nsINodeInfo
* aNodeInfo
, bool aNsAware
)
190 NS_ASSERTION(aNodeInfo
, "GetAttribute() called with aNodeInfo == nullptr!");
192 nsAttrKey
attr(aNodeInfo
->NamespaceID(), aNodeInfo
->NameAtom());
194 Attr
* node
= mAttributeCache
.GetWeak(attr
);
196 nsCOMPtr
<nsINodeInfo
> ni
= aNodeInfo
;
197 nsRefPtr
<Attr
> newAttr
=
198 new Attr(this, ni
.forget(), EmptyString(), aNsAware
);
199 mAttributeCache
.Put(attr
, newAttr
);
207 nsDOMAttributeMap::NamedGetter(const nsAString
& aAttrName
, bool& aFound
)
210 NS_ENSURE_TRUE(mContent
, nullptr);
212 nsCOMPtr
<nsINodeInfo
> ni
= mContent
->GetExistingAttrNameFromQName(aAttrName
);
218 return GetAttribute(ni
, false);
222 nsDOMAttributeMap::GetNamedItem(const nsAString
& aAttrName
)
225 return NamedGetter(aAttrName
, dummy
);
229 nsDOMAttributeMap::GetNamedItem(const nsAString
& aAttrName
,
230 nsIDOMAttr
** aAttribute
)
232 NS_ENSURE_ARG_POINTER(aAttribute
);
234 NS_IF_ADDREF(*aAttribute
= GetNamedItem(aAttrName
));
240 nsDOMAttributeMap::SetNamedItem(nsIDOMAttr
* aAttr
, nsIDOMAttr
** aReturn
)
242 Attr
* attribute
= static_cast<Attr
*>(aAttr
);
243 NS_ENSURE_ARG(attribute
);
246 *aReturn
= SetNamedItem(*attribute
, rv
).get();
247 return rv
.ErrorCode();
251 nsDOMAttributeMap::SetNamedItemNS(nsIDOMAttr
* aAttr
, nsIDOMAttr
** aReturn
)
253 Attr
* attribute
= static_cast<Attr
*>(aAttr
);
254 NS_ENSURE_ARG(attribute
);
257 *aReturn
= SetNamedItemNS(*attribute
, rv
).get();
258 return rv
.ErrorCode();
261 already_AddRefed
<Attr
>
262 nsDOMAttributeMap::SetNamedItemInternal(Attr
& aAttr
,
266 NS_ENSURE_TRUE(mContent
, nullptr);
268 // XXX should check same-origin between mContent and aAttr however
269 // nsContentUtils::CheckSameOrigin can't deal with attributenodes yet
271 // Check that attribute is not owned by somebody else
272 nsDOMAttributeMap
* owner
= aAttr
.GetMap();
275 aError
.Throw(NS_ERROR_DOM_INUSE_ATTRIBUTE_ERR
);
279 // setting a preexisting attribute is a no-op, just return the same
281 nsRefPtr
<Attr
> attribute
= &aAttr
;
282 return attribute
.forget();
286 if (mContent
->OwnerDoc() != aAttr
.OwnerDoc()) {
287 nsCOMPtr
<nsINode
> adoptedNode
=
288 mContent
->OwnerDoc()->AdoptNode(aAttr
, aError
);
289 if (aError
.Failed()) {
293 NS_ASSERTION(adoptedNode
== &aAttr
, "Uh, adopt node changed nodes?");
296 // Get nodeinfo and preexisting attribute (if it exists)
298 nsCOMPtr
<nsINodeInfo
> ni
;
303 // Return existing attribute, if present
304 ni
= aAttr
.NodeInfo();
306 if (mContent
->HasAttr(ni
->NamespaceID(), ni
->NameAtom())) {
307 attr
= RemoveAttribute(ni
);
309 } else { // SetNamedItem()
312 // get node-info of old attribute
313 ni
= mContent
->GetExistingAttrNameFromQName(name
);
315 attr
= RemoveAttribute(ni
);
318 if (mContent
->IsInHTMLDocument() &&
319 mContent
->IsHTML()) {
320 nsContentUtils::ASCIIToLower(name
);
323 rv
= mContent
->NodeInfo()->NodeInfoManager()->
324 GetNodeInfo(name
, nullptr, kNameSpaceID_None
,
325 nsIDOMNode::ATTRIBUTE_NODE
, getter_AddRefs(ni
));
330 // value is already empty
335 aAttr
.GetValue(value
);
337 // Add the new attribute to the attribute map before updating
338 // its value in the element. @see bug 364413.
339 nsAttrKey
attrkey(ni
->NamespaceID(), ni
->NameAtom());
340 mAttributeCache
.Put(attrkey
, &aAttr
);
343 rv
= mContent
->SetAttr(ni
->NamespaceID(), ni
->NameAtom(),
344 ni
->GetPrefixAtom(), value
, true);
347 DropAttribute(ni
->NamespaceID(), ni
->NameAtom());
350 return attr
.forget();
354 nsDOMAttributeMap::RemoveNamedItem(const nsAString
& aName
,
355 nsIDOMAttr
** aReturn
)
357 NS_ENSURE_ARG_POINTER(aReturn
);
360 *aReturn
= RemoveNamedItem(aName
, rv
).get();
361 return rv
.ErrorCode();
364 already_AddRefed
<Attr
>
365 nsDOMAttributeMap::RemoveNamedItem(const nsAString
& aName
, ErrorResult
& aError
)
368 aError
.Throw(NS_ERROR_DOM_NOT_FOUND_ERR
);
372 nsCOMPtr
<nsINodeInfo
> ni
= mContent
->GetExistingAttrNameFromQName(aName
);
374 aError
.Throw(NS_ERROR_DOM_NOT_FOUND_ERR
);
378 nsRefPtr
<Attr
> attribute
= GetAttribute(ni
, true);
380 // This removes the attribute node from the attribute map.
381 aError
= mContent
->UnsetAttr(ni
->NamespaceID(), ni
->NameAtom(), true);
382 return attribute
.forget();
387 nsDOMAttributeMap::IndexedGetter(uint32_t aIndex
, bool& aFound
)
390 NS_ENSURE_TRUE(mContent
, nullptr);
392 const nsAttrName
* name
= mContent
->GetAttrNameAt(aIndex
);
393 NS_ENSURE_TRUE(name
, nullptr);
396 // Don't use the nodeinfo even if one exists since it can have the wrong
398 nsCOMPtr
<nsINodeInfo
> ni
= mContent
->NodeInfo()->NodeInfoManager()->
399 GetNodeInfo(name
->LocalName(), name
->GetPrefix(), name
->NamespaceID(),
400 nsIDOMNode::ATTRIBUTE_NODE
);
401 return GetAttribute(ni
, true);
405 nsDOMAttributeMap::Item(uint32_t aIndex
)
408 return IndexedGetter(aIndex
, dummy
);
412 nsDOMAttributeMap::Item(uint32_t aIndex
, nsIDOMAttr
** aReturn
)
414 NS_IF_ADDREF(*aReturn
= Item(aIndex
));
419 nsDOMAttributeMap::Length() const
421 NS_ENSURE_TRUE(mContent
, 0);
423 return mContent
->GetAttrCount();
427 nsDOMAttributeMap::GetLength(uint32_t *aLength
)
429 NS_ENSURE_ARG_POINTER(aLength
);
435 nsDOMAttributeMap::GetNamedItemNS(const nsAString
& aNamespaceURI
,
436 const nsAString
& aLocalName
,
437 nsIDOMAttr
** aReturn
)
439 NS_IF_ADDREF(*aReturn
= GetNamedItemNS(aNamespaceURI
, aLocalName
));
444 nsDOMAttributeMap::GetNamedItemNS(const nsAString
& aNamespaceURI
,
445 const nsAString
& aLocalName
)
447 nsCOMPtr
<nsINodeInfo
> ni
= GetAttrNodeInfo(aNamespaceURI
, aLocalName
);
452 return GetAttribute(ni
, true);
455 already_AddRefed
<nsINodeInfo
>
456 nsDOMAttributeMap::GetAttrNodeInfo(const nsAString
& aNamespaceURI
,
457 const nsAString
& aLocalName
)
463 int32_t nameSpaceID
= kNameSpaceID_None
;
465 if (!aNamespaceURI
.IsEmpty()) {
467 nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI
);
469 if (nameSpaceID
== kNameSpaceID_Unknown
) {
474 uint32_t i
, count
= mContent
->GetAttrCount();
475 for (i
= 0; i
< count
; ++i
) {
476 const nsAttrName
* name
= mContent
->GetAttrNameAt(i
);
477 int32_t attrNS
= name
->NamespaceID();
478 nsIAtom
* nameAtom
= name
->LocalName();
480 if (nameSpaceID
== attrNS
&&
481 nameAtom
->Equals(aLocalName
)) {
482 nsCOMPtr
<nsINodeInfo
> ni
;
483 ni
= mContent
->NodeInfo()->NodeInfoManager()->
484 GetNodeInfo(nameAtom
, name
->GetPrefix(), nameSpaceID
,
485 nsIDOMNode::ATTRIBUTE_NODE
);
495 nsDOMAttributeMap::RemoveNamedItemNS(const nsAString
& aNamespaceURI
,
496 const nsAString
& aLocalName
,
497 nsIDOMAttr
** aReturn
)
499 NS_ENSURE_ARG_POINTER(aReturn
);
501 *aReturn
= RemoveNamedItemNS(aNamespaceURI
, aLocalName
, rv
).get();
502 return rv
.ErrorCode();
505 already_AddRefed
<Attr
>
506 nsDOMAttributeMap::RemoveNamedItemNS(const nsAString
& aNamespaceURI
,
507 const nsAString
& aLocalName
,
510 nsCOMPtr
<nsINodeInfo
> ni
= GetAttrNodeInfo(aNamespaceURI
, aLocalName
);
512 aError
.Throw(NS_ERROR_DOM_NOT_FOUND_ERR
);
516 nsRefPtr
<Attr
> attr
= RemoveAttribute(ni
);
517 nsINodeInfo
* attrNi
= attr
->NodeInfo();
518 mContent
->UnsetAttr(attrNi
->NamespaceID(), attrNi
->NameAtom(), true);
520 return attr
.forget();
524 nsDOMAttributeMap::Count() const
526 return mAttributeCache
.Count();
530 nsDOMAttributeMap::Enumerate(AttrCache::EnumReadFunction aFunc
,
531 void *aUserArg
) const
533 return mAttributeCache
.EnumerateRead(aFunc
, aUserArg
);
537 AttrCacheSizeEnumerator(const nsAttrKey
& aKey
,
538 const nsRefPtr
<Attr
>& aValue
,
539 MallocSizeOf aMallocSizeOf
,
542 return aMallocSizeOf(aValue
.get());
546 nsDOMAttributeMap::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const
548 size_t n
= aMallocSizeOf(this);
549 n
+= mAttributeCache
.SizeOfExcludingThis(AttrCacheSizeEnumerator
,
552 // NB: mContent is non-owning and thus not counted.
556 /* virtual */ JSObject
*
557 nsDOMAttributeMap::WrapObject(JSContext
* aCx
, JS::Handle
<JSObject
*> aScope
)
559 return MozNamedAttrMapBinding::Wrap(aCx
, aScope
, this);