content/base/src/nsDOMAttributeMap.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

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 }

mercurial