1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/base/src/nsNodeInfoManager.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,429 @@ 1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ 1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.9 + 1.10 +/* 1.11 + * A class for handing out nodeinfos and ensuring sharing of them as needed. 1.12 + */ 1.13 + 1.14 +#include "nsNodeInfoManager.h" 1.15 + 1.16 +#include "mozilla/DebugOnly.h" 1.17 +#include "nsNodeInfo.h" 1.18 +#include "nsCOMPtr.h" 1.19 +#include "nsString.h" 1.20 +#include "nsIAtom.h" 1.21 +#include "nsIDocument.h" 1.22 +#include "nsIPrincipal.h" 1.23 +#include "nsIURI.h" 1.24 +#include "nsContentUtils.h" 1.25 +#include "nsReadableUtils.h" 1.26 +#include "nsGkAtoms.h" 1.27 +#include "nsComponentManagerUtils.h" 1.28 +#include "nsLayoutStatics.h" 1.29 +#include "nsBindingManager.h" 1.30 +#include "nsHashKeys.h" 1.31 +#include "nsCCUncollectableMarker.h" 1.32 + 1.33 +using namespace mozilla; 1.34 + 1.35 +#ifdef MOZ_LOGGING 1.36 +// so we can get logging even in release builds 1.37 +#define FORCE_PR_LOG 1 1.38 +#endif 1.39 +#include "prlog.h" 1.40 + 1.41 +#ifdef PR_LOGGING 1.42 +static PRLogModuleInfo* gNodeInfoManagerLeakPRLog; 1.43 +#endif 1.44 + 1.45 +PLHashNumber 1.46 +nsNodeInfoManager::GetNodeInfoInnerHashValue(const void *key) 1.47 +{ 1.48 + NS_ASSERTION(key, "Null key passed to nsNodeInfo::GetHashValue!"); 1.49 + 1.50 + const nsINodeInfo::nsNodeInfoInner *node = 1.51 + reinterpret_cast<const nsINodeInfo::nsNodeInfoInner *>(key); 1.52 + 1.53 + return node->mName ? node->mName->hash() : HashString(*(node->mNameString)); 1.54 +} 1.55 + 1.56 + 1.57 +int 1.58 +nsNodeInfoManager::NodeInfoInnerKeyCompare(const void *key1, const void *key2) 1.59 +{ 1.60 + NS_ASSERTION(key1 && key2, "Null key passed to NodeInfoInnerKeyCompare!"); 1.61 + 1.62 + const nsINodeInfo::nsNodeInfoInner *node1 = 1.63 + reinterpret_cast<const nsINodeInfo::nsNodeInfoInner *>(key1); 1.64 + const nsINodeInfo::nsNodeInfoInner *node2 = 1.65 + reinterpret_cast<const nsINodeInfo::nsNodeInfoInner *>(key2); 1.66 + 1.67 + if (node1->mPrefix != node2->mPrefix || 1.68 + node1->mNamespaceID != node2->mNamespaceID || 1.69 + node1->mNodeType != node2->mNodeType || 1.70 + node1->mExtraName != node2->mExtraName) { 1.71 + return 0; 1.72 + } 1.73 + 1.74 + if (node1->mName) { 1.75 + if (node2->mName) { 1.76 + return (node1->mName == node2->mName); 1.77 + } 1.78 + return (node1->mName->Equals(*(node2->mNameString))); 1.79 + } 1.80 + if (node2->mName) { 1.81 + return (node2->mName->Equals(*(node1->mNameString))); 1.82 + } 1.83 + return (node1->mNameString->Equals(*(node2->mNameString))); 1.84 +} 1.85 + 1.86 + 1.87 +static void* PR_CALLBACK 1.88 +AllocTable(void* pool, size_t size) 1.89 +{ 1.90 + return malloc(size); 1.91 +} 1.92 + 1.93 +static void PR_CALLBACK 1.94 +FreeTable(void* pool, void* item) 1.95 +{ 1.96 + free(item); 1.97 +} 1.98 + 1.99 +static PLHashEntry* PR_CALLBACK 1.100 +AllocEntry(void* pool, const void* key) 1.101 +{ 1.102 + return (PLHashEntry*)malloc(sizeof(PLHashEntry)); 1.103 +} 1.104 + 1.105 +static void PR_CALLBACK 1.106 +FreeEntry(void* pool, PLHashEntry* he, unsigned flag) 1.107 +{ 1.108 + if (flag == HT_FREE_ENTRY) { 1.109 + free(he); 1.110 + } 1.111 +} 1.112 + 1.113 +static PLHashAllocOps allocOps = 1.114 + { AllocTable, FreeTable, AllocEntry, FreeEntry }; 1.115 + 1.116 +nsNodeInfoManager::nsNodeInfoManager() 1.117 + : mDocument(nullptr), 1.118 + mNonDocumentNodeInfos(0), 1.119 + mTextNodeInfo(nullptr), 1.120 + mCommentNodeInfo(nullptr), 1.121 + mDocumentNodeInfo(nullptr) 1.122 +{ 1.123 + nsLayoutStatics::AddRef(); 1.124 + 1.125 +#ifdef PR_LOGGING 1.126 + if (!gNodeInfoManagerLeakPRLog) 1.127 + gNodeInfoManagerLeakPRLog = PR_NewLogModule("NodeInfoManagerLeak"); 1.128 + 1.129 + if (gNodeInfoManagerLeakPRLog) 1.130 + PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG, 1.131 + ("NODEINFOMANAGER %p created", this)); 1.132 +#endif 1.133 + 1.134 + mNodeInfoHash = PL_NewHashTable(32, GetNodeInfoInnerHashValue, 1.135 + NodeInfoInnerKeyCompare, 1.136 + PL_CompareValues, &allocOps, nullptr); 1.137 +} 1.138 + 1.139 + 1.140 +nsNodeInfoManager::~nsNodeInfoManager() 1.141 +{ 1.142 + if (mNodeInfoHash) 1.143 + PL_HashTableDestroy(mNodeInfoHash); 1.144 + 1.145 + // Note: mPrincipal may be null here if we never got inited correctly 1.146 + mPrincipal = nullptr; 1.147 + 1.148 + mBindingManager = nullptr; 1.149 + 1.150 +#ifdef PR_LOGGING 1.151 + if (gNodeInfoManagerLeakPRLog) 1.152 + PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG, 1.153 + ("NODEINFOMANAGER %p destroyed", this)); 1.154 +#endif 1.155 + 1.156 + nsLayoutStatics::Release(); 1.157 +} 1.158 + 1.159 +NS_IMPL_CYCLE_COLLECTION_CLASS(nsNodeInfoManager) 1.160 + 1.161 +NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsNodeInfoManager) 1.162 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNodeInfoManager) 1.163 + if (tmp->mDocument && 1.164 + nsCCUncollectableMarker::InGeneration(cb, 1.165 + tmp->mDocument->GetMarkedCCGeneration())) { 1.166 + return NS_SUCCESS_INTERRUPTED_TRAVERSE; 1.167 + } 1.168 + if (tmp->mNonDocumentNodeInfos) { 1.169 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDocument) 1.170 + } 1.171 + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBindingManager) 1.172 +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1.173 + 1.174 +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsNodeInfoManager, AddRef) 1.175 +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsNodeInfoManager, Release) 1.176 + 1.177 +nsresult 1.178 +nsNodeInfoManager::Init(nsIDocument *aDocument) 1.179 +{ 1.180 + NS_ENSURE_TRUE(mNodeInfoHash, NS_ERROR_OUT_OF_MEMORY); 1.181 + 1.182 + NS_PRECONDITION(!mPrincipal, 1.183 + "Being inited when we already have a principal?"); 1.184 + nsresult rv; 1.185 + mPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv); 1.186 + NS_ENSURE_TRUE(mPrincipal, rv); 1.187 + 1.188 + if (aDocument) { 1.189 + mBindingManager = new nsBindingManager(aDocument); 1.190 + } 1.191 + 1.192 + mDefaultPrincipal = mPrincipal; 1.193 + 1.194 + mDocument = aDocument; 1.195 + 1.196 +#ifdef PR_LOGGING 1.197 + if (gNodeInfoManagerLeakPRLog) 1.198 + PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG, 1.199 + ("NODEINFOMANAGER %p Init document=%p", this, aDocument)); 1.200 +#endif 1.201 + 1.202 + return NS_OK; 1.203 +} 1.204 + 1.205 +// static 1.206 +int 1.207 +nsNodeInfoManager::DropNodeInfoDocument(PLHashEntry *he, int hashIndex, void *arg) 1.208 +{ 1.209 + static_cast<nsINodeInfo*>(he->value)->mDocument = nullptr; 1.210 + return HT_ENUMERATE_NEXT; 1.211 +} 1.212 + 1.213 +void 1.214 +nsNodeInfoManager::DropDocumentReference() 1.215 +{ 1.216 + if (mBindingManager) { 1.217 + mBindingManager->DropDocumentReference(); 1.218 + } 1.219 + 1.220 + // This is probably not needed anymore. 1.221 + PL_HashTableEnumerateEntries(mNodeInfoHash, DropNodeInfoDocument, nullptr); 1.222 + 1.223 + NS_ASSERTION(!mNonDocumentNodeInfos, "Shouldn't have non-document nodeinfos!"); 1.224 + mDocument = nullptr; 1.225 +} 1.226 + 1.227 + 1.228 +already_AddRefed<nsINodeInfo> 1.229 +nsNodeInfoManager::GetNodeInfo(nsIAtom *aName, nsIAtom *aPrefix, 1.230 + int32_t aNamespaceID, uint16_t aNodeType, 1.231 + nsIAtom* aExtraName /* = nullptr */) 1.232 +{ 1.233 + CheckValidNodeInfo(aNodeType, aName, aNamespaceID, aExtraName); 1.234 + 1.235 + nsINodeInfo::nsNodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType, 1.236 + aExtraName); 1.237 + 1.238 + void *node = PL_HashTableLookup(mNodeInfoHash, &tmpKey); 1.239 + 1.240 + if (node) { 1.241 + nsCOMPtr<nsINodeInfo> nodeInfo = static_cast<nsINodeInfo*>(node); 1.242 + 1.243 + return nodeInfo.forget(); 1.244 + } 1.245 + 1.246 + nsRefPtr<nsNodeInfo> newNodeInfo = 1.247 + new nsNodeInfo(aName, aPrefix, aNamespaceID, aNodeType, aExtraName, this); 1.248 + 1.249 + DebugOnly<PLHashEntry*> he = 1.250 + PL_HashTableAdd(mNodeInfoHash, &newNodeInfo->mInner, newNodeInfo); 1.251 + MOZ_ASSERT(he, "PL_HashTableAdd() failed"); 1.252 + 1.253 + // Have to do the swap thing, because already_AddRefed<nsNodeInfo> 1.254 + // doesn't cast to already_AddRefed<nsINodeInfo> 1.255 + ++mNonDocumentNodeInfos; 1.256 + if (mNonDocumentNodeInfos == 1) { 1.257 + NS_IF_ADDREF(mDocument); 1.258 + } 1.259 + 1.260 + return newNodeInfo.forget(); 1.261 +} 1.262 + 1.263 + 1.264 +nsresult 1.265 +nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsIAtom *aPrefix, 1.266 + int32_t aNamespaceID, uint16_t aNodeType, 1.267 + nsINodeInfo** aNodeInfo) 1.268 +{ 1.269 +#ifdef DEBUG 1.270 + { 1.271 + nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aName); 1.272 + CheckValidNodeInfo(aNodeType, nameAtom, aNamespaceID, nullptr); 1.273 + } 1.274 +#endif 1.275 + 1.276 + nsINodeInfo::nsNodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType); 1.277 + 1.278 + void *node = PL_HashTableLookup(mNodeInfoHash, &tmpKey); 1.279 + 1.280 + if (node) { 1.281 + nsINodeInfo* nodeInfo = static_cast<nsINodeInfo *>(node); 1.282 + 1.283 + NS_ADDREF(*aNodeInfo = nodeInfo); 1.284 + 1.285 + return NS_OK; 1.286 + } 1.287 + 1.288 + nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aName); 1.289 + NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY); 1.290 + 1.291 + nsRefPtr<nsNodeInfo> newNodeInfo = 1.292 + new nsNodeInfo(nameAtom, aPrefix, aNamespaceID, aNodeType, nullptr, this); 1.293 + NS_ENSURE_TRUE(newNodeInfo, NS_ERROR_OUT_OF_MEMORY); 1.294 + 1.295 + PLHashEntry *he; 1.296 + he = PL_HashTableAdd(mNodeInfoHash, &newNodeInfo->mInner, newNodeInfo); 1.297 + NS_ENSURE_TRUE(he, NS_ERROR_FAILURE); 1.298 + 1.299 + ++mNonDocumentNodeInfos; 1.300 + if (mNonDocumentNodeInfos == 1) { 1.301 + NS_IF_ADDREF(mDocument); 1.302 + } 1.303 + 1.304 + newNodeInfo.forget(aNodeInfo); 1.305 + 1.306 + return NS_OK; 1.307 +} 1.308 + 1.309 + 1.310 +nsresult 1.311 +nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsIAtom *aPrefix, 1.312 + const nsAString& aNamespaceURI, 1.313 + uint16_t aNodeType, 1.314 + nsINodeInfo** aNodeInfo) 1.315 +{ 1.316 + int32_t nsid = kNameSpaceID_None; 1.317 + 1.318 + if (!aNamespaceURI.IsEmpty()) { 1.319 + nsresult rv = nsContentUtils::NameSpaceManager()-> 1.320 + RegisterNameSpace(aNamespaceURI, nsid); 1.321 + NS_ENSURE_SUCCESS(rv, rv); 1.322 + } 1.323 + 1.324 + return GetNodeInfo(aName, aPrefix, nsid, aNodeType, aNodeInfo); 1.325 +} 1.326 + 1.327 +already_AddRefed<nsINodeInfo> 1.328 +nsNodeInfoManager::GetTextNodeInfo() 1.329 +{ 1.330 + nsCOMPtr<nsINodeInfo> nodeInfo; 1.331 + 1.332 + if (!mTextNodeInfo) { 1.333 + nodeInfo = GetNodeInfo(nsGkAtoms::textTagName, nullptr, kNameSpaceID_None, 1.334 + nsIDOMNode::TEXT_NODE, nullptr); 1.335 + // Hold a weak ref; the nodeinfo will let us know when it goes away 1.336 + mTextNodeInfo = nodeInfo; 1.337 + } else { 1.338 + nodeInfo = mTextNodeInfo; 1.339 + } 1.340 + 1.341 + return nodeInfo.forget(); 1.342 +} 1.343 + 1.344 +already_AddRefed<nsINodeInfo> 1.345 +nsNodeInfoManager::GetCommentNodeInfo() 1.346 +{ 1.347 + nsCOMPtr<nsINodeInfo> nodeInfo; 1.348 + 1.349 + if (!mCommentNodeInfo) { 1.350 + nodeInfo = GetNodeInfo(nsGkAtoms::commentTagName, nullptr, 1.351 + kNameSpaceID_None, nsIDOMNode::COMMENT_NODE, 1.352 + nullptr); 1.353 + // Hold a weak ref; the nodeinfo will let us know when it goes away 1.354 + mCommentNodeInfo = nodeInfo; 1.355 + } 1.356 + else { 1.357 + nodeInfo = mCommentNodeInfo; 1.358 + } 1.359 + 1.360 + return nodeInfo.forget(); 1.361 +} 1.362 + 1.363 +already_AddRefed<nsINodeInfo> 1.364 +nsNodeInfoManager::GetDocumentNodeInfo() 1.365 +{ 1.366 + nsCOMPtr<nsINodeInfo> nodeInfo; 1.367 + 1.368 + if (!mDocumentNodeInfo) { 1.369 + NS_ASSERTION(mDocument, "Should have mDocument!"); 1.370 + nodeInfo = GetNodeInfo(nsGkAtoms::documentNodeName, nullptr, 1.371 + kNameSpaceID_None, nsIDOMNode::DOCUMENT_NODE, 1.372 + nullptr); 1.373 + // Hold a weak ref; the nodeinfo will let us know when it goes away 1.374 + mDocumentNodeInfo = nodeInfo; 1.375 + 1.376 + --mNonDocumentNodeInfos; 1.377 + if (!mNonDocumentNodeInfos) { 1.378 + mDocument->Release(); // Don't set mDocument to null! 1.379 + } 1.380 + } 1.381 + else { 1.382 + nodeInfo = mDocumentNodeInfo; 1.383 + } 1.384 + 1.385 + return nodeInfo.forget(); 1.386 +} 1.387 + 1.388 +void 1.389 +nsNodeInfoManager::SetDocumentPrincipal(nsIPrincipal *aPrincipal) 1.390 +{ 1.391 + mPrincipal = nullptr; 1.392 + if (!aPrincipal) { 1.393 + aPrincipal = mDefaultPrincipal; 1.394 + } 1.395 + 1.396 + NS_ASSERTION(aPrincipal, "Must have principal by this point!"); 1.397 + 1.398 + mPrincipal = aPrincipal; 1.399 +} 1.400 + 1.401 +void 1.402 +nsNodeInfoManager::RemoveNodeInfo(nsNodeInfo *aNodeInfo) 1.403 +{ 1.404 + NS_PRECONDITION(aNodeInfo, "Trying to remove null nodeinfo from manager!"); 1.405 + 1.406 + if (aNodeInfo == mDocumentNodeInfo) { 1.407 + mDocumentNodeInfo = nullptr; 1.408 + mDocument = nullptr; 1.409 + } else { 1.410 + if (--mNonDocumentNodeInfos == 0) { 1.411 + if (mDocument) { 1.412 + // Note, whoever calls this method should keep NodeInfoManager alive, 1.413 + // even if mDocument gets deleted. 1.414 + mDocument->Release(); 1.415 + } 1.416 + } 1.417 + // Drop weak reference if needed 1.418 + if (aNodeInfo == mTextNodeInfo) { 1.419 + mTextNodeInfo = nullptr; 1.420 + } 1.421 + else if (aNodeInfo == mCommentNodeInfo) { 1.422 + mCommentNodeInfo = nullptr; 1.423 + } 1.424 + } 1.425 + 1.426 +#ifdef DEBUG 1.427 + bool ret = 1.428 +#endif 1.429 + PL_HashTableRemove(mNodeInfoHash, &aNodeInfo->mInner); 1.430 + 1.431 + NS_POSTCONDITION(ret, "Can't find nsINodeInfo to remove!!!"); 1.432 +}