LCOV - code coverage report
Current view: top level - dom/base - nsNodeInfoManager.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 151 199 75.9 %
Date: 2017-07-14 16:53:18 Functions: 16 26 61.5 %
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             :  * A class for handing out nodeinfos and ensuring sharing of them as needed.
       9             :  */
      10             : 
      11             : #include "nsNodeInfoManager.h"
      12             : 
      13             : #include "mozilla/DebugOnly.h"
      14             : #include "mozilla/Telemetry.h"
      15             : #include "mozilla/dom/NodeInfo.h"
      16             : #include "mozilla/dom/NodeInfoInlines.h"
      17             : #include "nsCOMPtr.h"
      18             : #include "nsString.h"
      19             : #include "nsIAtom.h"
      20             : #include "nsIDocument.h"
      21             : #include "nsIPrincipal.h"
      22             : #include "nsIURI.h"
      23             : #include "nsContentUtils.h"
      24             : #include "nsReadableUtils.h"
      25             : #include "nsGkAtoms.h"
      26             : #include "nsComponentManagerUtils.h"
      27             : #include "nsLayoutStatics.h"
      28             : #include "nsBindingManager.h"
      29             : #include "nsHashKeys.h"
      30             : #include "nsCCUncollectableMarker.h"
      31             : #include "nsNameSpaceManager.h"
      32             : #include "nsDocument.h"
      33             : #include "NullPrincipal.h"
      34             : 
      35             : using namespace mozilla;
      36             : using mozilla::dom::NodeInfo;
      37             : 
      38             : #include "mozilla/Logging.h"
      39             : 
      40             : static LazyLogModule gNodeInfoManagerLeakPRLog("NodeInfoManagerLeak");
      41             : 
      42             : PLHashNumber
      43        7219 : nsNodeInfoManager::GetNodeInfoInnerHashValue(const void *key)
      44             : {
      45        7219 :   MOZ_ASSERT(key, "Null key passed to NodeInfo::GetHashValue!");
      46             : 
      47        7219 :   auto *node = reinterpret_cast<const NodeInfo::NodeInfoInner*>(key);
      48             : 
      49        7219 :   return node->mName ? node->mName->hash() : HashString(*(node->mNameString));
      50             : }
      51             : 
      52             : 
      53             : int
      54        4410 : nsNodeInfoManager::NodeInfoInnerKeyCompare(const void *key1, const void *key2)
      55             : {
      56        4410 :   MOZ_ASSERT(key1 && key2, "Null key passed to NodeInfoInnerKeyCompare!");
      57             : 
      58        4410 :   auto *node1 = reinterpret_cast<const NodeInfo::NodeInfoInner*>(key1);
      59        4410 :   auto *node2 = reinterpret_cast<const NodeInfo::NodeInfoInner*>(key2);
      60             : 
      61       12945 :   if (node1->mPrefix != node2->mPrefix ||
      62        8112 :       node1->mNamespaceID != node2->mNamespaceID ||
      63       12383 :       node1->mNodeType != node2->mNodeType ||
      64        3986 :       node1->mExtraName != node2->mExtraName) {
      65         430 :     return 0;
      66             :   }
      67             : 
      68        3980 :   if (node1->mName) {
      69        3958 :     if (node2->mName) {
      70        3615 :       return (node1->mName == node2->mName);
      71             :     }
      72         343 :     return (node1->mName->Equals(*(node2->mNameString)));
      73             :   }
      74          22 :   if (node2->mName) {
      75          22 :     return (node2->mName->Equals(*(node1->mNameString)));
      76             :   }
      77           0 :   return (node1->mNameString->Equals(*(node2->mNameString)));
      78             : }
      79             : 
      80             : 
      81             : static void* PR_CALLBACK
      82         135 : AllocTable(void* pool, size_t size)
      83             : {
      84         135 :   return malloc(size);
      85             : }
      86             : 
      87             : static void PR_CALLBACK
      88          13 : FreeTable(void* pool, void* item)
      89             : {
      90          13 :   free(item);
      91          13 : }
      92             : 
      93             : static PLHashEntry* PR_CALLBACK
      94        1039 : AllocEntry(void* pool, const void* key)
      95             : {
      96        1039 :   return (PLHashEntry*)malloc(sizeof(PLHashEntry));
      97             : }
      98             : 
      99             : static void PR_CALLBACK
     100         279 : FreeEntry(void* pool, PLHashEntry* he, unsigned flag)
     101             : {
     102         279 :   if (flag == HT_FREE_ENTRY) {
     103         279 :     free(he);
     104             :   }
     105         279 : }
     106             : 
     107             : static PLHashAllocOps allocOps =
     108             :   { AllocTable, FreeTable, AllocEntry, FreeEntry };
     109             : 
     110          61 : nsNodeInfoManager::nsNodeInfoManager()
     111             :   : mDocument(nullptr),
     112             :     mNonDocumentNodeInfos(0),
     113             :     mTextNodeInfo(nullptr),
     114             :     mCommentNodeInfo(nullptr),
     115             :     mDocumentNodeInfo(nullptr),
     116          61 :     mRecentlyUsedNodeInfos{}
     117             : {
     118          61 :   nsLayoutStatics::AddRef();
     119             : 
     120          61 :   if (gNodeInfoManagerLeakPRLog)
     121          61 :     MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug,
     122             :            ("NODEINFOMANAGER %p created", this));
     123             : 
     124          61 :   mNodeInfoHash = PL_NewHashTable(32, GetNodeInfoInnerHashValue,
     125             :                                   NodeInfoInnerKeyCompare,
     126             :                                   PL_CompareValues, &allocOps, nullptr);
     127          61 : }
     128             : 
     129             : 
     130           0 : nsNodeInfoManager::~nsNodeInfoManager()
     131             : {
     132           0 :   if (mNodeInfoHash)
     133           0 :     PL_HashTableDestroy(mNodeInfoHash);
     134             : 
     135             :   // Note: mPrincipal may be null here if we never got inited correctly
     136           0 :   mPrincipal = nullptr;
     137             : 
     138           0 :   mBindingManager = nullptr;
     139             : 
     140           0 :   if (gNodeInfoManagerLeakPRLog)
     141           0 :     MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug,
     142             :            ("NODEINFOMANAGER %p destroyed", this));
     143             : 
     144           0 :   nsLayoutStatics::Release();
     145           0 : }
     146             : 
     147             : NS_IMPL_CYCLE_COLLECTION_CLASS(nsNodeInfoManager)
     148             : 
     149           0 : NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsNodeInfoManager)
     150           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNodeInfoManager)
     151           0 :   if (tmp->mNonDocumentNodeInfos) {
     152           0 :     NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDocument)
     153             :   }
     154           0 :   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBindingManager)
     155           0 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     156             : 
     157           0 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsNodeInfoManager, AddRef)
     158           0 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsNodeInfoManager, Release)
     159             : 
     160           0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsNodeInfoManager)
     161           0 :   if (tmp->mDocument) {
     162           0 :     return NS_CYCLE_COLLECTION_PARTICIPANT(nsDocument)->CanSkip(tmp->mDocument, aRemovingAllowed);
     163             :   }
     164           0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
     165             : 
     166           0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsNodeInfoManager)
     167           0 :   if (tmp->mDocument) {
     168           0 :     return NS_CYCLE_COLLECTION_PARTICIPANT(nsDocument)->CanSkipInCC(tmp->mDocument);
     169             :   }
     170           0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
     171             : 
     172           0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsNodeInfoManager)
     173           0 :   if (tmp->mDocument) {
     174           0 :     return NS_CYCLE_COLLECTION_PARTICIPANT(nsDocument)->CanSkipThis(tmp->mDocument);
     175             :   }
     176           0 : NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
     177             : 
     178             : nsresult
     179          61 : nsNodeInfoManager::Init(nsIDocument *aDocument)
     180             : {
     181          61 :   NS_ENSURE_TRUE(mNodeInfoHash, NS_ERROR_OUT_OF_MEMORY);
     182             : 
     183          61 :   NS_PRECONDITION(!mPrincipal,
     184             :                   "Being inited when we already have a principal?");
     185             : 
     186          61 :   mPrincipal = NullPrincipal::Create();
     187             : 
     188          61 :   if (aDocument) {
     189          55 :     mBindingManager = new nsBindingManager(aDocument);
     190             :   }
     191             : 
     192          61 :   mDefaultPrincipal = mPrincipal;
     193             : 
     194          61 :   mDocument = aDocument;
     195             : 
     196          61 :   if (gNodeInfoManagerLeakPRLog)
     197          61 :     MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug,
     198             :            ("NODEINFOMANAGER %p Init document=%p", this, aDocument));
     199             : 
     200          61 :   return NS_OK;
     201             : }
     202             : 
     203             : // static
     204             : int
     205           0 : nsNodeInfoManager::DropNodeInfoDocument(PLHashEntry *he, int hashIndex, void *arg)
     206             : {
     207           0 :   static_cast<mozilla::dom::NodeInfo*>(he->value)->mDocument = nullptr;
     208           0 :   return HT_ENUMERATE_NEXT;
     209             : }
     210             : 
     211             : void
     212           0 : nsNodeInfoManager::DropDocumentReference()
     213             : {
     214           0 :   if (mBindingManager) {
     215           0 :     mBindingManager->DropDocumentReference();
     216             :   }
     217             : 
     218             :   // This is probably not needed anymore.
     219           0 :   PL_HashTableEnumerateEntries(mNodeInfoHash, DropNodeInfoDocument, nullptr);
     220             : 
     221           0 :   NS_ASSERTION(!mNonDocumentNodeInfos, "Shouldn't have non-document nodeinfos!");
     222           0 :   mDocument = nullptr;
     223           0 : }
     224             : 
     225             : 
     226             : already_AddRefed<mozilla::dom::NodeInfo>
     227        3756 : nsNodeInfoManager::GetNodeInfo(nsIAtom *aName, nsIAtom *aPrefix,
     228             :                                int32_t aNamespaceID, uint16_t aNodeType,
     229             :                                nsIAtom* aExtraName /* = nullptr */)
     230             : {
     231        3756 :   CheckValidNodeInfo(aNodeType, aName, aNamespaceID, aExtraName);
     232             : 
     233             :   NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType,
     234        7512 :                                  aExtraName);
     235             : 
     236             :   uint32_t index =
     237        3756 :     GetNodeInfoInnerHashValue(&tmpKey) % RECENTLY_USED_NODEINFOS_SIZE;
     238        3756 :   NodeInfo* ni = mRecentlyUsedNodeInfos[index];
     239        3756 :   if (ni && NodeInfoInnerKeyCompare(&(ni->mInner), &tmpKey)) {
     240        5958 :     RefPtr<NodeInfo> nodeInfo = ni;
     241        2979 :     return nodeInfo.forget();
     242             :   }
     243             : 
     244         777 :   void *node = PL_HashTableLookup(mNodeInfoHash, &tmpKey);
     245             : 
     246         777 :   if (node) {
     247         370 :     RefPtr<NodeInfo> nodeInfo = static_cast<NodeInfo*>(node);
     248         185 :     mRecentlyUsedNodeInfos[index] = nodeInfo;
     249         185 :     return nodeInfo.forget();
     250             :   }
     251             : 
     252             :   RefPtr<NodeInfo> newNodeInfo =
     253        1184 :     new NodeInfo(aName, aPrefix, aNamespaceID, aNodeType, aExtraName, this);
     254             : 
     255             :   DebugOnly<PLHashEntry*> he =
     256        1184 :     PL_HashTableAdd(mNodeInfoHash, &newNodeInfo->mInner, newNodeInfo);
     257         592 :   MOZ_ASSERT(he, "PL_HashTableAdd() failed");
     258             : 
     259             :   // Have to do the swap thing, because already_AddRefed<nsNodeInfo>
     260             :   // doesn't cast to already_AddRefed<mozilla::dom::NodeInfo>
     261         592 :   ++mNonDocumentNodeInfos;
     262         592 :   if (mNonDocumentNodeInfos == 1) {
     263          85 :     NS_IF_ADDREF(mDocument);
     264             :   }
     265             : 
     266         592 :   mRecentlyUsedNodeInfos[index] = newNodeInfo;
     267         592 :   return newNodeInfo.forget();
     268             : }
     269             : 
     270             : 
     271             : nsresult
     272         620 : nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsIAtom *aPrefix,
     273             :                                int32_t aNamespaceID, uint16_t aNodeType,
     274             :                                NodeInfo** aNodeInfo)
     275             : {
     276             : #ifdef DEBUG
     277             :   {
     278        1240 :     nsCOMPtr<nsIAtom> nameAtom = NS_Atomize(aName);
     279         620 :     CheckValidNodeInfo(aNodeType, nameAtom, aNamespaceID, nullptr);
     280             :   }
     281             : #endif
     282             : 
     283        1240 :   NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType);
     284             : 
     285             :   uint32_t index =
     286         620 :     GetNodeInfoInnerHashValue(&tmpKey) % RECENTLY_USED_NODEINFOS_SIZE;
     287         620 :   NodeInfo* ni = mRecentlyUsedNodeInfos[index];
     288         620 :   if (ni && NodeInfoInnerKeyCompare(&(ni->mInner), &tmpKey)) {
     289         302 :     RefPtr<NodeInfo> nodeInfo = ni;
     290         151 :     nodeInfo.forget(aNodeInfo);
     291         151 :     return NS_OK;
     292             :   }
     293             : 
     294         469 :   void *node = PL_HashTableLookup(mNodeInfoHash, &tmpKey);
     295             : 
     296         469 :   if (node) {
     297          44 :     RefPtr<NodeInfo> nodeInfo = static_cast<NodeInfo*>(node);
     298          22 :     mRecentlyUsedNodeInfos[index] = nodeInfo;
     299          22 :     nodeInfo.forget(aNodeInfo);
     300             : 
     301          22 :     return NS_OK;
     302             :   }
     303             : 
     304         894 :   nsCOMPtr<nsIAtom> nameAtom = NS_Atomize(aName);
     305         447 :   NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY);
     306             : 
     307             :   RefPtr<NodeInfo> newNodeInfo =
     308        1341 :     new NodeInfo(nameAtom, aPrefix, aNamespaceID, aNodeType, nullptr, this);
     309         447 :   NS_ENSURE_TRUE(newNodeInfo, NS_ERROR_OUT_OF_MEMORY);
     310             : 
     311             :   PLHashEntry *he;
     312         447 :   he = PL_HashTableAdd(mNodeInfoHash, &newNodeInfo->mInner, newNodeInfo);
     313         447 :   NS_ENSURE_TRUE(he, NS_ERROR_FAILURE);
     314             : 
     315         447 :   ++mNonDocumentNodeInfos;
     316         447 :   if (mNonDocumentNodeInfos == 1) {
     317          31 :     NS_IF_ADDREF(mDocument);
     318             :   }
     319             : 
     320         447 :   mRecentlyUsedNodeInfos[index] = newNodeInfo;
     321         447 :   newNodeInfo.forget(aNodeInfo);
     322             : 
     323         447 :   return NS_OK;
     324             : }
     325             : 
     326             : 
     327             : nsresult
     328         395 : nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsIAtom *aPrefix,
     329             :                                const nsAString& aNamespaceURI,
     330             :                                uint16_t aNodeType,
     331             :                                NodeInfo** aNodeInfo)
     332             : {
     333         395 :   int32_t nsid = kNameSpaceID_None;
     334             : 
     335         395 :   if (!aNamespaceURI.IsEmpty()) {
     336             :     nsresult rv = nsContentUtils::NameSpaceManager()->
     337         117 :       RegisterNameSpace(aNamespaceURI, nsid);
     338         117 :     NS_ENSURE_SUCCESS(rv, rv);
     339             :   }
     340             : 
     341         395 :   return GetNodeInfo(aName, aPrefix, nsid, aNodeType, aNodeInfo);
     342             : }
     343             : 
     344             : already_AddRefed<NodeInfo>
     345         331 : nsNodeInfoManager::GetTextNodeInfo()
     346             : {
     347         662 :   RefPtr<mozilla::dom::NodeInfo> nodeInfo;
     348             : 
     349         331 :   if (!mTextNodeInfo) {
     350          54 :     nodeInfo = GetNodeInfo(nsGkAtoms::textTagName, nullptr, kNameSpaceID_None,
     351          27 :                            nsIDOMNode::TEXT_NODE, nullptr);
     352             :     // Hold a weak ref; the nodeinfo will let us know when it goes away
     353          27 :     mTextNodeInfo = nodeInfo;
     354             :   } else {
     355         304 :     nodeInfo = mTextNodeInfo;
     356             :   }
     357             : 
     358         662 :   return nodeInfo.forget();
     359             : }
     360             : 
     361             : already_AddRefed<NodeInfo>
     362          36 : nsNodeInfoManager::GetCommentNodeInfo()
     363             : {
     364          72 :   RefPtr<NodeInfo> nodeInfo;
     365             : 
     366          36 :   if (!mCommentNodeInfo) {
     367          54 :     nodeInfo = GetNodeInfo(nsGkAtoms::commentTagName, nullptr,
     368             :                            kNameSpaceID_None, nsIDOMNode::COMMENT_NODE,
     369          27 :                            nullptr);
     370             :     // Hold a weak ref; the nodeinfo will let us know when it goes away
     371          27 :     mCommentNodeInfo = nodeInfo;
     372             :   }
     373             :   else {
     374           9 :     nodeInfo = mCommentNodeInfo;
     375             :   }
     376             : 
     377          72 :   return nodeInfo.forget();
     378             : }
     379             : 
     380             : already_AddRefed<NodeInfo>
     381          55 : nsNodeInfoManager::GetDocumentNodeInfo()
     382             : {
     383         110 :   RefPtr<NodeInfo> nodeInfo;
     384             : 
     385          55 :   if (!mDocumentNodeInfo) {
     386          55 :     NS_ASSERTION(mDocument, "Should have mDocument!");
     387         110 :     nodeInfo = GetNodeInfo(nsGkAtoms::documentNodeName, nullptr,
     388             :                            kNameSpaceID_None, nsIDOMNode::DOCUMENT_NODE,
     389          55 :                            nullptr);
     390             :     // Hold a weak ref; the nodeinfo will let us know when it goes away
     391          55 :     mDocumentNodeInfo = nodeInfo;
     392             : 
     393          55 :     --mNonDocumentNodeInfos;
     394          55 :     if (!mNonDocumentNodeInfos) {
     395          55 :       mDocument->Release(); // Don't set mDocument to null!
     396             :     }
     397             :   }
     398             :   else {
     399           0 :     nodeInfo = mDocumentNodeInfo;
     400             :   }
     401             : 
     402         110 :   return nodeInfo.forget();
     403             : }
     404             : 
     405             : void
     406          90 : nsNodeInfoManager::SetDocumentPrincipal(nsIPrincipal *aPrincipal)
     407             : {
     408          90 :   mPrincipal = nullptr;
     409          90 :   if (!aPrincipal) {
     410          29 :     aPrincipal = mDefaultPrincipal;
     411             :   }
     412             : 
     413          90 :   NS_ASSERTION(aPrincipal, "Must have principal by this point!");
     414          90 :   MOZ_DIAGNOSTIC_ASSERT(!nsContentUtils::IsExpandedPrincipal(aPrincipal),
     415             :                         "Documents shouldn't have an expanded principal");
     416          90 :   if (nsContentUtils::IsExpandedPrincipal(aPrincipal)) {
     417           0 :     Telemetry::Accumulate(Telemetry::DOCUMENT_WITH_EXPANDED_PRINCIPAL, 1);
     418             :   }
     419             : 
     420          90 :   mPrincipal = aPrincipal;
     421          90 : }
     422             : 
     423             : void
     424         279 : nsNodeInfoManager::RemoveNodeInfo(NodeInfo *aNodeInfo)
     425             : {
     426         279 :   NS_PRECONDITION(aNodeInfo, "Trying to remove null nodeinfo from manager!");
     427             : 
     428         279 :   if (aNodeInfo == mDocumentNodeInfo) {
     429           0 :     mDocumentNodeInfo = nullptr;
     430           0 :     mDocument = nullptr;
     431             :   } else {
     432         279 :     if (--mNonDocumentNodeInfos == 0) {
     433           0 :       if (mDocument) {
     434             :         // Note, whoever calls this method should keep NodeInfoManager alive,
     435             :         // even if mDocument gets deleted.
     436           0 :         mDocument->Release();
     437             :       }
     438             :     }
     439             :     // Drop weak reference if needed
     440         279 :     if (aNodeInfo == mTextNodeInfo) {
     441           0 :       mTextNodeInfo = nullptr;
     442             :     }
     443         279 :     else if (aNodeInfo == mCommentNodeInfo) {
     444           0 :       mCommentNodeInfo = nullptr;
     445             :     }
     446             :   }
     447             : 
     448             :   uint32_t index =
     449         279 :     GetNodeInfoInnerHashValue(&aNodeInfo->mInner) % RECENTLY_USED_NODEINFOS_SIZE;
     450         279 :   if (mRecentlyUsedNodeInfos[index] == aNodeInfo) {
     451          67 :     mRecentlyUsedNodeInfos[index] = nullptr;
     452             :   }
     453             : 
     454             : #ifdef DEBUG
     455             :   bool ret =
     456             : #endif
     457         279 :   PL_HashTableRemove(mNodeInfoHash, &aNodeInfo->mInner);
     458             : 
     459         279 :   NS_POSTCONDITION(ret, "Can't find mozilla::dom::NodeInfo to remove!!!");
     460         279 : }

Generated by: LCOV version 1.13