/src/mozilla-central/dom/base/nsNodeInfoManager.cpp
Line | Count | Source (jump to first uncovered line) |
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/dom/NodeInfo.h" |
15 | | #include "mozilla/dom/NodeInfoInlines.h" |
16 | | #include "mozilla/NullPrincipal.h" |
17 | | #include "nsCOMPtr.h" |
18 | | #include "nsString.h" |
19 | | #include "nsAtom.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 "nsWindowSizes.h" |
34 | | |
35 | | using namespace mozilla; |
36 | | using mozilla::dom::NodeInfo; |
37 | | |
38 | | #include "mozilla/Logging.h" |
39 | | |
40 | | static LazyLogModule gNodeInfoManagerLeakPRLog("NodeInfoManagerLeak"); |
41 | | static const uint32_t kInitialNodeInfoHashSize = 32; |
42 | | |
43 | | nsNodeInfoManager::nsNodeInfoManager() |
44 | | : mNodeInfoHash(kInitialNodeInfoHashSize), |
45 | | mDocument(nullptr), |
46 | | mNonDocumentNodeInfos(0), |
47 | | mTextNodeInfo(nullptr), |
48 | | mCommentNodeInfo(nullptr), |
49 | | mDocumentNodeInfo(nullptr), |
50 | | mRecentlyUsedNodeInfos(), |
51 | | mSVGEnabled(eTriUnset), |
52 | | mMathMLEnabled(eTriUnset) |
53 | 0 | { |
54 | 0 | nsLayoutStatics::AddRef(); |
55 | 0 |
|
56 | 0 | if (gNodeInfoManagerLeakPRLog) |
57 | 0 | MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug, |
58 | 0 | ("NODEINFOMANAGER %p created", this)); |
59 | 0 | } |
60 | | |
61 | | |
62 | | nsNodeInfoManager::~nsNodeInfoManager() |
63 | 0 | { |
64 | 0 | // Note: mPrincipal may be null here if we never got inited correctly |
65 | 0 | mPrincipal = nullptr; |
66 | 0 |
|
67 | 0 | mBindingManager = nullptr; |
68 | 0 |
|
69 | 0 | if (gNodeInfoManagerLeakPRLog) |
70 | 0 | MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug, |
71 | 0 | ("NODEINFOMANAGER %p destroyed", this)); |
72 | 0 |
|
73 | 0 | nsLayoutStatics::Release(); |
74 | 0 | } |
75 | | |
76 | | NS_IMPL_CYCLE_COLLECTION_CLASS(nsNodeInfoManager) |
77 | | |
78 | | NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsNodeInfoManager) |
79 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNodeInfoManager) |
80 | 0 | if (tmp->mNonDocumentNodeInfos) { |
81 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDocument) |
82 | 0 | } |
83 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBindingManager) |
84 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
85 | | |
86 | | NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsNodeInfoManager, AddRef) |
87 | | NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsNodeInfoManager, Release) |
88 | | |
89 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsNodeInfoManager) |
90 | 0 | if (tmp->mDocument) { |
91 | 0 | return NS_CYCLE_COLLECTION_PARTICIPANT(nsDocument)->CanSkip(tmp->mDocument, aRemovingAllowed); |
92 | 0 | } |
93 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END |
94 | 0 |
|
95 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsNodeInfoManager) |
96 | 0 | if (tmp->mDocument) { |
97 | 0 | return NS_CYCLE_COLLECTION_PARTICIPANT(nsDocument)->CanSkipInCC(tmp->mDocument); |
98 | 0 | } |
99 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END |
100 | 0 |
|
101 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsNodeInfoManager) |
102 | 0 | if (tmp->mDocument) { |
103 | 0 | return NS_CYCLE_COLLECTION_PARTICIPANT(nsDocument)->CanSkipThis(tmp->mDocument); |
104 | 0 | } |
105 | 0 | NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END |
106 | 0 |
|
107 | 0 | nsresult |
108 | 0 | nsNodeInfoManager::Init(nsIDocument *aDocument) |
109 | 0 | { |
110 | 0 | MOZ_ASSERT(!mPrincipal, |
111 | 0 | "Being inited when we already have a principal?"); |
112 | 0 |
|
113 | 0 | mPrincipal = NullPrincipal::CreateWithoutOriginAttributes(); |
114 | 0 |
|
115 | 0 | if (aDocument) { |
116 | 0 | mBindingManager = new nsBindingManager(aDocument); |
117 | 0 | } |
118 | 0 |
|
119 | 0 | mDefaultPrincipal = mPrincipal; |
120 | 0 |
|
121 | 0 | mDocument = aDocument; |
122 | 0 |
|
123 | 0 | if (gNodeInfoManagerLeakPRLog) |
124 | 0 | MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug, |
125 | 0 | ("NODEINFOMANAGER %p Init document=%p", this, aDocument)); |
126 | 0 |
|
127 | 0 | return NS_OK; |
128 | 0 | } |
129 | | |
130 | | void |
131 | | nsNodeInfoManager::DropDocumentReference() |
132 | 0 | { |
133 | 0 | if (mBindingManager) { |
134 | 0 | mBindingManager->DropDocumentReference(); |
135 | 0 | } |
136 | 0 |
|
137 | 0 | // This is probably not needed anymore. |
138 | 0 | for (auto iter = mNodeInfoHash.Iter(); !iter.Done(); iter.Next()) { |
139 | 0 | iter.Data()->mDocument = nullptr; |
140 | 0 | } |
141 | 0 |
|
142 | 0 | NS_ASSERTION(!mNonDocumentNodeInfos, "Shouldn't have non-document nodeinfos!"); |
143 | 0 | mDocument = nullptr; |
144 | 0 | } |
145 | | |
146 | | |
147 | | already_AddRefed<mozilla::dom::NodeInfo> |
148 | | nsNodeInfoManager::GetNodeInfo(nsAtom *aName, nsAtom *aPrefix, |
149 | | int32_t aNamespaceID, uint16_t aNodeType, |
150 | | nsAtom* aExtraName /* = nullptr */) |
151 | 0 | { |
152 | 0 | CheckValidNodeInfo(aNodeType, aName, aNamespaceID, aExtraName); |
153 | 0 |
|
154 | 0 | NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType, |
155 | 0 | aExtraName); |
156 | 0 |
|
157 | 0 | auto p = mRecentlyUsedNodeInfos.Lookup(tmpKey); |
158 | 0 | if (p) { |
159 | 0 | RefPtr<NodeInfo> nodeInfo = p.Data(); |
160 | 0 | return nodeInfo.forget(); |
161 | 0 | } |
162 | 0 | |
163 | 0 | // We don't use LookupForAdd here as that would end up storing the temporary |
164 | 0 | // key instead of using `mInner`. |
165 | 0 | RefPtr<NodeInfo> nodeInfo = mNodeInfoHash.Get(&tmpKey); |
166 | 0 | if (!nodeInfo) { |
167 | 0 | ++mNonDocumentNodeInfos; |
168 | 0 | if (mNonDocumentNodeInfos == 1) { |
169 | 0 | NS_IF_ADDREF(mDocument); |
170 | 0 | } |
171 | 0 |
|
172 | 0 | nodeInfo = new NodeInfo(aName, aPrefix, aNamespaceID, aNodeType, aExtraName, this); |
173 | 0 | mNodeInfoHash.Put(&nodeInfo->mInner, nodeInfo); |
174 | 0 | } |
175 | 0 |
|
176 | 0 | // Have to do the swap thing, because already_AddRefed<nsNodeInfo> |
177 | 0 | // doesn't cast to already_AddRefed<mozilla::dom::NodeInfo> |
178 | 0 | p.Set(nodeInfo); |
179 | 0 | return nodeInfo.forget(); |
180 | 0 | } |
181 | | |
182 | | |
183 | | nsresult |
184 | | nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsAtom *aPrefix, |
185 | | int32_t aNamespaceID, uint16_t aNodeType, |
186 | | NodeInfo** aNodeInfo) |
187 | 0 | { |
188 | 0 | // TODO(erahm): Combine this with the atom version. |
189 | | #ifdef DEBUG |
190 | | { |
191 | | RefPtr<nsAtom> nameAtom = NS_Atomize(aName); |
192 | | CheckValidNodeInfo(aNodeType, nameAtom, aNamespaceID, nullptr); |
193 | | } |
194 | | #endif |
195 | |
|
196 | 0 | NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType); |
197 | 0 |
|
198 | 0 | auto p = mRecentlyUsedNodeInfos.Lookup(tmpKey); |
199 | 0 | if (p) { |
200 | 0 | RefPtr<NodeInfo> nodeInfo = p.Data(); |
201 | 0 | nodeInfo.forget(aNodeInfo); |
202 | 0 | return NS_OK; |
203 | 0 | } |
204 | 0 | |
205 | 0 | RefPtr<NodeInfo> nodeInfo = mNodeInfoHash.Get(&tmpKey); |
206 | 0 | if (!nodeInfo) { |
207 | 0 | ++mNonDocumentNodeInfos; |
208 | 0 | if (mNonDocumentNodeInfos == 1) { |
209 | 0 | NS_IF_ADDREF(mDocument); |
210 | 0 | } |
211 | 0 |
|
212 | 0 | RefPtr<nsAtom> nameAtom = NS_Atomize(aName); |
213 | 0 | nodeInfo = new NodeInfo(nameAtom, aPrefix, aNamespaceID, aNodeType, nullptr, this); |
214 | 0 | mNodeInfoHash.Put(&nodeInfo->mInner, nodeInfo); |
215 | 0 | } |
216 | 0 |
|
217 | 0 | p.Set(nodeInfo); |
218 | 0 | nodeInfo.forget(aNodeInfo); |
219 | 0 |
|
220 | 0 | return NS_OK; |
221 | 0 | } |
222 | | |
223 | | |
224 | | nsresult |
225 | | nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsAtom *aPrefix, |
226 | | const nsAString& aNamespaceURI, |
227 | | uint16_t aNodeType, |
228 | | NodeInfo** aNodeInfo) |
229 | 0 | { |
230 | 0 | int32_t nsid = kNameSpaceID_None; |
231 | 0 |
|
232 | 0 | if (!aNamespaceURI.IsEmpty()) { |
233 | 0 | nsresult rv = nsContentUtils::NameSpaceManager()-> |
234 | 0 | RegisterNameSpace(aNamespaceURI, nsid); |
235 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
236 | 0 | } |
237 | 0 |
|
238 | 0 | return GetNodeInfo(aName, aPrefix, nsid, aNodeType, aNodeInfo); |
239 | 0 | } |
240 | | |
241 | | already_AddRefed<NodeInfo> |
242 | | nsNodeInfoManager::GetTextNodeInfo() |
243 | 0 | { |
244 | 0 | RefPtr<mozilla::dom::NodeInfo> nodeInfo; |
245 | 0 |
|
246 | 0 | if (!mTextNodeInfo) { |
247 | 0 | nodeInfo = GetNodeInfo(nsGkAtoms::textTagName, nullptr, kNameSpaceID_None, |
248 | 0 | nsINode::TEXT_NODE, nullptr); |
249 | 0 | // Hold a weak ref; the nodeinfo will let us know when it goes away |
250 | 0 | mTextNodeInfo = nodeInfo; |
251 | 0 | } else { |
252 | 0 | nodeInfo = mTextNodeInfo; |
253 | 0 | } |
254 | 0 |
|
255 | 0 | return nodeInfo.forget(); |
256 | 0 | } |
257 | | |
258 | | already_AddRefed<NodeInfo> |
259 | | nsNodeInfoManager::GetCommentNodeInfo() |
260 | 0 | { |
261 | 0 | RefPtr<NodeInfo> nodeInfo; |
262 | 0 |
|
263 | 0 | if (!mCommentNodeInfo) { |
264 | 0 | nodeInfo = GetNodeInfo(nsGkAtoms::commentTagName, nullptr, |
265 | 0 | kNameSpaceID_None, nsINode::COMMENT_NODE, |
266 | 0 | nullptr); |
267 | 0 | // Hold a weak ref; the nodeinfo will let us know when it goes away |
268 | 0 | mCommentNodeInfo = nodeInfo; |
269 | 0 | } |
270 | 0 | else { |
271 | 0 | nodeInfo = mCommentNodeInfo; |
272 | 0 | } |
273 | 0 |
|
274 | 0 | return nodeInfo.forget(); |
275 | 0 | } |
276 | | |
277 | | already_AddRefed<NodeInfo> |
278 | | nsNodeInfoManager::GetDocumentNodeInfo() |
279 | 0 | { |
280 | 0 | RefPtr<NodeInfo> nodeInfo; |
281 | 0 |
|
282 | 0 | if (!mDocumentNodeInfo) { |
283 | 0 | NS_ASSERTION(mDocument, "Should have mDocument!"); |
284 | 0 | nodeInfo = GetNodeInfo(nsGkAtoms::documentNodeName, nullptr, |
285 | 0 | kNameSpaceID_None, nsINode::DOCUMENT_NODE, |
286 | 0 | nullptr); |
287 | 0 | // Hold a weak ref; the nodeinfo will let us know when it goes away |
288 | 0 | mDocumentNodeInfo = nodeInfo; |
289 | 0 |
|
290 | 0 | --mNonDocumentNodeInfos; |
291 | 0 | if (!mNonDocumentNodeInfos) { |
292 | 0 | mDocument->Release(); // Don't set mDocument to null! |
293 | 0 | } |
294 | 0 | } |
295 | 0 | else { |
296 | 0 | nodeInfo = mDocumentNodeInfo; |
297 | 0 | } |
298 | 0 |
|
299 | 0 | return nodeInfo.forget(); |
300 | 0 | } |
301 | | |
302 | | void |
303 | | nsNodeInfoManager::SetDocumentPrincipal(nsIPrincipal *aPrincipal) |
304 | 0 | { |
305 | 0 | mPrincipal = nullptr; |
306 | 0 | if (!aPrincipal) { |
307 | 0 | aPrincipal = mDefaultPrincipal; |
308 | 0 | } |
309 | 0 |
|
310 | 0 | NS_ASSERTION(aPrincipal, "Must have principal by this point!"); |
311 | 0 | MOZ_DIAGNOSTIC_ASSERT(!nsContentUtils::IsExpandedPrincipal(aPrincipal), |
312 | 0 | "Documents shouldn't have an expanded principal"); |
313 | 0 |
|
314 | 0 | mPrincipal = aPrincipal; |
315 | 0 | } |
316 | | |
317 | | void |
318 | | nsNodeInfoManager::RemoveNodeInfo(NodeInfo *aNodeInfo) |
319 | 0 | { |
320 | 0 | MOZ_ASSERT(aNodeInfo, "Trying to remove null nodeinfo from manager!"); |
321 | 0 |
|
322 | 0 | if (aNodeInfo == mDocumentNodeInfo) { |
323 | 0 | mDocumentNodeInfo = nullptr; |
324 | 0 | mDocument = nullptr; |
325 | 0 | } else { |
326 | 0 | if (--mNonDocumentNodeInfos == 0) { |
327 | 0 | if (mDocument) { |
328 | 0 | // Note, whoever calls this method should keep NodeInfoManager alive, |
329 | 0 | // even if mDocument gets deleted. |
330 | 0 | mDocument->Release(); |
331 | 0 | } |
332 | 0 | } |
333 | 0 | // Drop weak reference if needed |
334 | 0 | if (aNodeInfo == mTextNodeInfo) { |
335 | 0 | mTextNodeInfo = nullptr; |
336 | 0 | } |
337 | 0 | else if (aNodeInfo == mCommentNodeInfo) { |
338 | 0 | mCommentNodeInfo = nullptr; |
339 | 0 | } |
340 | 0 | } |
341 | 0 |
|
342 | 0 | mRecentlyUsedNodeInfos.Remove(aNodeInfo->mInner); |
343 | 0 | DebugOnly<bool> ret = mNodeInfoHash.Remove(&aNodeInfo->mInner); |
344 | 0 | MOZ_ASSERT(ret, "Can't find mozilla::dom::NodeInfo to remove!!!"); |
345 | 0 | } |
346 | | |
347 | | bool |
348 | | nsNodeInfoManager::InternalSVGEnabled() |
349 | 0 | { |
350 | 0 | // If the svg.disabled pref. is true, convert all SVG nodes into |
351 | 0 | // disabled SVG nodes by swapping the namespace. |
352 | 0 | nsNameSpaceManager* nsmgr = nsNameSpaceManager::GetInstance(); |
353 | 0 | nsCOMPtr<nsILoadInfo> loadInfo; |
354 | 0 | bool SVGEnabled = false; |
355 | 0 |
|
356 | 0 | if (nsmgr && !nsmgr->mSVGDisabled) { |
357 | 0 | SVGEnabled = true; |
358 | 0 | } else { |
359 | 0 | nsCOMPtr<nsIChannel> channel = mDocument->GetChannel(); |
360 | 0 | // We don't have a channel for SVGs constructed inside a SVG script |
361 | 0 | if (channel) { |
362 | 0 | loadInfo = channel->GetLoadInfo(); |
363 | 0 | } |
364 | 0 | } |
365 | 0 | bool conclusion = |
366 | 0 | (SVGEnabled || nsContentUtils::IsSystemPrincipal(mPrincipal) || |
367 | 0 | (loadInfo && |
368 | 0 | (loadInfo->GetExternalContentPolicyType() == |
369 | 0 | nsIContentPolicy::TYPE_IMAGE || |
370 | 0 | loadInfo->GetExternalContentPolicyType() == |
371 | 0 | nsIContentPolicy::TYPE_OTHER) && |
372 | 0 | (nsContentUtils::IsSystemPrincipal(loadInfo->LoadingPrincipal()) || |
373 | 0 | nsContentUtils::IsSystemPrincipal(loadInfo->TriggeringPrincipal())))); |
374 | 0 | mSVGEnabled = conclusion ? eTriTrue : eTriFalse; |
375 | 0 | return conclusion; |
376 | 0 | } |
377 | | |
378 | | bool |
379 | | nsNodeInfoManager::InternalMathMLEnabled() |
380 | 0 | { |
381 | 0 | // If the mathml.disabled pref. is true, convert all MathML nodes into |
382 | 0 | // disabled MathML nodes by swapping the namespace. |
383 | 0 | nsNameSpaceManager* nsmgr = nsNameSpaceManager::GetInstance(); |
384 | 0 | bool conclusion = ((nsmgr && !nsmgr->mMathMLDisabled) || |
385 | 0 | nsContentUtils::IsSystemPrincipal(mPrincipal)); |
386 | 0 | mMathMLEnabled = conclusion ? eTriTrue : eTriFalse; |
387 | 0 | return conclusion; |
388 | 0 | } |
389 | | |
390 | | void |
391 | | nsNodeInfoManager::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const |
392 | 0 | { |
393 | 0 | aSizes.mDOMOtherSize += aSizes.mState.mMallocSizeOf(this); |
394 | 0 |
|
395 | 0 | if (mBindingManager) { |
396 | 0 | aSizes.mBindingsSize += |
397 | 0 | mBindingManager->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf); |
398 | 0 | } |
399 | 0 |
|
400 | 0 | // Measurement of the following members may be added later if DMD finds it |
401 | 0 | // is worthwhile: |
402 | 0 | // - mNodeInfoHash |
403 | 0 | } |