Backed out changeset 58dbd2146e24 (bug 944961) for bustage.
[gecko.git] / content / base / src / nsDOMAttributeMap.cpp
blobb7a0fe7911bf3da46a966e573f46c29d71ea65f1
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/. */
6 /*
7 * Implementation of the |attributes| property of DOM Core's Element object.
8 */
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"
18 #include "nsError.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)
33 : mContent(aContent)
35 // We don't add a reference to our content. If it goes away,
36 // we'll be told to drop our reference
37 SetIsDOMBinding();
40 /**
41 * Clear map pointer for attributes.
43 PLDHashOperator
44 RemoveMapRef(nsAttrHashKey::KeyType aKey, nsRefPtr<Attr>& aData,
45 void* aUserArg)
47 aData->SetMap(nullptr);
49 return PL_DHASH_REMOVE;
52 nsDOMAttributeMap::~nsDOMAttributeMap()
54 mAttributeCache.Enumerate(RemoveMapRef, nullptr);
57 void
58 nsDOMAttributeMap::DropReference()
60 mAttributeCache.Enumerate(RemoveMapRef, nullptr);
61 mContent = nullptr;
64 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMAttributeMap)
66 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttributeMap)
67 tmp->DropReference();
68 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
69 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent)
70 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
73 PLDHashOperator
74 TraverseMapEntry(nsAttrHashKey::KeyType aKey, nsRefPtr<Attr>& aData,
75 void* aUserArg)
77 nsCycleCollectionTraversalCallback *cb =
78 static_cast<nsCycleCollectionTraversalCallback*>(aUserArg);
80 cb->NoteXPCOMChild(static_cast<nsINode*>(aData.get()));
82 return PL_DHASH_NEXT;
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)
94 if (tmp->IsBlack()) {
95 if (tmp->mContent) {
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);
100 return true;
102 if (tmp->mContent &&
103 mozilla::dom::FragmentOrElement::CanSkip(tmp->mContent, true)) {
104 return 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)
122 NS_INTERFACE_MAP_END
124 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMAttributeMap)
125 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMAttributeMap)
127 PLDHashOperator
128 SetOwnerDocumentFunc(nsAttrHashKey::KeyType aKey,
129 nsRefPtr<Attr>& aData,
130 void* aUserArg)
132 nsresult rv = aData->SetOwnerDocument(static_cast<nsIDocument*>(aUserArg));
134 return NS_FAILED(rv) ? PL_DHASH_STOP : PL_DHASH_NEXT;
137 nsresult
138 nsDOMAttributeMap::SetOwnerDocument(nsIDocument* aDocument)
140 uint32_t n = mAttributeCache.Enumerate(SetOwnerDocumentFunc, aDocument);
141 NS_ENSURE_TRUE(n == mAttributeCache.Count(), NS_ERROR_FAILURE);
143 return NS_OK;
146 void
147 nsDOMAttributeMap::DropAttribute(int32_t aNamespaceID, nsIAtom* aLocalName)
149 nsAttrKey attr(aNamespaceID, aLocalName);
150 Attr *node = mAttributeCache.GetWeak(attr);
151 if (node) {
152 // Break link to map
153 node->SetMap(nullptr);
155 // Remove from cache
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());
167 nsRefPtr<Attr> node;
168 if (!mAttributeCache.Get(attr, getter_AddRefs(node))) {
169 nsAutoString value;
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);
176 else {
177 // Break link to map
178 node->SetMap(nullptr);
180 // Remove from cache
181 mAttributeCache.Remove(attr);
184 return node.forget();
187 Attr*
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);
195 if (!node) {
196 nsCOMPtr<nsINodeInfo> ni = aNodeInfo;
197 nsRefPtr<Attr> newAttr =
198 new Attr(this, ni.forget(), EmptyString(), aNsAware);
199 mAttributeCache.Put(attr, newAttr);
200 node = newAttr;
203 return node;
206 Attr*
207 nsDOMAttributeMap::NamedGetter(const nsAString& aAttrName, bool& aFound)
209 aFound = false;
210 NS_ENSURE_TRUE(mContent, nullptr);
212 nsCOMPtr<nsINodeInfo> ni = mContent->GetExistingAttrNameFromQName(aAttrName);
213 if (!ni) {
214 return nullptr;
217 aFound = true;
218 return GetAttribute(ni, false);
221 Attr*
222 nsDOMAttributeMap::GetNamedItem(const nsAString& aAttrName)
224 bool dummy;
225 return NamedGetter(aAttrName, dummy);
228 NS_IMETHODIMP
229 nsDOMAttributeMap::GetNamedItem(const nsAString& aAttrName,
230 nsIDOMAttr** aAttribute)
232 NS_ENSURE_ARG_POINTER(aAttribute);
234 NS_IF_ADDREF(*aAttribute = GetNamedItem(aAttrName));
236 return NS_OK;
239 NS_IMETHODIMP
240 nsDOMAttributeMap::SetNamedItem(nsIDOMAttr* aAttr, nsIDOMAttr** aReturn)
242 Attr* attribute = static_cast<Attr*>(aAttr);
243 NS_ENSURE_ARG(attribute);
245 ErrorResult rv;
246 *aReturn = SetNamedItem(*attribute, rv).get();
247 return rv.ErrorCode();
250 NS_IMETHODIMP
251 nsDOMAttributeMap::SetNamedItemNS(nsIDOMAttr* aAttr, nsIDOMAttr** aReturn)
253 Attr* attribute = static_cast<Attr*>(aAttr);
254 NS_ENSURE_ARG(attribute);
256 ErrorResult rv;
257 *aReturn = SetNamedItemNS(*attribute, rv).get();
258 return rv.ErrorCode();
261 already_AddRefed<Attr>
262 nsDOMAttributeMap::SetNamedItemInternal(Attr& aAttr,
263 bool aWithNS,
264 ErrorResult& aError)
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();
273 if (owner) {
274 if (owner != this) {
275 aError.Throw(NS_ERROR_DOM_INUSE_ATTRIBUTE_ERR);
276 return nullptr;
279 // setting a preexisting attribute is a no-op, just return the same
280 // node.
281 nsRefPtr<Attr> attribute = &aAttr;
282 return attribute.forget();
285 nsresult rv;
286 if (mContent->OwnerDoc() != aAttr.OwnerDoc()) {
287 nsCOMPtr<nsINode> adoptedNode =
288 mContent->OwnerDoc()->AdoptNode(aAttr, aError);
289 if (aError.Failed()) {
290 return nullptr;
293 NS_ASSERTION(adoptedNode == &aAttr, "Uh, adopt node changed nodes?");
296 // Get nodeinfo and preexisting attribute (if it exists)
297 nsAutoString name;
298 nsCOMPtr<nsINodeInfo> ni;
300 nsRefPtr<Attr> attr;
301 // SetNamedItemNS()
302 if (aWithNS) {
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()
310 aAttr.GetName(name);
312 // get node-info of old attribute
313 ni = mContent->GetExistingAttrNameFromQName(name);
314 if (ni) {
315 attr = RemoveAttribute(ni);
317 else {
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));
326 if (NS_FAILED(rv)) {
327 aError.Throw(rv);
328 return nullptr;
330 // value is already empty
334 nsAutoString value;
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);
341 aAttr.SetMap(this);
343 rv = mContent->SetAttr(ni->NamespaceID(), ni->NameAtom(),
344 ni->GetPrefixAtom(), value, true);
345 if (NS_FAILED(rv)) {
346 aError.Throw(rv);
347 DropAttribute(ni->NamespaceID(), ni->NameAtom());
350 return attr.forget();
353 NS_IMETHODIMP
354 nsDOMAttributeMap::RemoveNamedItem(const nsAString& aName,
355 nsIDOMAttr** aReturn)
357 NS_ENSURE_ARG_POINTER(aReturn);
359 ErrorResult rv;
360 *aReturn = RemoveNamedItem(aName, rv).get();
361 return rv.ErrorCode();
364 already_AddRefed<Attr>
365 nsDOMAttributeMap::RemoveNamedItem(const nsAString& aName, ErrorResult& aError)
367 if (!mContent) {
368 aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
369 return nullptr;
372 nsCOMPtr<nsINodeInfo> ni = mContent->GetExistingAttrNameFromQName(aName);
373 if (!ni) {
374 aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
375 return nullptr;
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();
386 Attr*
387 nsDOMAttributeMap::IndexedGetter(uint32_t aIndex, bool& aFound)
389 aFound = false;
390 NS_ENSURE_TRUE(mContent, nullptr);
392 const nsAttrName* name = mContent->GetAttrNameAt(aIndex);
393 NS_ENSURE_TRUE(name, nullptr);
395 aFound = true;
396 // Don't use the nodeinfo even if one exists since it can have the wrong
397 // owner document.
398 nsCOMPtr<nsINodeInfo> ni = mContent->NodeInfo()->NodeInfoManager()->
399 GetNodeInfo(name->LocalName(), name->GetPrefix(), name->NamespaceID(),
400 nsIDOMNode::ATTRIBUTE_NODE);
401 return GetAttribute(ni, true);
404 Attr*
405 nsDOMAttributeMap::Item(uint32_t aIndex)
407 bool dummy;
408 return IndexedGetter(aIndex, dummy);
411 NS_IMETHODIMP
412 nsDOMAttributeMap::Item(uint32_t aIndex, nsIDOMAttr** aReturn)
414 NS_IF_ADDREF(*aReturn = Item(aIndex));
415 return NS_OK;
418 uint32_t
419 nsDOMAttributeMap::Length() const
421 NS_ENSURE_TRUE(mContent, 0);
423 return mContent->GetAttrCount();
426 nsresult
427 nsDOMAttributeMap::GetLength(uint32_t *aLength)
429 NS_ENSURE_ARG_POINTER(aLength);
430 *aLength = Length();
431 return NS_OK;
434 NS_IMETHODIMP
435 nsDOMAttributeMap::GetNamedItemNS(const nsAString& aNamespaceURI,
436 const nsAString& aLocalName,
437 nsIDOMAttr** aReturn)
439 NS_IF_ADDREF(*aReturn = GetNamedItemNS(aNamespaceURI, aLocalName));
440 return NS_OK;
443 Attr*
444 nsDOMAttributeMap::GetNamedItemNS(const nsAString& aNamespaceURI,
445 const nsAString& aLocalName)
447 nsCOMPtr<nsINodeInfo> ni = GetAttrNodeInfo(aNamespaceURI, aLocalName);
448 if (!ni) {
449 return nullptr;
452 return GetAttribute(ni, true);
455 already_AddRefed<nsINodeInfo>
456 nsDOMAttributeMap::GetAttrNodeInfo(const nsAString& aNamespaceURI,
457 const nsAString& aLocalName)
459 if (!mContent) {
460 return nullptr;
463 int32_t nameSpaceID = kNameSpaceID_None;
465 if (!aNamespaceURI.IsEmpty()) {
466 nameSpaceID =
467 nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
469 if (nameSpaceID == kNameSpaceID_Unknown) {
470 return nullptr;
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);
487 return ni.forget();
491 return nullptr;
494 NS_IMETHODIMP
495 nsDOMAttributeMap::RemoveNamedItemNS(const nsAString& aNamespaceURI,
496 const nsAString& aLocalName,
497 nsIDOMAttr** aReturn)
499 NS_ENSURE_ARG_POINTER(aReturn);
500 ErrorResult rv;
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,
508 ErrorResult& aError)
510 nsCOMPtr<nsINodeInfo> ni = GetAttrNodeInfo(aNamespaceURI, aLocalName);
511 if (!ni) {
512 aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
513 return nullptr;
516 nsRefPtr<Attr> attr = RemoveAttribute(ni);
517 nsINodeInfo* attrNi = attr->NodeInfo();
518 mContent->UnsetAttr(attrNi->NamespaceID(), attrNi->NameAtom(), true);
520 return attr.forget();
523 uint32_t
524 nsDOMAttributeMap::Count() const
526 return mAttributeCache.Count();
529 uint32_t
530 nsDOMAttributeMap::Enumerate(AttrCache::EnumReadFunction aFunc,
531 void *aUserArg) const
533 return mAttributeCache.EnumerateRead(aFunc, aUserArg);
536 size_t
537 AttrCacheSizeEnumerator(const nsAttrKey& aKey,
538 const nsRefPtr<Attr>& aValue,
539 MallocSizeOf aMallocSizeOf,
540 void* aUserArg)
542 return aMallocSizeOf(aValue.get());
545 size_t
546 nsDOMAttributeMap::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
548 size_t n = aMallocSizeOf(this);
549 n += mAttributeCache.SizeOfExcludingThis(AttrCacheSizeEnumerator,
550 aMallocSizeOf);
552 // NB: mContent is non-owning and thus not counted.
553 return n;
556 /* virtual */ JSObject*
557 nsDOMAttributeMap::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
559 return MozNamedAttrMapBinding::Wrap(aCx, aScope, this);