1 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is mozilla.org code.
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either of the GNU General Public License Version 2 or later (the "GPL"),
26 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /*
39 : * A class for handing out nodeinfos and ensuring sharing of them as needed.
40 : */
41 :
42 : #include "nsNodeInfoManager.h"
43 : #include "nsNodeInfo.h"
44 : #include "nsCOMPtr.h"
45 : #include "nsString.h"
46 : #include "nsIAtom.h"
47 : #include "nsIDocument.h"
48 : #include "nsIPrincipal.h"
49 : #include "nsIURI.h"
50 : #include "nsContentUtils.h"
51 : #include "nsReadableUtils.h"
52 : #include "nsGkAtoms.h"
53 : #include "nsComponentManagerUtils.h"
54 : #include "nsLayoutStatics.h"
55 : #include "nsBindingManager.h"
56 : #include "nsHashKeys.h"
57 : #include "nsCCUncollectableMarker.h"
58 :
59 : using namespace mozilla;
60 :
61 : #ifdef MOZ_LOGGING
62 : // so we can get logging even in release builds
63 : #define FORCE_PR_LOG 1
64 : #endif
65 : #include "prlog.h"
66 :
67 : #ifdef PR_LOGGING
68 : static PRLogModuleInfo* gNodeInfoManagerLeakPRLog;
69 : #endif
70 :
71 : PLHashNumber
72 67156 : nsNodeInfoManager::GetNodeInfoInnerHashValue(const void *key)
73 : {
74 67156 : NS_ASSERTION(key, "Null key passed to nsNodeInfo::GetHashValue!");
75 :
76 : const nsINodeInfo::nsNodeInfoInner *node =
77 67156 : reinterpret_cast<const nsINodeInfo::nsNodeInfoInner *>(key);
78 :
79 67156 : if (node->mName) {
80 : // Ideally, we'd return node->mName->hash() here. But that doesn't work at
81 : // the moment because node->mName->hash() is not the same as
82 : // HashString(*(node->mNameString)). See bug 732815.
83 66949 : return HashString(nsDependentAtomString(node->mName));
84 : }
85 207 : return HashString(*(node->mNameString));
86 : }
87 :
88 :
89 : PRIntn
90 44260 : nsNodeInfoManager::NodeInfoInnerKeyCompare(const void *key1, const void *key2)
91 : {
92 44260 : NS_ASSERTION(key1 && key2, "Null key passed to NodeInfoInnerKeyCompare!");
93 :
94 : const nsINodeInfo::nsNodeInfoInner *node1 =
95 44260 : reinterpret_cast<const nsINodeInfo::nsNodeInfoInner *>(key1);
96 : const nsINodeInfo::nsNodeInfoInner *node2 =
97 44260 : reinterpret_cast<const nsINodeInfo::nsNodeInfoInner *>(key2);
98 :
99 44260 : if (node1->mPrefix != node2->mPrefix ||
100 : node1->mNamespaceID != node2->mNamespaceID ||
101 : node1->mNodeType != node2->mNodeType ||
102 : node1->mExtraName != node2->mExtraName) {
103 1090 : return 0;
104 : }
105 :
106 43170 : if (node1->mName) {
107 43143 : if (node2->mName) {
108 43143 : return (node1->mName == node2->mName);
109 : }
110 0 : return (node1->mName->Equals(*(node2->mNameString)));
111 : }
112 27 : if (node2->mName) {
113 27 : return (node2->mName->Equals(*(node1->mNameString)));
114 : }
115 0 : return (node1->mNameString->Equals(*(node2->mNameString)));
116 : }
117 :
118 :
119 1273 : nsNodeInfoManager::nsNodeInfoManager()
120 : : mDocument(nsnull),
121 : mNonDocumentNodeInfos(0),
122 : mPrincipal(nsnull),
123 : mTextNodeInfo(nsnull),
124 : mCommentNodeInfo(nsnull),
125 : mDocumentNodeInfo(nsnull),
126 1273 : mBindingManager(nsnull)
127 : {
128 1273 : nsLayoutStatics::AddRef();
129 :
130 : #ifdef PR_LOGGING
131 1273 : if (!gNodeInfoManagerLeakPRLog)
132 249 : gNodeInfoManagerLeakPRLog = PR_NewLogModule("NodeInfoManagerLeak");
133 :
134 1273 : if (gNodeInfoManagerLeakPRLog)
135 1273 : PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG,
136 : ("NODEINFOMANAGER %p created", this));
137 : #endif
138 :
139 : mNodeInfoHash = PL_NewHashTable(32, GetNodeInfoInnerHashValue,
140 : NodeInfoInnerKeyCompare,
141 1273 : PL_CompareValues, nsnull, nsnull);
142 1273 : }
143 :
144 :
145 2542 : nsNodeInfoManager::~nsNodeInfoManager()
146 : {
147 1271 : if (mNodeInfoHash)
148 1271 : PL_HashTableDestroy(mNodeInfoHash);
149 :
150 : // Note: mPrincipal may be null here if we never got inited correctly
151 1271 : NS_IF_RELEASE(mPrincipal);
152 :
153 1271 : NS_IF_RELEASE(mBindingManager);
154 :
155 : #ifdef PR_LOGGING
156 1271 : if (gNodeInfoManagerLeakPRLog)
157 1271 : PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG,
158 : ("NODEINFOMANAGER %p destroyed", this));
159 : #endif
160 :
161 1271 : nsLayoutStatics::Release();
162 1271 : }
163 :
164 :
165 1464 : NS_IMPL_CYCLE_COLLECTION_CLASS(nsNodeInfoManager)
166 1271 : NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsNodeInfoManager, AddRef)
167 1271 : NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsNodeInfoManager, Release)
168 1271 : NS_IMPL_CYCLE_COLLECTION_UNLINK_NATIVE_0(nsNodeInfoManager)
169 1287 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsNodeInfoManager)
170 2574 : if (tmp->mDocument &&
171 : nsCCUncollectableMarker::InGeneration(cb,
172 1287 : tmp->mDocument->GetMarkedCCGeneration())) {
173 8 : return NS_SUCCESS_INTERRUPTED_TRAVERSE;
174 : }
175 1279 : if (tmp->mNonDocumentNodeInfos) {
176 1279 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDocument)
177 : }
178 1279 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mBindingManager)
179 1279 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
180 :
181 : nsresult
182 1273 : nsNodeInfoManager::Init(nsIDocument *aDocument)
183 : {
184 1273 : NS_ENSURE_TRUE(mNodeInfoHash, NS_ERROR_OUT_OF_MEMORY);
185 :
186 1273 : NS_PRECONDITION(!mPrincipal,
187 : "Being inited when we already have a principal?");
188 : nsresult rv = CallCreateInstance("@mozilla.org/nullprincipal;1",
189 1273 : &mPrincipal);
190 1273 : NS_ENSURE_TRUE(mPrincipal, rv);
191 :
192 1273 : if (aDocument) {
193 1273 : mBindingManager = new nsBindingManager(aDocument);
194 1273 : NS_ENSURE_TRUE(mBindingManager, NS_ERROR_OUT_OF_MEMORY);
195 :
196 1273 : NS_ADDREF(mBindingManager);
197 : }
198 :
199 1273 : mDefaultPrincipal = mPrincipal;
200 :
201 1273 : mDocument = aDocument;
202 :
203 : #ifdef PR_LOGGING
204 1273 : if (gNodeInfoManagerLeakPRLog)
205 1273 : PR_LOG(gNodeInfoManagerLeakPRLog, PR_LOG_DEBUG,
206 : ("NODEINFOMANAGER %p Init document=%p", this, aDocument));
207 : #endif
208 :
209 1273 : return NS_OK;
210 : }
211 :
212 : // static
213 : PRIntn
214 2371 : nsNodeInfoManager::DropNodeInfoDocument(PLHashEntry *he, PRIntn hashIndex, void *arg)
215 : {
216 2371 : static_cast<nsINodeInfo*>(he->value)->mDocument = nsnull;
217 2371 : return HT_ENUMERATE_NEXT;
218 : }
219 :
220 : void
221 1271 : nsNodeInfoManager::DropDocumentReference()
222 : {
223 1271 : if (mBindingManager) {
224 1271 : mBindingManager->DropDocumentReference();
225 : }
226 :
227 : // This is probably not needed anymore.
228 1271 : PL_HashTableEnumerateEntries(mNodeInfoHash, DropNodeInfoDocument, nsnull);
229 :
230 1271 : NS_ASSERTION(!mNonDocumentNodeInfos, "Shouldn't have non-document nodeinfos!");
231 1271 : mDocument = nsnull;
232 1271 : }
233 :
234 :
235 : already_AddRefed<nsINodeInfo>
236 42971 : nsNodeInfoManager::GetNodeInfo(nsIAtom *aName, nsIAtom *aPrefix,
237 : PRInt32 aNamespaceID, PRUint16 aNodeType,
238 : nsIAtom* aExtraName /* = nsnull */)
239 : {
240 42971 : CHECK_VALID_NODEINFO(aNodeType, aName, aNamespaceID, aExtraName);
241 :
242 : nsINodeInfo::nsNodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType,
243 42971 : aExtraName);
244 :
245 42971 : void *node = PL_HashTableLookup(mNodeInfoHash, &tmpKey);
246 :
247 42971 : if (node) {
248 31158 : nsINodeInfo* nodeInfo = static_cast<nsINodeInfo *>(node);
249 :
250 31158 : NS_ADDREF(nodeInfo);
251 :
252 31158 : return nodeInfo;
253 : }
254 :
255 : nsRefPtr<nsNodeInfo> newNodeInfo =
256 : nsNodeInfo::Create(aName, aPrefix, aNamespaceID, aNodeType, aExtraName,
257 23626 : this);
258 11813 : NS_ENSURE_TRUE(newNodeInfo, nsnull);
259 :
260 : PLHashEntry *he;
261 11813 : he = PL_HashTableAdd(mNodeInfoHash, &newNodeInfo->mInner, newNodeInfo);
262 11813 : NS_ENSURE_TRUE(he, nsnull);
263 :
264 : // Have to do the swap thing, because already_AddRefed<nsNodeInfo>
265 : // doesn't cast to already_AddRefed<nsINodeInfo>
266 11813 : ++mNonDocumentNodeInfos;
267 11813 : if (mNonDocumentNodeInfos == 1) {
268 2545 : NS_IF_ADDREF(mDocument);
269 : }
270 :
271 11813 : nsNodeInfo *nodeInfo = nsnull;
272 11813 : newNodeInfo.swap(nodeInfo);
273 :
274 11813 : return nodeInfo;
275 : }
276 :
277 :
278 : nsresult
279 207 : nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsIAtom *aPrefix,
280 : PRInt32 aNamespaceID, PRUint16 aNodeType,
281 : nsINodeInfo** aNodeInfo)
282 : {
283 : #ifdef DEBUG
284 : {
285 414 : nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aName);
286 207 : CHECK_VALID_NODEINFO(aNodeType, nameAtom, aNamespaceID, nsnull);
287 : }
288 : #endif
289 :
290 207 : nsINodeInfo::nsNodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType);
291 :
292 207 : void *node = PL_HashTableLookup(mNodeInfoHash, &tmpKey);
293 :
294 207 : if (node) {
295 27 : nsINodeInfo* nodeInfo = static_cast<nsINodeInfo *>(node);
296 :
297 27 : NS_ADDREF(*aNodeInfo = nodeInfo);
298 :
299 27 : return NS_OK;
300 : }
301 :
302 :
303 360 : nsCOMPtr<nsIAtom> nameAtom = do_GetAtom(aName);
304 180 : NS_ENSURE_TRUE(nameAtom, NS_ERROR_OUT_OF_MEMORY);
305 :
306 : nsRefPtr<nsNodeInfo> newNodeInfo =
307 : nsNodeInfo::Create(nameAtom, aPrefix, aNamespaceID, aNodeType, nsnull,
308 360 : this);
309 180 : NS_ENSURE_TRUE(newNodeInfo, NS_ERROR_OUT_OF_MEMORY);
310 :
311 : PLHashEntry *he;
312 180 : he = PL_HashTableAdd(mNodeInfoHash, &newNodeInfo->mInner, newNodeInfo);
313 180 : NS_ENSURE_TRUE(he, NS_ERROR_FAILURE);
314 :
315 180 : ++mNonDocumentNodeInfos;
316 180 : if (mNonDocumentNodeInfos == 1) {
317 1 : NS_IF_ADDREF(mDocument);
318 : }
319 :
320 180 : newNodeInfo.forget(aNodeInfo);
321 :
322 180 : return NS_OK;
323 : }
324 :
325 :
326 : nsresult
327 0 : nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsIAtom *aPrefix,
328 : const nsAString& aNamespaceURI,
329 : PRUint16 aNodeType,
330 : nsINodeInfo** aNodeInfo)
331 : {
332 0 : PRInt32 nsid = kNameSpaceID_None;
333 :
334 0 : if (!aNamespaceURI.IsEmpty()) {
335 0 : nsresult rv = nsContentUtils::NameSpaceManager()->
336 0 : RegisterNameSpace(aNamespaceURI, nsid);
337 0 : NS_ENSURE_SUCCESS(rv, rv);
338 : }
339 :
340 0 : return GetNodeInfo(aName, aPrefix, nsid, aNodeType, aNodeInfo);
341 : }
342 :
343 : already_AddRefed<nsINodeInfo>
344 72639 : nsNodeInfoManager::GetTextNodeInfo()
345 : {
346 72639 : if (!mTextNodeInfo) {
347 : mTextNodeInfo = GetNodeInfo(nsGkAtoms::textTagName, nsnull,
348 : kNameSpaceID_None,
349 1064 : nsIDOMNode::TEXT_NODE, nsnull).get();
350 : }
351 : else {
352 71575 : NS_ADDREF(mTextNodeInfo);
353 : }
354 :
355 72639 : return mTextNodeInfo;
356 : }
357 :
358 : already_AddRefed<nsINodeInfo>
359 1638 : nsNodeInfoManager::GetCommentNodeInfo()
360 : {
361 1638 : if (!mCommentNodeInfo) {
362 : mCommentNodeInfo = GetNodeInfo(nsGkAtoms::commentTagName, nsnull,
363 : kNameSpaceID_None,
364 186 : nsIDOMNode::COMMENT_NODE, nsnull).get();
365 : }
366 : else {
367 1452 : NS_ADDREF(mCommentNodeInfo);
368 : }
369 :
370 1638 : return mCommentNodeInfo;
371 : }
372 :
373 : already_AddRefed<nsINodeInfo>
374 1273 : nsNodeInfoManager::GetDocumentNodeInfo()
375 : {
376 1273 : if (!mDocumentNodeInfo) {
377 1273 : NS_ASSERTION(mDocument, "Should have mDocument!");
378 : mDocumentNodeInfo = GetNodeInfo(nsGkAtoms::documentNodeName, nsnull,
379 : kNameSpaceID_None,
380 1273 : nsIDOMNode::DOCUMENT_NODE, nsnull).get();
381 1273 : --mNonDocumentNodeInfos;
382 1273 : if (!mNonDocumentNodeInfos) {
383 1273 : mDocument->Release(); // Don't set mDocument to null!
384 : }
385 : }
386 : else {
387 0 : NS_ADDREF(mDocumentNodeInfo);
388 : }
389 :
390 1273 : return mDocumentNodeInfo;
391 : }
392 :
393 : void
394 3249 : nsNodeInfoManager::SetDocumentPrincipal(nsIPrincipal *aPrincipal)
395 : {
396 3249 : NS_RELEASE(mPrincipal);
397 3249 : if (!aPrincipal) {
398 469 : aPrincipal = mDefaultPrincipal;
399 : }
400 :
401 3249 : NS_ASSERTION(aPrincipal, "Must have principal by this point!");
402 :
403 3249 : NS_ADDREF(mPrincipal = aPrincipal);
404 3249 : }
405 :
406 : void
407 11985 : nsNodeInfoManager::RemoveNodeInfo(nsNodeInfo *aNodeInfo)
408 : {
409 11985 : NS_PRECONDITION(aNodeInfo, "Trying to remove null nodeinfo from manager!");
410 :
411 11985 : if (aNodeInfo == mDocumentNodeInfo) {
412 1271 : mDocumentNodeInfo = nsnull;
413 1271 : mDocument = nsnull;
414 : } else {
415 10714 : if (--mNonDocumentNodeInfos == 0) {
416 1271 : if (mDocument) {
417 : // Note, whoever calls this method should keep NodeInfoManager alive,
418 : // even if mDocument gets deleted.
419 1271 : mDocument->Release();
420 : }
421 : }
422 : // Drop weak reference if needed
423 10714 : if (aNodeInfo == mTextNodeInfo) {
424 1062 : mTextNodeInfo = nsnull;
425 : }
426 9652 : else if (aNodeInfo == mCommentNodeInfo) {
427 186 : mCommentNodeInfo = nsnull;
428 : }
429 : }
430 :
431 : #ifdef DEBUG
432 : bool ret =
433 : #endif
434 11985 : PL_HashTableRemove(mNodeInfoHash, &aNodeInfo->mInner);
435 :
436 11985 : NS_POSTCONDITION(ret, "Can't find nsINodeInfo to remove!!!");
437 16377 : }
|