|
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 } |