content/base/src/nsNodeInfoManager.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:db9071f9da12
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 "nsNodeInfo.h"
15 #include "nsCOMPtr.h"
16 #include "nsString.h"
17 #include "nsIAtom.h"
18 #include "nsIDocument.h"
19 #include "nsIPrincipal.h"
20 #include "nsIURI.h"
21 #include "nsContentUtils.h"
22 #include "nsReadableUtils.h"
23 #include "nsGkAtoms.h"
24 #include "nsComponentManagerUtils.h"
25 #include "nsLayoutStatics.h"
26 #include "nsBindingManager.h"
27 #include "nsHashKeys.h"
28 #include "nsCCUncollectableMarker.h"
29
30 using namespace mozilla;
31
32 #ifdef MOZ_LOGGING
33 // so we can get logging even in release builds
34 #define FORCE_PR_LOG 1
35 #endif
36 #include "prlog.h"
37
38 #ifdef PR_LOGGING
39 static PRLogModuleInfo* gNodeInfoManagerLeakPRLog;
40 #endif
41
42 PLHashNumber
43 nsNodeInfoManager::GetNodeInfoInnerHashValue(const void *key)
44 {
45 NS_ASSERTION(key, "Null key passed to nsNodeInfo::GetHashValue!");
46
47 const nsINodeInfo::nsNodeInfoInner *node =
48 reinterpret_cast<const nsINodeInfo::nsNodeInfoInner *>(key);
49
50 return node->mName ? node->mName->hash() : HashString(*(node->mNameString));
51 }
52
53
54 int
55 nsNodeInfoManager::NodeInfoInnerKeyCompare(const void *key1, const void *key2)
56 {
57 NS_ASSERTION(key1 && key2, "Null key passed to NodeInfoInnerKeyCompare!");
58
59 const nsINodeInfo::nsNodeInfoInner *node1 =
60 reinterpret_cast<const nsINodeInfo::nsNodeInfoInner *>(key1);
61 const nsINodeInfo::nsNodeInfoInner *node2 =
62 reinterpret_cast<const nsINodeInfo::nsNodeInfoInner *>(key2);
63
64 if (node1->mPrefix != node2->mPrefix ||
65 node1->mNamespaceID != node2->mNamespaceID ||
66 node1->mNodeType != node2->mNodeType ||
67 node1->mExtraName != node2->mExtraName) {
68 return 0;
69 }
70
71 if (node1->mName) {
72 if (node2->mName) {
73 return (node1->mName == node2->mName);
74 }
75 return (node1->mName->Equals(*(node2->mNameString)));
76 }
77 if (node2->mName) {
78 return (node2->mName->Equals(*(node1->mNameString)));
79 }
80 return (node1->mNameString->Equals(*(node2->mNameString)));
81 }
82
83
84 static void* PR_CALLBACK
85 AllocTable(void* pool, size_t size)
86 {
87 return malloc(size);
88 }
89
90 static void PR_CALLBACK
91 FreeTable(void* pool, void* item)
92 {
93 free(item);
94 }
95
96 static PLHashEntry* PR_CALLBACK
97 AllocEntry(void* pool, const void* key)
98 {
99 return (PLHashEntry*)malloc(sizeof(PLHashEntry));
100 }
101
102 static void PR_CALLBACK
103 FreeEntry(void* pool, PLHashEntry* he, unsigned flag)
104 {
105 if (flag == HT_FREE_ENTRY) {
106 free(he);
107 }
108 }
109
110 static PLHashAllocOps allocOps =
111 { AllocTable, FreeTable, AllocEntry, FreeEntry };
112
113 nsNodeInfoManager::nsNodeInfoManager()
114 : mDocument(nullptr),
115 mNonDocumentNodeInfos(0),
116 mTextNodeInfo(nullptr),
117 mCommentNodeInfo(nullptr),
118 mDocumentNodeInfo(nullptr)
119 {
120 nsLayoutStatics::AddRef();
121
122 #ifdef PR_LOGGING
123 if (!gNodeInfoManagerLeakPRLog)
124 gNodeInfoManagerLeakPRLog = PR_NewLogModule("NodeInfoManagerLeak");
125
126 if (gNodeInfoManagerLeakPRLog)
127 PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG,
128 ("NODEINFOMANAGER %p created", this));
129 #endif
130
131 mNodeInfoHash = PL_NewHashTable(32, GetNodeInfoInnerHashValue,
132 NodeInfoInnerKeyCompare,
133 PL_CompareValues, &allocOps, nullptr);
134 }
135
136
137 nsNodeInfoManager::~nsNodeInfoManager()
138 {
139 if (mNodeInfoHash)
140 PL_HashTableDestroy(mNodeInfoHash);
141
142 // Note: mPrincipal may be null here if we never got inited correctly
143 mPrincipal = nullptr;
144
145 mBindingManager = nullptr;
146
147 #ifdef PR_LOGGING
148 if (gNodeInfoManagerLeakPRLog)
149 PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG,
150 ("NODEINFOMANAGER %p destroyed", this));
151 #endif
152
153 nsLayoutStatics::Release();
154 }
155
156 NS_IMPL_CYCLE_COLLECTION_CLASS(nsNodeInfoManager)
157
158 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsNodeInfoManager)
159 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNodeInfoManager)
160 if (tmp->mDocument &&
161 nsCCUncollectableMarker::InGeneration(cb,
162 tmp->mDocument->GetMarkedCCGeneration())) {
163 return NS_SUCCESS_INTERRUPTED_TRAVERSE;
164 }
165 if (tmp->mNonDocumentNodeInfos) {
166 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDocument)
167 }
168 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBindingManager)
169 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
170
171 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsNodeInfoManager, AddRef)
172 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsNodeInfoManager, Release)
173
174 nsresult
175 nsNodeInfoManager::Init(nsIDocument *aDocument)
176 {
177 NS_ENSURE_TRUE(mNodeInfoHash, NS_ERROR_OUT_OF_MEMORY);
178
179 NS_PRECONDITION(!mPrincipal,
180 "Being inited when we already have a principal?");
181 nsresult rv;
182 mPrincipal = do_CreateInstance("@mozilla.org/nullprincipal;1", &rv);
183 NS_ENSURE_TRUE(mPrincipal, rv);
184
185 if (aDocument) {
186 mBindingManager = new nsBindingManager(aDocument);
187 }
188
189 mDefaultPrincipal = mPrincipal;
190
191 mDocument = aDocument;
192
193 #ifdef PR_LOGGING
194 if (gNodeInfoManagerLeakPRLog)
195 PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG,
196 ("NODEINFOMANAGER %p Init document=%p", this, aDocument));
197 #endif
198
199 return NS_OK;
200 }
201
202 // static
203 int
204 nsNodeInfoManager::DropNodeInfoDocument(PLHashEntry *he, int hashIndex, void *arg)
205 {
206 static_cast<nsINodeInfo*>(he->value)->mDocument = nullptr;
207 return HT_ENUMERATE_NEXT;
208 }
209
210 void
211 nsNodeInfoManager::DropDocumentReference()
212 {
213 if (mBindingManager) {
214 mBindingManager->DropDocumentReference();
215 }
216
217 // This is probably not needed anymore.
218 PL_HashTableEnumerateEntries(mNodeInfoHash, DropNodeInfoDocument, nullptr);
219
220 NS_ASSERTION(!mNonDocumentNodeInfos, "Shouldn't have non-document nodeinfos!");
221 mDocument = nullptr;
222 }
223
224
225 already_AddRefed<nsINodeInfo>
226 nsNodeInfoManager::GetNodeInfo(nsIAtom *aName, nsIAtom *aPrefix,
227 int32_t aNamespaceID, uint16_t aNodeType,
228 nsIAtom* aExtraName /* = nullptr */)
229 {
230 CheckValidNodeInfo(aNodeType, aName, aNamespaceID, aExtraName);
231
232 nsINodeInfo::nsNodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType,
233 aExtraName);
234
235 void *node = PL_HashTableLookup(mNodeInfoHash, &tmpKey);
236
237 if (node) {
238 nsCOMPtr<nsINodeInfo> nodeInfo = static_cast<nsINodeInfo*>(node);
239
240 return nodeInfo.forget();
241 }
242
243 nsRefPtr<nsNodeInfo> newNodeInfo =
244 new nsNodeInfo(aName, aPrefix, aNamespaceID, aNodeType, aExtraName, this);
245
246 DebugOnly<PLHashEntry*> he =
247 PL_HashTableAdd(mNodeInfoHash, &newNodeInfo->mInner, newNodeInfo);
248 MOZ_ASSERT(he, "PL_HashTableAdd() failed");
249
250 // Have to do the swap thing, because already_AddRefed<nsNodeInfo>
251 // doesn't cast to already_AddRefed<nsINodeInfo>
252 ++mNonDocumentNodeInfos;
253 if (mNonDocumentNodeInfos == 1) {
254 NS_IF_ADDREF(mDocument);
255 }
256
257 return newNodeInfo.forget();
258 }
259
260
261 nsresult
262 nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsIAtom *aPrefix,
263 int32_t aNamespaceID, uint16_t aNodeType,
264 nsINodeInfo** aNodeInfo)
265 {
266 #ifdef DEBUG
267 {
268 nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aName);
269 CheckValidNodeInfo(aNodeType, nameAtom, aNamespaceID, nullptr);
270 }
271 #endif
272
273 nsINodeInfo::nsNodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType);
274
275 void *node = PL_HashTableLookup(mNodeInfoHash, &tmpKey);
276
277 if (node) {
278 nsINodeInfo* nodeInfo = static_cast<nsINodeInfo *>(node);
279
280 NS_ADDREF(*aNodeInfo = nodeInfo);
281
282 return NS_OK;
283 }
284
285 nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aName);
286 NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY);
287
288 nsRefPtr<nsNodeInfo> newNodeInfo =
289 new nsNodeInfo(nameAtom, aPrefix, aNamespaceID, aNodeType, nullptr, this);
290 NS_ENSURE_TRUE(newNodeInfo, NS_ERROR_OUT_OF_MEMORY);
291
292 PLHashEntry *he;
293 he = PL_HashTableAdd(mNodeInfoHash, &newNodeInfo->mInner, newNodeInfo);
294 NS_ENSURE_TRUE(he, NS_ERROR_FAILURE);
295
296 ++mNonDocumentNodeInfos;
297 if (mNonDocumentNodeInfos == 1) {
298 NS_IF_ADDREF(mDocument);
299 }
300
301 newNodeInfo.forget(aNodeInfo);
302
303 return NS_OK;
304 }
305
306
307 nsresult
308 nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsIAtom *aPrefix,
309 const nsAString& aNamespaceURI,
310 uint16_t aNodeType,
311 nsINodeInfo** aNodeInfo)
312 {
313 int32_t nsid = kNameSpaceID_None;
314
315 if (!aNamespaceURI.IsEmpty()) {
316 nsresult rv = nsContentUtils::NameSpaceManager()->
317 RegisterNameSpace(aNamespaceURI, nsid);
318 NS_ENSURE_SUCCESS(rv, rv);
319 }
320
321 return GetNodeInfo(aName, aPrefix, nsid, aNodeType, aNodeInfo);
322 }
323
324 already_AddRefed<nsINodeInfo>
325 nsNodeInfoManager::GetTextNodeInfo()
326 {
327 nsCOMPtr<nsINodeInfo> nodeInfo;
328
329 if (!mTextNodeInfo) {
330 nodeInfo = GetNodeInfo(nsGkAtoms::textTagName, nullptr, kNameSpaceID_None,
331 nsIDOMNode::TEXT_NODE, nullptr);
332 // Hold a weak ref; the nodeinfo will let us know when it goes away
333 mTextNodeInfo = nodeInfo;
334 } else {
335 nodeInfo = mTextNodeInfo;
336 }
337
338 return nodeInfo.forget();
339 }
340
341 already_AddRefed<nsINodeInfo>
342 nsNodeInfoManager::GetCommentNodeInfo()
343 {
344 nsCOMPtr<nsINodeInfo> nodeInfo;
345
346 if (!mCommentNodeInfo) {
347 nodeInfo = GetNodeInfo(nsGkAtoms::commentTagName, nullptr,
348 kNameSpaceID_None, nsIDOMNode::COMMENT_NODE,
349 nullptr);
350 // Hold a weak ref; the nodeinfo will let us know when it goes away
351 mCommentNodeInfo = nodeInfo;
352 }
353 else {
354 nodeInfo = mCommentNodeInfo;
355 }
356
357 return nodeInfo.forget();
358 }
359
360 already_AddRefed<nsINodeInfo>
361 nsNodeInfoManager::GetDocumentNodeInfo()
362 {
363 nsCOMPtr<nsINodeInfo> nodeInfo;
364
365 if (!mDocumentNodeInfo) {
366 NS_ASSERTION(mDocument, "Should have mDocument!");
367 nodeInfo = GetNodeInfo(nsGkAtoms::documentNodeName, nullptr,
368 kNameSpaceID_None, nsIDOMNode::DOCUMENT_NODE,
369 nullptr);
370 // Hold a weak ref; the nodeinfo will let us know when it goes away
371 mDocumentNodeInfo = nodeInfo;
372
373 --mNonDocumentNodeInfos;
374 if (!mNonDocumentNodeInfos) {
375 mDocument->Release(); // Don't set mDocument to null!
376 }
377 }
378 else {
379 nodeInfo = mDocumentNodeInfo;
380 }
381
382 return nodeInfo.forget();
383 }
384
385 void
386 nsNodeInfoManager::SetDocumentPrincipal(nsIPrincipal *aPrincipal)
387 {
388 mPrincipal = nullptr;
389 if (!aPrincipal) {
390 aPrincipal = mDefaultPrincipal;
391 }
392
393 NS_ASSERTION(aPrincipal, "Must have principal by this point!");
394
395 mPrincipal = aPrincipal;
396 }
397
398 void
399 nsNodeInfoManager::RemoveNodeInfo(nsNodeInfo *aNodeInfo)
400 {
401 NS_PRECONDITION(aNodeInfo, "Trying to remove null nodeinfo from manager!");
402
403 if (aNodeInfo == mDocumentNodeInfo) {
404 mDocumentNodeInfo = nullptr;
405 mDocument = nullptr;
406 } else {
407 if (--mNonDocumentNodeInfos == 0) {
408 if (mDocument) {
409 // Note, whoever calls this method should keep NodeInfoManager alive,
410 // even if mDocument gets deleted.
411 mDocument->Release();
412 }
413 }
414 // Drop weak reference if needed
415 if (aNodeInfo == mTextNodeInfo) {
416 mTextNodeInfo = nullptr;
417 }
418 else if (aNodeInfo == mCommentNodeInfo) {
419 mCommentNodeInfo = nullptr;
420 }
421 }
422
423 #ifdef DEBUG
424 bool ret =
425 #endif
426 PL_HashTableRemove(mNodeInfoHash, &aNodeInfo->mInner);
427
428 NS_POSTCONDITION(ret, "Can't find nsINodeInfo to remove!!!");
429 }

mercurial