LCOV - code coverage report
Current view: top level - dom/base - nsDOMTokenList.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 55 204 27.0 %
Date: 2017-07-14 16:53:18 Functions: 12 31 38.7 %
Legend: Lines: hit not hit

          Line data    Source code
       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/. */
       6             : 
       7             : /*
       8             :  * Implementation of DOMTokenList specified by HTML5.
       9             :  */
      10             : 
      11             : #include "nsDOMTokenList.h"
      12             : #include "nsAttrValue.h"
      13             : #include "nsAttrValueInlines.h"
      14             : #include "nsDataHashtable.h"
      15             : #include "nsError.h"
      16             : #include "nsHashKeys.h"
      17             : #include "mozilla/dom/Element.h"
      18             : #include "mozilla/dom/DOMTokenListBinding.h"
      19             : #include "mozilla/BloomFilter.h"
      20             : #include "mozilla/ErrorResult.h"
      21             : 
      22             : using namespace mozilla;
      23             : using namespace mozilla::dom;
      24             : 
      25           8 : nsDOMTokenList::nsDOMTokenList(Element* aElement, nsIAtom* aAttrAtom,
      26           8 :                                const DOMTokenListSupportedTokenArray aSupportedTokens)
      27             :   : mElement(aElement),
      28             :     mAttrAtom(aAttrAtom),
      29           8 :     mSupportedTokens(aSupportedTokens)
      30             : {
      31             :   // We don't add a reference to our element. If it goes away,
      32             :   // we'll be told to drop our reference
      33           8 : }
      34             : 
      35           0 : nsDOMTokenList::~nsDOMTokenList() { }
      36             : 
      37          24 : NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsDOMTokenList, mElement)
      38             : 
      39          80 : NS_INTERFACE_MAP_BEGIN(nsDOMTokenList)
      40          80 :   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
      41          72 :   NS_INTERFACE_MAP_ENTRY(nsISupports)
      42          72 :   NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(nsDOMTokenList)
      43           0 : NS_INTERFACE_MAP_END
      44             : 
      45          16 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMTokenList)
      46           0 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMTokenList)
      47             : 
      48             : const nsAttrValue*
      49           8 : nsDOMTokenList::GetParsedAttr()
      50             : {
      51           8 :   if (!mElement) {
      52           0 :     return nullptr;
      53             :   }
      54           8 :   return mElement->GetAttrInfo(kNameSpaceID_None, mAttrAtom).mValue;
      55             : }
      56             : 
      57             : void
      58           4 : nsDOMTokenList::RemoveDuplicates(const nsAttrValue* aAttr)
      59             : {
      60           4 :   if (!aAttr || aAttr->Type() != nsAttrValue::eAtomArray) {
      61           8 :     return;
      62             :   }
      63             : 
      64           0 :   BloomFilter<8, nsIAtom> filter;
      65           0 :   nsAttrValue::AtomArray* array = aAttr->GetAtomArrayValue();
      66           0 :   for (uint32_t i = 0; i < array->Length(); i++) {
      67           0 :     nsIAtom* atom = array->ElementAt(i);
      68           0 :     if (filter.mightContain(atom)) {
      69             :       // Start again, with a hashtable
      70           0 :       RemoveDuplicatesInternal(array, i);
      71           0 :       return;
      72             :     } else {
      73           0 :       filter.add(atom);
      74             :     }
      75             :   }
      76             : }
      77             : 
      78             : void
      79           0 : nsDOMTokenList::RemoveDuplicatesInternal(nsAttrValue::AtomArray* aArray,
      80             :                                          uint32_t aStart)
      81             : {
      82           0 :   nsDataHashtable<nsPtrHashKey<nsIAtom>, bool> tokens;
      83             : 
      84           0 :   for (uint32_t i = 0; i < aArray->Length(); i++) {
      85           0 :     nsIAtom* atom = aArray->ElementAt(i);
      86             :     // No need to check the hashtable below aStart
      87           0 :     if (i >= aStart && tokens.Get(atom)) {
      88           0 :       aArray->RemoveElementAt(i);
      89           0 :       i--;
      90             :     } else {
      91           0 :       tokens.Put(atom, true);
      92             :     }
      93             :   }
      94           0 : }
      95             : 
      96             : uint32_t
      97           0 : nsDOMTokenList::Length()
      98             : {
      99           0 :   const nsAttrValue* attr = GetParsedAttr();
     100           0 :   if (!attr) {
     101           0 :     return 0;
     102             :   }
     103             : 
     104           0 :   RemoveDuplicates(attr);
     105           0 :   return attr->GetAtomCount();
     106             : }
     107             : 
     108             : void
     109           0 : nsDOMTokenList::IndexedGetter(uint32_t aIndex, bool& aFound, nsAString& aResult)
     110             : {
     111           0 :   const nsAttrValue* attr = GetParsedAttr();
     112             : 
     113           0 :   if (!attr || aIndex >= static_cast<uint32_t>(attr->GetAtomCount())) {
     114           0 :     aFound = false;
     115           0 :     return;
     116             :   }
     117             : 
     118           0 :   RemoveDuplicates(attr);
     119             : 
     120           0 :   if (attr && aIndex < static_cast<uint32_t>(attr->GetAtomCount())) {
     121           0 :     aFound = true;
     122           0 :     attr->AtomAt(aIndex)->ToString(aResult);
     123             :   } else {
     124           0 :     aFound = false;
     125             :   }
     126             : }
     127             : 
     128             : void
     129           0 : nsDOMTokenList::SetValue(const nsAString& aValue, ErrorResult& rv)
     130             : {
     131           0 :   if (!mElement) {
     132           0 :     return;
     133             :   }
     134             : 
     135           0 :   rv = mElement->SetAttr(kNameSpaceID_None, mAttrAtom, aValue, true);
     136             : }
     137             : 
     138             : nsresult
     139           8 : nsDOMTokenList::CheckToken(const nsAString& aStr)
     140             : {
     141           8 :   if (aStr.IsEmpty()) {
     142           0 :     return NS_ERROR_DOM_SYNTAX_ERR;
     143             :   }
     144             : 
     145           8 :   nsAString::const_iterator iter, end;
     146           8 :   aStr.BeginReading(iter);
     147           8 :   aStr.EndReading(end);
     148             : 
     149         318 :   while (iter != end) {
     150         155 :     if (nsContentUtils::IsHTMLWhitespace(*iter))
     151           0 :       return NS_ERROR_DOM_INVALID_CHARACTER_ERR;
     152         155 :     ++iter;
     153             :   }
     154             : 
     155           8 :   return NS_OK;
     156             : }
     157             : 
     158             : nsresult
     159           8 : nsDOMTokenList::CheckTokens(const nsTArray<nsString>& aTokens)
     160             : {
     161          16 :   for (uint32_t i = 0, l = aTokens.Length(); i < l; ++i) {
     162           8 :     nsresult rv = CheckToken(aTokens[i]);
     163           8 :     if (NS_FAILED(rv)) {
     164           0 :       return rv;
     165             :     }
     166             :   }
     167             : 
     168           8 :   return NS_OK;
     169             : }
     170             : 
     171             : bool
     172           0 : nsDOMTokenList::Contains(const nsAString& aToken)
     173             : {
     174           0 :   const nsAttrValue* attr = GetParsedAttr();
     175           0 :   return attr && attr->Contains(aToken);
     176             : }
     177             : 
     178             : void
     179           8 : nsDOMTokenList::AddInternal(const nsAttrValue* aAttr,
     180             :                             const nsTArray<nsString>& aTokens)
     181             : {
     182           8 :   if (!mElement) {
     183           0 :     return;
     184             :   }
     185             : 
     186          16 :   nsAutoString resultStr;
     187             : 
     188           8 :   if (aAttr) {
     189           4 :     RemoveDuplicates(aAttr);
     190           8 :     for (uint32_t i = 0; i < aAttr->GetAtomCount(); i++) {
     191           4 :       if (i != 0) {
     192           0 :         resultStr.AppendLiteral(" ");
     193             :       }
     194           4 :       resultStr.Append(nsDependentAtomString(aAttr->AtomAt(i)));
     195             :     }
     196             :   }
     197             : 
     198          16 :   AutoTArray<nsString, 10> addedClasses;
     199             : 
     200          16 :   for (uint32_t i = 0, l = aTokens.Length(); i < l; ++i) {
     201           8 :     const nsString& aToken = aTokens[i];
     202             : 
     203          16 :     if ((aAttr && aAttr->Contains(aToken)) ||
     204           8 :         addedClasses.Contains(aToken)) {
     205           0 :       continue;
     206             :     }
     207             : 
     208           8 :     if (!resultStr.IsEmpty()) {
     209           4 :       resultStr.Append(' ');
     210             :     }
     211           8 :     resultStr.Append(aToken);
     212             : 
     213           8 :     addedClasses.AppendElement(aToken);
     214             :   }
     215             : 
     216           8 :   mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
     217             : }
     218             : 
     219             : void
     220           8 : nsDOMTokenList::Add(const nsTArray<nsString>& aTokens, ErrorResult& aError)
     221             : {
     222           8 :   aError = CheckTokens(aTokens);
     223           8 :   if (aError.Failed()) {
     224           0 :     return;
     225             :   }
     226             : 
     227           8 :   const nsAttrValue* attr = GetParsedAttr();
     228           8 :   AddInternal(attr, aTokens);
     229             : }
     230             : 
     231             : void
     232           0 : nsDOMTokenList::Add(const nsAString& aToken, ErrorResult& aError)
     233             : {
     234           0 :   AutoTArray<nsString, 1> tokens;
     235           0 :   tokens.AppendElement(aToken);
     236           0 :   Add(tokens, aError);
     237           0 : }
     238             : 
     239             : void
     240           0 : nsDOMTokenList::RemoveInternal(const nsAttrValue* aAttr,
     241             :                                const nsTArray<nsString>& aTokens)
     242             : {
     243           0 :   MOZ_ASSERT(aAttr, "Need an attribute");
     244             : 
     245           0 :   RemoveDuplicates(aAttr);
     246             : 
     247           0 :   nsAutoString resultStr;
     248           0 :   for (uint32_t i = 0; i < aAttr->GetAtomCount(); i++) {
     249           0 :     if (aTokens.Contains(nsDependentAtomString(aAttr->AtomAt(i)))) {
     250           0 :       continue;
     251             :     }
     252           0 :     if (!resultStr.IsEmpty()) {
     253           0 :       resultStr.AppendLiteral(" ");
     254             :     }
     255           0 :     resultStr.Append(nsDependentAtomString(aAttr->AtomAt(i)));
     256             :   }
     257             : 
     258           0 :   mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
     259           0 : }
     260             : 
     261             : void
     262           0 : nsDOMTokenList::Remove(const nsTArray<nsString>& aTokens, ErrorResult& aError)
     263             : {
     264           0 :   aError = CheckTokens(aTokens);
     265           0 :   if (aError.Failed()) {
     266           0 :     return;
     267             :   }
     268             : 
     269           0 :   const nsAttrValue* attr = GetParsedAttr();
     270           0 :   if (!attr) {
     271           0 :     return;
     272             :   }
     273             : 
     274           0 :   RemoveInternal(attr, aTokens);
     275             : }
     276             : 
     277             : void
     278           0 : nsDOMTokenList::Remove(const nsAString& aToken, ErrorResult& aError)
     279             : {
     280           0 :   AutoTArray<nsString, 1> tokens;
     281           0 :   tokens.AppendElement(aToken);
     282           0 :   Remove(tokens, aError);
     283           0 : }
     284             : 
     285             : bool
     286           0 : nsDOMTokenList::Toggle(const nsAString& aToken,
     287             :                        const Optional<bool>& aForce,
     288             :                        ErrorResult& aError)
     289             : {
     290           0 :   aError = CheckToken(aToken);
     291           0 :   if (aError.Failed()) {
     292           0 :     return false;
     293             :   }
     294             : 
     295           0 :   const nsAttrValue* attr = GetParsedAttr();
     296           0 :   const bool forceOn = aForce.WasPassed() && aForce.Value();
     297           0 :   const bool forceOff = aForce.WasPassed() && !aForce.Value();
     298             : 
     299           0 :   bool isPresent = attr && attr->Contains(aToken);
     300           0 :   AutoTArray<nsString, 1> tokens;
     301           0 :   (*tokens.AppendElement()).Rebind(aToken.Data(), aToken.Length());
     302             : 
     303           0 :   if (isPresent) {
     304           0 :     if (!forceOn) {
     305           0 :       RemoveInternal(attr, tokens);
     306           0 :       isPresent = false;
     307             :     }
     308             :   } else {
     309           0 :     if (!forceOff) {
     310           0 :       AddInternal(attr, tokens);
     311           0 :       isPresent = true;
     312             :     }
     313             :   }
     314             : 
     315           0 :   return isPresent;
     316             : }
     317             : 
     318             : void
     319           0 : nsDOMTokenList::Replace(const nsAString& aToken,
     320             :                         const nsAString& aNewToken,
     321             :                         ErrorResult& aError)
     322             : {
     323             :   // Doing this here instead of using `CheckToken` because if aToken had invalid
     324             :   // characters, and aNewToken is empty, the returned error should be a
     325             :   // SyntaxError, not an InvalidCharacterError.
     326           0 :   if (aNewToken.IsEmpty()) {
     327           0 :     aError.Throw(NS_ERROR_DOM_SYNTAX_ERR);
     328           0 :     return;
     329             :   }
     330             : 
     331           0 :   aError = CheckToken(aToken);
     332           0 :   if (aError.Failed()) {
     333           0 :     return;
     334             :   }
     335             : 
     336           0 :   aError = CheckToken(aNewToken);
     337           0 :   if (aError.Failed()) {
     338           0 :     return;
     339             :   }
     340             : 
     341           0 :   const nsAttrValue* attr = GetParsedAttr();
     342           0 :   if (!attr) {
     343           0 :     return;
     344             :   }
     345             : 
     346           0 :   ReplaceInternal(attr, aToken, aNewToken);
     347             : }
     348             : 
     349             : void
     350           0 : nsDOMTokenList::ReplaceInternal(const nsAttrValue* aAttr,
     351             :                                 const nsAString& aToken,
     352             :                                 const nsAString& aNewToken)
     353             : {
     354           0 :   RemoveDuplicates(aAttr);
     355             : 
     356           0 :   bool sawIt = false;
     357           0 :   nsAutoString resultStr;
     358           0 :   for (uint32_t i = 0; i < aAttr->GetAtomCount(); i++) {
     359           0 :     if (aAttr->AtomAt(i)->Equals(aToken) ||
     360           0 :         aAttr->AtomAt(i)->Equals(aNewToken)) {
     361           0 :       if (sawIt) {
     362             :         // We keep only the first
     363           0 :         continue;
     364             :       }
     365           0 :       sawIt = true;
     366           0 :       if (!resultStr.IsEmpty()) {
     367           0 :         resultStr.AppendLiteral(" ");
     368             :       }
     369           0 :       resultStr.Append(aNewToken);
     370           0 :       continue;
     371             :     }
     372           0 :     if (!resultStr.IsEmpty()) {
     373           0 :       resultStr.AppendLiteral(" ");
     374             :     }
     375           0 :     resultStr.Append(nsDependentAtomString(aAttr->AtomAt(i)));
     376             :   }
     377             : 
     378           0 :   if (sawIt) {
     379           0 :     mElement->SetAttr(kNameSpaceID_None, mAttrAtom, resultStr, true);
     380             :   }
     381           0 : }
     382             : 
     383             : bool
     384           0 : nsDOMTokenList::Supports(const nsAString& aToken,
     385             :                          ErrorResult& aError)
     386             : {
     387           0 :   if (!mSupportedTokens) {
     388           0 :     aError.ThrowTypeError<MSG_TOKENLIST_NO_SUPPORTED_TOKENS>(
     389           0 :       mElement->LocalName(),
     390           0 :       nsDependentAtomString(mAttrAtom));
     391           0 :     return false;
     392             :   }
     393             : 
     394           0 :   for (DOMTokenListSupportedToken* supportedToken = mSupportedTokens;
     395           0 :        *supportedToken;
     396             :        ++supportedToken) {
     397           0 :     if (aToken.LowerCaseEqualsASCII(*supportedToken)) {
     398           0 :       return true;
     399             :     }
     400             :   }
     401             : 
     402           0 :   return false;
     403             : }
     404             : 
     405             : void
     406           0 : nsDOMTokenList::Stringify(nsAString& aResult)
     407             : {
     408           0 :   if (!mElement) {
     409           0 :     aResult.Truncate();
     410           0 :     return;
     411             :   }
     412             : 
     413           0 :   mElement->GetAttr(kNameSpaceID_None, mAttrAtom, aResult);
     414             : }
     415             : 
     416             : JSObject*
     417           8 : nsDOMTokenList::WrapObject(JSContext *cx, JS::Handle<JSObject*> aGivenProto)
     418             : {
     419           8 :   return DOMTokenListBinding::Wrap(cx, this, aGivenProto);
     420             : }
     421             : 

Generated by: LCOV version 1.13