Bumping gaia.json for 8 gaia revision(s) a=gaia-bump
[gecko.git] / dom / base / nsDOMAttributeMap.cpp
blob797365167deeb9a01772904e377c371fef95cb63
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * Implementation of the |attributes| property of DOM Core's Element object.
9 */
11 #include "nsDOMAttributeMap.h"
13 #include "mozilla/MemoryReporting.h"
14 #include "mozilla/dom/Attr.h"
15 #include "mozilla/dom/Element.h"
16 #include "mozilla/dom/NamedNodeMapBinding.h"
17 #include "mozilla/dom/NodeInfoInlines.h"
18 #include "nsAttrName.h"
19 #include "nsContentUtils.h"
20 #include "nsError.h"
21 #include "nsIContentInlines.h"
22 #include "nsIDocument.h"
23 #include "nsNameSpaceManager.h"
24 #include "nsNodeInfoManager.h"
25 #include "nsUnicharUtils.h"
26 #include "nsWrapperCacheInlines.h"
28 using namespace mozilla;
29 using namespace mozilla::dom;
31 //----------------------------------------------------------------------
33 nsDOMAttributeMap::nsDOMAttributeMap(Element* aContent)
34 : mContent(aContent)
36 // We don't add a reference to our content. If it goes away,
37 // we'll be told to drop our reference
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 if (mAttributeCache) {
55 mAttributeCache->Enumerate(RemoveMapRef, nullptr);
59 void
60 nsDOMAttributeMap::DropReference()
62 if (mAttributeCache) {
63 mAttributeCache->Enumerate(RemoveMapRef, nullptr);
65 mContent = nullptr;
68 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMAttributeMap)
70 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttributeMap)
71 tmp->DropReference();
72 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
73 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent)
74 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
77 PLDHashOperator
78 TraverseMapEntry(nsAttrHashKey::KeyType aKey, nsRefPtr<Attr>& aData,
79 void* aUserArg)
81 nsCycleCollectionTraversalCallback *cb =
82 static_cast<nsCycleCollectionTraversalCallback*>(aUserArg);
84 cb->NoteXPCOMChild(static_cast<nsINode*>(aData.get()));
86 return PL_DHASH_NEXT;
89 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMAttributeMap)
90 if (tmp->mAttributeCache) {
91 tmp->mAttributeCache->Enumerate(TraverseMapEntry, &cb);
93 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
94 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
95 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
97 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDOMAttributeMap)
99 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMAttributeMap)
100 if (tmp->IsBlack()) {
101 if (tmp->mContent) {
102 // The map owns the element so we can mark it when the
103 // map itself is certainly alive.
104 mozilla::dom::FragmentOrElement::MarkNodeChildren(tmp->mContent);
106 return true;
108 if (tmp->mContent &&
109 mozilla::dom::FragmentOrElement::CanSkip(tmp->mContent, true)) {
110 return true;
112 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
114 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDOMAttributeMap)
115 return tmp->IsBlackAndDoesNotNeedTracing(tmp);
116 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
118 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDOMAttributeMap)
119 return tmp->IsBlack();
120 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
122 // QueryInterface implementation for nsDOMAttributeMap
123 NS_INTERFACE_TABLE_HEAD(nsDOMAttributeMap)
124 NS_INTERFACE_TABLE(nsDOMAttributeMap, nsIDOMMozNamedAttrMap)
125 NS_INTERFACE_TABLE_TO_MAP_SEGUE
126 NS_WRAPPERCACHE_INTERFACE_TABLE_ENTRY
127 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDOMAttributeMap)
128 NS_INTERFACE_MAP_END
130 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMAttributeMap)
131 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMAttributeMap)
133 PLDHashOperator
134 SetOwnerDocumentFunc(nsAttrHashKey::KeyType aKey,
135 nsRefPtr<Attr>& aData,
136 void* aUserArg)
138 nsresult rv = aData->SetOwnerDocument(static_cast<nsIDocument*>(aUserArg));
140 return NS_FAILED(rv) ? PL_DHASH_STOP : PL_DHASH_NEXT;
143 nsresult
144 nsDOMAttributeMap::SetOwnerDocument(nsIDocument* aDocument)
146 if (mAttributeCache) {
147 uint32_t n = mAttributeCache->Enumerate(SetOwnerDocumentFunc, aDocument);
148 NS_ENSURE_TRUE(n == mAttributeCache->Count(), NS_ERROR_FAILURE);
150 return NS_OK;
153 void
154 nsDOMAttributeMap::DropAttribute(int32_t aNamespaceID, nsIAtom* aLocalName)
156 nsAttrKey attr(aNamespaceID, aLocalName);
157 if (mAttributeCache) {
158 Attr *node = mAttributeCache->GetWeak(attr);
159 if (node) {
160 // Break link to map
161 node->SetMap(nullptr);
163 // Remove from cache
164 mAttributeCache->Remove(attr);
169 already_AddRefed<Attr>
170 nsDOMAttributeMap::RemoveAttribute(mozilla::dom::NodeInfo* aNodeInfo)
172 NS_ASSERTION(aNodeInfo, "RemoveAttribute() called with aNodeInfo == nullptr!");
174 nsAttrKey attr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom());
176 nsRefPtr<Attr> node;
177 if (mAttributeCache && mAttributeCache->Get(attr, getter_AddRefs(node))) {
178 // Break link to map
179 node->SetMap(nullptr);
181 // Remove from cache
182 mAttributeCache->Remove(attr);
183 } else {
184 nsAutoString value;
185 // As we are removing the attribute we need to set the current value in
186 // the attribute node.
187 mContent->GetAttr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom(), value);
188 nsRefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
189 node = new Attr(nullptr, ni.forget(), value, true);
192 return node.forget();
195 Attr*
196 nsDOMAttributeMap::GetAttribute(mozilla::dom::NodeInfo* aNodeInfo, bool aNsAware)
198 NS_ASSERTION(aNodeInfo, "GetAttribute() called with aNodeInfo == nullptr!");
200 nsAttrKey attr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom());
202 EnsureAttributeCache();
203 Attr* node = mAttributeCache->GetWeak(attr);
204 if (!node) {
205 nsRefPtr<mozilla::dom::NodeInfo> ni = aNodeInfo;
206 nsRefPtr<Attr> newAttr =
207 new Attr(this, ni.forget(), EmptyString(), aNsAware);
208 mAttributeCache->Put(attr, newAttr);
209 node = newAttr;
212 return node;
215 Attr*
216 nsDOMAttributeMap::NamedGetter(const nsAString& aAttrName, bool& aFound)
218 aFound = false;
219 NS_ENSURE_TRUE(mContent, nullptr);
221 nsRefPtr<mozilla::dom::NodeInfo> ni = mContent->GetExistingAttrNameFromQName(aAttrName);
222 if (!ni) {
223 return nullptr;
226 aFound = true;
227 return GetAttribute(ni, false);
230 bool
231 nsDOMAttributeMap::NameIsEnumerable(const nsAString& aName)
233 return true;
236 Attr*
237 nsDOMAttributeMap::GetNamedItem(const nsAString& aAttrName)
239 bool dummy;
240 return NamedGetter(aAttrName, dummy);
243 NS_IMETHODIMP
244 nsDOMAttributeMap::GetNamedItem(const nsAString& aAttrName,
245 nsIDOMAttr** aAttribute)
247 NS_ENSURE_ARG_POINTER(aAttribute);
249 NS_IF_ADDREF(*aAttribute = GetNamedItem(aAttrName));
251 return NS_OK;
254 void
255 nsDOMAttributeMap::EnsureAttributeCache()
257 if (!mAttributeCache) {
258 mAttributeCache = MakeUnique<AttrCache>();
262 NS_IMETHODIMP
263 nsDOMAttributeMap::SetNamedItem(nsIDOMAttr* aAttr, nsIDOMAttr** aReturn)
265 Attr* attribute = static_cast<Attr*>(aAttr);
266 NS_ENSURE_ARG(attribute);
268 ErrorResult rv;
269 *aReturn = SetNamedItem(*attribute, rv).take();
270 return rv.ErrorCode();
273 NS_IMETHODIMP
274 nsDOMAttributeMap::SetNamedItemNS(nsIDOMAttr* aAttr, nsIDOMAttr** aReturn)
276 Attr* attribute = static_cast<Attr*>(aAttr);
277 NS_ENSURE_ARG(attribute);
279 ErrorResult rv;
280 *aReturn = SetNamedItemNS(*attribute, rv).take();
281 return rv.ErrorCode();
284 already_AddRefed<Attr>
285 nsDOMAttributeMap::SetNamedItemInternal(Attr& aAttr,
286 bool aWithNS,
287 ErrorResult& aError)
289 NS_ENSURE_TRUE(mContent, nullptr);
291 // XXX should check same-origin between mContent and aAttr however
292 // nsContentUtils::CheckSameOrigin can't deal with attributenodes yet
294 // Check that attribute is not owned by somebody else
295 nsDOMAttributeMap* owner = aAttr.GetMap();
296 if (owner) {
297 if (owner != this) {
298 aError.Throw(NS_ERROR_DOM_INUSE_ATTRIBUTE_ERR);
299 return nullptr;
302 // setting a preexisting attribute is a no-op, just return the same
303 // node.
304 nsRefPtr<Attr> attribute = &aAttr;
305 return attribute.forget();
308 nsresult rv;
309 if (mContent->OwnerDoc() != aAttr.OwnerDoc()) {
310 nsCOMPtr<nsINode> adoptedNode =
311 mContent->OwnerDoc()->AdoptNode(aAttr, aError);
312 if (aError.Failed()) {
313 return nullptr;
316 NS_ASSERTION(adoptedNode == &aAttr, "Uh, adopt node changed nodes?");
319 // Get nodeinfo and preexisting attribute (if it exists)
320 nsAutoString name;
321 nsRefPtr<mozilla::dom::NodeInfo> ni;
323 nsRefPtr<Attr> attr;
324 // SetNamedItemNS()
325 if (aWithNS) {
326 // Return existing attribute, if present
327 ni = aAttr.NodeInfo();
329 if (mContent->HasAttr(ni->NamespaceID(), ni->NameAtom())) {
330 attr = RemoveAttribute(ni);
332 } else { // SetNamedItem()
333 aAttr.GetName(name);
335 // get node-info of old attribute
336 ni = mContent->GetExistingAttrNameFromQName(name);
337 if (ni) {
338 attr = RemoveAttribute(ni);
340 else {
341 if (mContent->IsInHTMLDocument() &&
342 mContent->IsHTML()) {
343 nsContentUtils::ASCIIToLower(name);
346 rv = mContent->NodeInfo()->NodeInfoManager()->
347 GetNodeInfo(name, nullptr, kNameSpaceID_None,
348 nsIDOMNode::ATTRIBUTE_NODE, getter_AddRefs(ni));
349 if (NS_FAILED(rv)) {
350 aError.Throw(rv);
351 return nullptr;
353 // value is already empty
357 nsAutoString value;
358 aAttr.GetValue(value);
360 // Add the new attribute to the attribute map before updating
361 // its value in the element. @see bug 364413.
362 nsAttrKey attrkey(ni->NamespaceID(), ni->NameAtom());
363 EnsureAttributeCache();
364 mAttributeCache->Put(attrkey, &aAttr);
365 aAttr.SetMap(this);
367 rv = mContent->SetAttr(ni->NamespaceID(), ni->NameAtom(),
368 ni->GetPrefixAtom(), value, true);
369 if (NS_FAILED(rv)) {
370 aError.Throw(rv);
371 DropAttribute(ni->NamespaceID(), ni->NameAtom());
374 return attr.forget();
377 NS_IMETHODIMP
378 nsDOMAttributeMap::RemoveNamedItem(const nsAString& aName,
379 nsIDOMAttr** aReturn)
381 NS_ENSURE_ARG_POINTER(aReturn);
383 ErrorResult rv;
384 *aReturn = RemoveNamedItem(aName, rv).take();
385 return rv.ErrorCode();
388 already_AddRefed<Attr>
389 nsDOMAttributeMap::RemoveNamedItem(const nsAString& aName, ErrorResult& aError)
391 if (!mContent) {
392 aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
393 return nullptr;
396 nsRefPtr<mozilla::dom::NodeInfo> ni = mContent->GetExistingAttrNameFromQName(aName);
397 if (!ni) {
398 aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
399 return nullptr;
402 nsRefPtr<Attr> attribute = GetAttribute(ni, true);
404 // This removes the attribute node from the attribute map.
405 aError = mContent->UnsetAttr(ni->NamespaceID(), ni->NameAtom(), true);
406 return attribute.forget();
410 Attr*
411 nsDOMAttributeMap::IndexedGetter(uint32_t aIndex, bool& aFound)
413 aFound = false;
414 NS_ENSURE_TRUE(mContent, nullptr);
416 const nsAttrName* name = mContent->GetAttrNameAt(aIndex);
417 NS_ENSURE_TRUE(name, nullptr);
419 aFound = true;
420 // Don't use the nodeinfo even if one exists since it can have the wrong
421 // owner document.
422 nsRefPtr<mozilla::dom::NodeInfo> ni = mContent->NodeInfo()->NodeInfoManager()->
423 GetNodeInfo(name->LocalName(), name->GetPrefix(), name->NamespaceID(),
424 nsIDOMNode::ATTRIBUTE_NODE);
425 return GetAttribute(ni, true);
428 Attr*
429 nsDOMAttributeMap::Item(uint32_t aIndex)
431 bool dummy;
432 return IndexedGetter(aIndex, dummy);
435 NS_IMETHODIMP
436 nsDOMAttributeMap::Item(uint32_t aIndex, nsIDOMAttr** aReturn)
438 NS_IF_ADDREF(*aReturn = Item(aIndex));
439 return NS_OK;
442 uint32_t
443 nsDOMAttributeMap::Length() const
445 NS_ENSURE_TRUE(mContent, 0);
447 return mContent->GetAttrCount();
450 nsresult
451 nsDOMAttributeMap::GetLength(uint32_t *aLength)
453 NS_ENSURE_ARG_POINTER(aLength);
454 *aLength = Length();
455 return NS_OK;
458 NS_IMETHODIMP
459 nsDOMAttributeMap::GetNamedItemNS(const nsAString& aNamespaceURI,
460 const nsAString& aLocalName,
461 nsIDOMAttr** aReturn)
463 NS_IF_ADDREF(*aReturn = GetNamedItemNS(aNamespaceURI, aLocalName));
464 return NS_OK;
467 Attr*
468 nsDOMAttributeMap::GetNamedItemNS(const nsAString& aNamespaceURI,
469 const nsAString& aLocalName)
471 nsRefPtr<mozilla::dom::NodeInfo> ni = GetAttrNodeInfo(aNamespaceURI, aLocalName);
472 if (!ni) {
473 return nullptr;
476 return GetAttribute(ni, true);
479 already_AddRefed<mozilla::dom::NodeInfo>
480 nsDOMAttributeMap::GetAttrNodeInfo(const nsAString& aNamespaceURI,
481 const nsAString& aLocalName)
483 if (!mContent) {
484 return nullptr;
487 int32_t nameSpaceID = kNameSpaceID_None;
489 if (!aNamespaceURI.IsEmpty()) {
490 nameSpaceID =
491 nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
493 if (nameSpaceID == kNameSpaceID_Unknown) {
494 return nullptr;
498 uint32_t i, count = mContent->GetAttrCount();
499 for (i = 0; i < count; ++i) {
500 const nsAttrName* name = mContent->GetAttrNameAt(i);
501 int32_t attrNS = name->NamespaceID();
502 nsIAtom* nameAtom = name->LocalName();
504 if (nameSpaceID == attrNS &&
505 nameAtom->Equals(aLocalName)) {
506 nsRefPtr<mozilla::dom::NodeInfo> ni;
507 ni = mContent->NodeInfo()->NodeInfoManager()->
508 GetNodeInfo(nameAtom, name->GetPrefix(), nameSpaceID,
509 nsIDOMNode::ATTRIBUTE_NODE);
511 return ni.forget();
515 return nullptr;
518 NS_IMETHODIMP
519 nsDOMAttributeMap::RemoveNamedItemNS(const nsAString& aNamespaceURI,
520 const nsAString& aLocalName,
521 nsIDOMAttr** aReturn)
523 NS_ENSURE_ARG_POINTER(aReturn);
524 ErrorResult rv;
525 *aReturn = RemoveNamedItemNS(aNamespaceURI, aLocalName, rv).take();
526 return rv.ErrorCode();
529 already_AddRefed<Attr>
530 nsDOMAttributeMap::RemoveNamedItemNS(const nsAString& aNamespaceURI,
531 const nsAString& aLocalName,
532 ErrorResult& aError)
534 nsRefPtr<mozilla::dom::NodeInfo> ni = GetAttrNodeInfo(aNamespaceURI, aLocalName);
535 if (!ni) {
536 aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
537 return nullptr;
540 nsRefPtr<Attr> attr = RemoveAttribute(ni);
541 mozilla::dom::NodeInfo* attrNi = attr->NodeInfo();
542 mContent->UnsetAttr(attrNi->NamespaceID(), attrNi->NameAtom(), true);
544 return attr.forget();
547 uint32_t
548 nsDOMAttributeMap::Count() const
550 return mAttributeCache ? mAttributeCache->Count() : 0;
553 uint32_t
554 nsDOMAttributeMap::Enumerate(AttrCache::EnumReadFunction aFunc,
555 void *aUserArg) const
557 return mAttributeCache ? mAttributeCache->EnumerateRead(aFunc, aUserArg) : 0;
560 size_t
561 AttrCacheSizeEnumerator(const nsAttrKey& aKey,
562 const nsRefPtr<Attr>& aValue,
563 MallocSizeOf aMallocSizeOf,
564 void* aUserArg)
566 return aMallocSizeOf(aValue.get());
569 size_t
570 nsDOMAttributeMap::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
572 size_t n = aMallocSizeOf(this);
573 n += mAttributeCache
574 ? mAttributeCache->SizeOfExcludingThis(AttrCacheSizeEnumerator,
575 aMallocSizeOf)
576 : 0;
578 // NB: mContent is non-owning and thus not counted.
579 return n;
582 /* virtual */ JSObject*
583 nsDOMAttributeMap::WrapObject(JSContext* aCx)
585 return NamedNodeMapBinding::Wrap(aCx, this);