Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
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 "nsNameSpaceManager.h"
22 #include "nsNodeInfoManager.h"
23 #include "nsUnicharUtils.h"
24 #include "nsWrapperCacheInlines.h"
26 using namespace mozilla;
27 using namespace mozilla::dom;
29 //----------------------------------------------------------------------
31 nsDOMAttributeMap::nsDOMAttributeMap(Element* aContent)
32 : mContent(aContent)
33 {
34 // We don't add a reference to our content. If it goes away,
35 // we'll be told to drop our reference
36 SetIsDOMBinding();
37 }
39 /**
40 * Clear map pointer for attributes.
41 */
42 PLDHashOperator
43 RemoveMapRef(nsAttrHashKey::KeyType aKey, nsRefPtr<Attr>& aData,
44 void* aUserArg)
45 {
46 aData->SetMap(nullptr);
48 return PL_DHASH_REMOVE;
49 }
51 nsDOMAttributeMap::~nsDOMAttributeMap()
52 {
53 mAttributeCache.Enumerate(RemoveMapRef, nullptr);
54 }
56 void
57 nsDOMAttributeMap::DropReference()
58 {
59 mAttributeCache.Enumerate(RemoveMapRef, nullptr);
60 mContent = nullptr;
61 }
63 NS_IMPL_CYCLE_COLLECTION_CLASS(nsDOMAttributeMap)
65 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDOMAttributeMap)
66 tmp->DropReference();
67 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
68 NS_IMPL_CYCLE_COLLECTION_UNLINK(mContent)
69 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
72 PLDHashOperator
73 TraverseMapEntry(nsAttrHashKey::KeyType aKey, nsRefPtr<Attr>& aData,
74 void* aUserArg)
75 {
76 nsCycleCollectionTraversalCallback *cb =
77 static_cast<nsCycleCollectionTraversalCallback*>(aUserArg);
79 cb->NoteXPCOMChild(static_cast<nsINode*>(aData.get()));
81 return PL_DHASH_NEXT;
82 }
84 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsDOMAttributeMap)
85 tmp->mAttributeCache.Enumerate(TraverseMapEntry, &cb);
86 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
87 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContent)
88 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
90 NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsDOMAttributeMap)
92 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsDOMAttributeMap)
93 if (tmp->IsBlack()) {
94 if (tmp->mContent) {
95 // The map owns the element so we can mark it when the
96 // map itself is certainly alive.
97 mozilla::dom::FragmentOrElement::MarkNodeChildren(tmp->mContent);
98 }
99 return true;
100 }
101 if (tmp->mContent &&
102 mozilla::dom::FragmentOrElement::CanSkip(tmp->mContent, true)) {
103 return true;
104 }
105 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
107 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsDOMAttributeMap)
108 return tmp->IsBlackAndDoesNotNeedTracing(tmp);
109 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
111 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDOMAttributeMap)
112 return tmp->IsBlack();
113 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
115 // QueryInterface implementation for nsDOMAttributeMap
116 NS_INTERFACE_TABLE_HEAD(nsDOMAttributeMap)
117 NS_INTERFACE_TABLE(nsDOMAttributeMap, nsIDOMMozNamedAttrMap)
118 NS_INTERFACE_TABLE_TO_MAP_SEGUE
119 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
120 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDOMAttributeMap)
121 NS_INTERFACE_MAP_END
123 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMAttributeMap)
124 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMAttributeMap)
126 PLDHashOperator
127 SetOwnerDocumentFunc(nsAttrHashKey::KeyType aKey,
128 nsRefPtr<Attr>& aData,
129 void* aUserArg)
130 {
131 nsresult rv = aData->SetOwnerDocument(static_cast<nsIDocument*>(aUserArg));
133 return NS_FAILED(rv) ? PL_DHASH_STOP : PL_DHASH_NEXT;
134 }
136 nsresult
137 nsDOMAttributeMap::SetOwnerDocument(nsIDocument* aDocument)
138 {
139 uint32_t n = mAttributeCache.Enumerate(SetOwnerDocumentFunc, aDocument);
140 NS_ENSURE_TRUE(n == mAttributeCache.Count(), NS_ERROR_FAILURE);
142 return NS_OK;
143 }
145 void
146 nsDOMAttributeMap::DropAttribute(int32_t aNamespaceID, nsIAtom* aLocalName)
147 {
148 nsAttrKey attr(aNamespaceID, aLocalName);
149 Attr *node = mAttributeCache.GetWeak(attr);
150 if (node) {
151 // Break link to map
152 node->SetMap(nullptr);
154 // Remove from cache
155 mAttributeCache.Remove(attr);
156 }
157 }
159 already_AddRefed<Attr>
160 nsDOMAttributeMap::RemoveAttribute(nsINodeInfo* aNodeInfo)
161 {
162 NS_ASSERTION(aNodeInfo, "RemoveAttribute() called with aNodeInfo == nullptr!");
164 nsAttrKey attr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom());
166 nsRefPtr<Attr> node;
167 if (!mAttributeCache.Get(attr, getter_AddRefs(node))) {
168 nsAutoString value;
169 // As we are removing the attribute we need to set the current value in
170 // the attribute node.
171 mContent->GetAttr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom(), value);
172 nsCOMPtr<nsINodeInfo> ni = aNodeInfo;
173 node = new Attr(nullptr, ni.forget(), value, true);
174 }
175 else {
176 // Break link to map
177 node->SetMap(nullptr);
179 // Remove from cache
180 mAttributeCache.Remove(attr);
181 }
183 return node.forget();
184 }
186 Attr*
187 nsDOMAttributeMap::GetAttribute(nsINodeInfo* aNodeInfo, bool aNsAware)
188 {
189 NS_ASSERTION(aNodeInfo, "GetAttribute() called with aNodeInfo == nullptr!");
191 nsAttrKey attr(aNodeInfo->NamespaceID(), aNodeInfo->NameAtom());
193 Attr* node = mAttributeCache.GetWeak(attr);
194 if (!node) {
195 nsCOMPtr<nsINodeInfo> ni = aNodeInfo;
196 nsRefPtr<Attr> newAttr =
197 new Attr(this, ni.forget(), EmptyString(), aNsAware);
198 mAttributeCache.Put(attr, newAttr);
199 node = newAttr;
200 }
202 return node;
203 }
205 Attr*
206 nsDOMAttributeMap::NamedGetter(const nsAString& aAttrName, bool& aFound)
207 {
208 aFound = false;
209 NS_ENSURE_TRUE(mContent, nullptr);
211 nsCOMPtr<nsINodeInfo> ni = mContent->GetExistingAttrNameFromQName(aAttrName);
212 if (!ni) {
213 return nullptr;
214 }
216 aFound = true;
217 return GetAttribute(ni, false);
218 }
220 bool
221 nsDOMAttributeMap::NameIsEnumerable(const nsAString& aName)
222 {
223 return true;
224 }
226 Attr*
227 nsDOMAttributeMap::GetNamedItem(const nsAString& aAttrName)
228 {
229 bool dummy;
230 return NamedGetter(aAttrName, dummy);
231 }
233 NS_IMETHODIMP
234 nsDOMAttributeMap::GetNamedItem(const nsAString& aAttrName,
235 nsIDOMAttr** aAttribute)
236 {
237 NS_ENSURE_ARG_POINTER(aAttribute);
239 NS_IF_ADDREF(*aAttribute = GetNamedItem(aAttrName));
241 return NS_OK;
242 }
244 NS_IMETHODIMP
245 nsDOMAttributeMap::SetNamedItem(nsIDOMAttr* aAttr, nsIDOMAttr** aReturn)
246 {
247 Attr* attribute = static_cast<Attr*>(aAttr);
248 NS_ENSURE_ARG(attribute);
250 ErrorResult rv;
251 *aReturn = SetNamedItem(*attribute, rv).take();
252 return rv.ErrorCode();
253 }
255 NS_IMETHODIMP
256 nsDOMAttributeMap::SetNamedItemNS(nsIDOMAttr* aAttr, nsIDOMAttr** aReturn)
257 {
258 Attr* attribute = static_cast<Attr*>(aAttr);
259 NS_ENSURE_ARG(attribute);
261 ErrorResult rv;
262 *aReturn = SetNamedItemNS(*attribute, rv).take();
263 return rv.ErrorCode();
264 }
266 already_AddRefed<Attr>
267 nsDOMAttributeMap::SetNamedItemInternal(Attr& aAttr,
268 bool aWithNS,
269 ErrorResult& aError)
270 {
271 NS_ENSURE_TRUE(mContent, nullptr);
273 // XXX should check same-origin between mContent and aAttr however
274 // nsContentUtils::CheckSameOrigin can't deal with attributenodes yet
276 // Check that attribute is not owned by somebody else
277 nsDOMAttributeMap* owner = aAttr.GetMap();
278 if (owner) {
279 if (owner != this) {
280 aError.Throw(NS_ERROR_DOM_INUSE_ATTRIBUTE_ERR);
281 return nullptr;
282 }
284 // setting a preexisting attribute is a no-op, just return the same
285 // node.
286 nsRefPtr<Attr> attribute = &aAttr;
287 return attribute.forget();
288 }
290 nsresult rv;
291 if (mContent->OwnerDoc() != aAttr.OwnerDoc()) {
292 nsCOMPtr<nsINode> adoptedNode =
293 mContent->OwnerDoc()->AdoptNode(aAttr, aError);
294 if (aError.Failed()) {
295 return nullptr;
296 }
298 NS_ASSERTION(adoptedNode == &aAttr, "Uh, adopt node changed nodes?");
299 }
301 // Get nodeinfo and preexisting attribute (if it exists)
302 nsAutoString name;
303 nsCOMPtr<nsINodeInfo> ni;
305 nsRefPtr<Attr> attr;
306 // SetNamedItemNS()
307 if (aWithNS) {
308 // Return existing attribute, if present
309 ni = aAttr.NodeInfo();
311 if (mContent->HasAttr(ni->NamespaceID(), ni->NameAtom())) {
312 attr = RemoveAttribute(ni);
313 }
314 } else { // SetNamedItem()
315 aAttr.GetName(name);
317 // get node-info of old attribute
318 ni = mContent->GetExistingAttrNameFromQName(name);
319 if (ni) {
320 attr = RemoveAttribute(ni);
321 }
322 else {
323 if (mContent->IsInHTMLDocument() &&
324 mContent->IsHTML()) {
325 nsContentUtils::ASCIIToLower(name);
326 }
328 rv = mContent->NodeInfo()->NodeInfoManager()->
329 GetNodeInfo(name, nullptr, kNameSpaceID_None,
330 nsIDOMNode::ATTRIBUTE_NODE, getter_AddRefs(ni));
331 if (NS_FAILED(rv)) {
332 aError.Throw(rv);
333 return nullptr;
334 }
335 // value is already empty
336 }
337 }
339 nsAutoString value;
340 aAttr.GetValue(value);
342 // Add the new attribute to the attribute map before updating
343 // its value in the element. @see bug 364413.
344 nsAttrKey attrkey(ni->NamespaceID(), ni->NameAtom());
345 mAttributeCache.Put(attrkey, &aAttr);
346 aAttr.SetMap(this);
348 rv = mContent->SetAttr(ni->NamespaceID(), ni->NameAtom(),
349 ni->GetPrefixAtom(), value, true);
350 if (NS_FAILED(rv)) {
351 aError.Throw(rv);
352 DropAttribute(ni->NamespaceID(), ni->NameAtom());
353 }
355 return attr.forget();
356 }
358 NS_IMETHODIMP
359 nsDOMAttributeMap::RemoveNamedItem(const nsAString& aName,
360 nsIDOMAttr** aReturn)
361 {
362 NS_ENSURE_ARG_POINTER(aReturn);
364 ErrorResult rv;
365 *aReturn = RemoveNamedItem(aName, rv).take();
366 return rv.ErrorCode();
367 }
369 already_AddRefed<Attr>
370 nsDOMAttributeMap::RemoveNamedItem(const nsAString& aName, ErrorResult& aError)
371 {
372 if (!mContent) {
373 aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
374 return nullptr;
375 }
377 nsCOMPtr<nsINodeInfo> ni = mContent->GetExistingAttrNameFromQName(aName);
378 if (!ni) {
379 aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
380 return nullptr;
381 }
383 nsRefPtr<Attr> attribute = GetAttribute(ni, true);
385 // This removes the attribute node from the attribute map.
386 aError = mContent->UnsetAttr(ni->NamespaceID(), ni->NameAtom(), true);
387 return attribute.forget();
388 }
391 Attr*
392 nsDOMAttributeMap::IndexedGetter(uint32_t aIndex, bool& aFound)
393 {
394 aFound = false;
395 NS_ENSURE_TRUE(mContent, nullptr);
397 const nsAttrName* name = mContent->GetAttrNameAt(aIndex);
398 NS_ENSURE_TRUE(name, nullptr);
400 aFound = true;
401 // Don't use the nodeinfo even if one exists since it can have the wrong
402 // owner document.
403 nsCOMPtr<nsINodeInfo> ni = mContent->NodeInfo()->NodeInfoManager()->
404 GetNodeInfo(name->LocalName(), name->GetPrefix(), name->NamespaceID(),
405 nsIDOMNode::ATTRIBUTE_NODE);
406 return GetAttribute(ni, true);
407 }
409 Attr*
410 nsDOMAttributeMap::Item(uint32_t aIndex)
411 {
412 bool dummy;
413 return IndexedGetter(aIndex, dummy);
414 }
416 NS_IMETHODIMP
417 nsDOMAttributeMap::Item(uint32_t aIndex, nsIDOMAttr** aReturn)
418 {
419 NS_IF_ADDREF(*aReturn = Item(aIndex));
420 return NS_OK;
421 }
423 uint32_t
424 nsDOMAttributeMap::Length() const
425 {
426 NS_ENSURE_TRUE(mContent, 0);
428 return mContent->GetAttrCount();
429 }
431 nsresult
432 nsDOMAttributeMap::GetLength(uint32_t *aLength)
433 {
434 NS_ENSURE_ARG_POINTER(aLength);
435 *aLength = Length();
436 return NS_OK;
437 }
439 NS_IMETHODIMP
440 nsDOMAttributeMap::GetNamedItemNS(const nsAString& aNamespaceURI,
441 const nsAString& aLocalName,
442 nsIDOMAttr** aReturn)
443 {
444 NS_IF_ADDREF(*aReturn = GetNamedItemNS(aNamespaceURI, aLocalName));
445 return NS_OK;
446 }
448 Attr*
449 nsDOMAttributeMap::GetNamedItemNS(const nsAString& aNamespaceURI,
450 const nsAString& aLocalName)
451 {
452 nsCOMPtr<nsINodeInfo> ni = GetAttrNodeInfo(aNamespaceURI, aLocalName);
453 if (!ni) {
454 return nullptr;
455 }
457 return GetAttribute(ni, true);
458 }
460 already_AddRefed<nsINodeInfo>
461 nsDOMAttributeMap::GetAttrNodeInfo(const nsAString& aNamespaceURI,
462 const nsAString& aLocalName)
463 {
464 if (!mContent) {
465 return nullptr;
466 }
468 int32_t nameSpaceID = kNameSpaceID_None;
470 if (!aNamespaceURI.IsEmpty()) {
471 nameSpaceID =
472 nsContentUtils::NameSpaceManager()->GetNameSpaceID(aNamespaceURI);
474 if (nameSpaceID == kNameSpaceID_Unknown) {
475 return nullptr;
476 }
477 }
479 uint32_t i, count = mContent->GetAttrCount();
480 for (i = 0; i < count; ++i) {
481 const nsAttrName* name = mContent->GetAttrNameAt(i);
482 int32_t attrNS = name->NamespaceID();
483 nsIAtom* nameAtom = name->LocalName();
485 if (nameSpaceID == attrNS &&
486 nameAtom->Equals(aLocalName)) {
487 nsCOMPtr<nsINodeInfo> ni;
488 ni = mContent->NodeInfo()->NodeInfoManager()->
489 GetNodeInfo(nameAtom, name->GetPrefix(), nameSpaceID,
490 nsIDOMNode::ATTRIBUTE_NODE);
492 return ni.forget();
493 }
494 }
496 return nullptr;
497 }
499 NS_IMETHODIMP
500 nsDOMAttributeMap::RemoveNamedItemNS(const nsAString& aNamespaceURI,
501 const nsAString& aLocalName,
502 nsIDOMAttr** aReturn)
503 {
504 NS_ENSURE_ARG_POINTER(aReturn);
505 ErrorResult rv;
506 *aReturn = RemoveNamedItemNS(aNamespaceURI, aLocalName, rv).take();
507 return rv.ErrorCode();
508 }
510 already_AddRefed<Attr>
511 nsDOMAttributeMap::RemoveNamedItemNS(const nsAString& aNamespaceURI,
512 const nsAString& aLocalName,
513 ErrorResult& aError)
514 {
515 nsCOMPtr<nsINodeInfo> ni = GetAttrNodeInfo(aNamespaceURI, aLocalName);
516 if (!ni) {
517 aError.Throw(NS_ERROR_DOM_NOT_FOUND_ERR);
518 return nullptr;
519 }
521 nsRefPtr<Attr> attr = RemoveAttribute(ni);
522 nsINodeInfo* attrNi = attr->NodeInfo();
523 mContent->UnsetAttr(attrNi->NamespaceID(), attrNi->NameAtom(), true);
525 return attr.forget();
526 }
528 uint32_t
529 nsDOMAttributeMap::Count() const
530 {
531 return mAttributeCache.Count();
532 }
534 uint32_t
535 nsDOMAttributeMap::Enumerate(AttrCache::EnumReadFunction aFunc,
536 void *aUserArg) const
537 {
538 return mAttributeCache.EnumerateRead(aFunc, aUserArg);
539 }
541 size_t
542 AttrCacheSizeEnumerator(const nsAttrKey& aKey,
543 const nsRefPtr<Attr>& aValue,
544 MallocSizeOf aMallocSizeOf,
545 void* aUserArg)
546 {
547 return aMallocSizeOf(aValue.get());
548 }
550 size_t
551 nsDOMAttributeMap::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const
552 {
553 size_t n = aMallocSizeOf(this);
554 n += mAttributeCache.SizeOfExcludingThis(AttrCacheSizeEnumerator,
555 aMallocSizeOf);
557 // NB: mContent is non-owning and thus not counted.
558 return n;
559 }
561 /* virtual */ JSObject*
562 nsDOMAttributeMap::WrapObject(JSContext* aCx)
563 {
564 return MozNamedAttrMapBinding::Wrap(aCx, this);
565 }