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/. */
8 * A class for handing out nodeinfos and ensuring sharing of them as needed.
11 #include "nsNodeInfoManager.h"
13 #include "mozilla/BasePrincipal.h"
14 #include "mozilla/DebugOnly.h"
15 #include "mozilla/dom/Document.h"
16 #include "mozilla/dom/NodeInfo.h"
17 #include "mozilla/dom/NodeInfoInlines.h"
18 #include "mozilla/dom/DocGroup.h"
19 #include "mozilla/NullPrincipal.h"
20 #include "mozilla/StaticPrefs_dom.h"
24 #include "nsIPrincipal.h"
25 #include "nsContentUtils.h"
26 #include "nsReadableUtils.h"
27 #include "nsGkAtoms.h"
28 #include "nsComponentManagerUtils.h"
29 #include "nsLayoutStatics.h"
30 #include "nsHashKeys.h"
31 #include "nsCCUncollectableMarker.h"
32 #include "nsNameSpaceManager.h"
33 #include "nsWindowSizes.h"
35 using namespace mozilla
;
36 using mozilla::dom::NodeInfo
;
38 #include "mozilla/Logging.h"
40 static LazyLogModule
gNodeInfoManagerLeakPRLog("NodeInfoManagerLeak");
41 static const uint32_t kInitialNodeInfoHashSize
= 32;
43 nsNodeInfoManager::nsNodeInfoManager()
44 : mNodeInfoHash(kInitialNodeInfoHashSize
),
46 mNonDocumentNodeInfos(0),
47 mTextNodeInfo(nullptr),
48 mCommentNodeInfo(nullptr),
49 mDocumentNodeInfo(nullptr),
50 mRecentlyUsedNodeInfos(),
52 nsLayoutStatics::AddRef();
54 if (gNodeInfoManagerLeakPRLog
)
55 MOZ_LOG(gNodeInfoManagerLeakPRLog
, LogLevel::Debug
,
56 ("NODEINFOMANAGER %p created", this));
59 nsNodeInfoManager::~nsNodeInfoManager() {
60 // Note: mPrincipal may be null here if we never got inited correctly
65 if (gNodeInfoManagerLeakPRLog
)
66 MOZ_LOG(gNodeInfoManagerLeakPRLog
, LogLevel::Debug
,
67 ("NODEINFOMANAGER %p destroyed", this));
69 nsLayoutStatics::Release();
72 NS_IMPL_CYCLE_COLLECTION_CLASS(nsNodeInfoManager
)
74 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsNodeInfoManager
)
75 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNodeInfoManager
)
76 if (tmp
->mNonDocumentNodeInfos
) {
77 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDocument
)
79 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
81 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsNodeInfoManager
, AddRef
)
82 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsNodeInfoManager
, Release
)
84 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsNodeInfoManager
)
86 return NS_CYCLE_COLLECTION_PARTICIPANT(mozilla::dom::Document
)
87 ->CanSkip(tmp
->mDocument
, aRemovingAllowed
);
89 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
91 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsNodeInfoManager
)
93 return NS_CYCLE_COLLECTION_PARTICIPANT(mozilla::dom::Document
)
94 ->CanSkipInCC(tmp
->mDocument
);
96 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
98 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsNodeInfoManager
)
100 return NS_CYCLE_COLLECTION_PARTICIPANT(mozilla::dom::Document
)
101 ->CanSkipThis(tmp
->mDocument
);
103 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
105 nsresult
nsNodeInfoManager::Init(mozilla::dom::Document
* aDocument
) {
106 MOZ_ASSERT(!mPrincipal
, "Being inited when we already have a principal?");
108 mPrincipal
= NullPrincipal::CreateWithoutOriginAttributes();
110 mDefaultPrincipal
= mPrincipal
;
112 mDocument
= aDocument
;
114 if (gNodeInfoManagerLeakPRLog
)
115 MOZ_LOG(gNodeInfoManagerLeakPRLog
, LogLevel::Debug
,
116 ("NODEINFOMANAGER %p Init document=%p", this, aDocument
));
121 void nsNodeInfoManager::DropDocumentReference() {
122 // This is probably not needed anymore.
123 for (const auto& entry
: mNodeInfoHash
.Values()) {
124 entry
->mDocument
= nullptr;
127 NS_ASSERTION(!mNonDocumentNodeInfos
,
128 "Shouldn't have non-document nodeinfos!");
132 already_AddRefed
<mozilla::dom::NodeInfo
> nsNodeInfoManager::GetNodeInfo(
133 nsAtom
* aName
, nsAtom
* aPrefix
, int32_t aNamespaceID
, uint16_t aNodeType
,
134 nsAtom
* aExtraName
/* = nullptr */) {
135 CheckValidNodeInfo(aNodeType
, aName
, aNamespaceID
, aExtraName
);
137 NodeInfo::NodeInfoInner
tmpKey(aName
, aPrefix
, aNamespaceID
, aNodeType
,
140 auto p
= mRecentlyUsedNodeInfos
.Lookup(tmpKey
);
142 RefPtr
<NodeInfo
> nodeInfo
= p
.Data();
143 return nodeInfo
.forget();
146 // We don't use WithEntryHandle here as that would end up storing the
147 // temporary key instead of using `mInner`.
148 RefPtr
<NodeInfo
> nodeInfo
= mNodeInfoHash
.Get(&tmpKey
);
150 ++mNonDocumentNodeInfos
;
151 if (mNonDocumentNodeInfos
== 1) {
152 NS_IF_ADDREF(mDocument
);
156 new NodeInfo(aName
, aPrefix
, aNamespaceID
, aNodeType
, aExtraName
, this);
157 mNodeInfoHash
.InsertOrUpdate(&nodeInfo
->mInner
, nodeInfo
);
160 // Have to do the swap thing, because already_AddRefed<nsNodeInfo>
161 // doesn't cast to already_AddRefed<mozilla::dom::NodeInfo>
163 return nodeInfo
.forget();
166 nsresult
nsNodeInfoManager::GetNodeInfo(const nsAString
& aName
, nsAtom
* aPrefix
,
167 int32_t aNamespaceID
,
169 NodeInfo
** aNodeInfo
) {
170 // TODO(erahm): Combine this with the atom version.
173 RefPtr
<nsAtom
> nameAtom
= NS_Atomize(aName
);
174 CheckValidNodeInfo(aNodeType
, nameAtom
, aNamespaceID
, nullptr);
178 NodeInfo::NodeInfoInner
tmpKey(aName
, aPrefix
, aNamespaceID
, aNodeType
);
180 auto p
= mRecentlyUsedNodeInfos
.Lookup(tmpKey
);
182 RefPtr
<NodeInfo
> nodeInfo
= p
.Data();
183 nodeInfo
.forget(aNodeInfo
);
187 RefPtr
<NodeInfo
> nodeInfo
= mNodeInfoHash
.Get(&tmpKey
);
189 ++mNonDocumentNodeInfos
;
190 if (mNonDocumentNodeInfos
== 1) {
191 NS_IF_ADDREF(mDocument
);
194 RefPtr
<nsAtom
> nameAtom
= NS_Atomize(aName
);
196 new NodeInfo(nameAtom
, aPrefix
, aNamespaceID
, aNodeType
, nullptr, this);
197 mNodeInfoHash
.InsertOrUpdate(&nodeInfo
->mInner
, nodeInfo
);
201 nodeInfo
.forget(aNodeInfo
);
206 nsresult
nsNodeInfoManager::GetNodeInfo(const nsAString
& aName
, nsAtom
* aPrefix
,
207 const nsAString
& aNamespaceURI
,
209 NodeInfo
** aNodeInfo
) {
210 int32_t nsid
= kNameSpaceID_None
;
212 if (!aNamespaceURI
.IsEmpty()) {
213 nsresult rv
= nsContentUtils::NameSpaceManager()->RegisterNameSpace(
214 aNamespaceURI
, nsid
);
215 NS_ENSURE_SUCCESS(rv
, rv
);
218 return GetNodeInfo(aName
, aPrefix
, nsid
, aNodeType
, aNodeInfo
);
221 already_AddRefed
<NodeInfo
> nsNodeInfoManager::GetTextNodeInfo() {
222 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
224 if (!mTextNodeInfo
) {
225 nodeInfo
= GetNodeInfo(nsGkAtoms::textTagName
, nullptr, kNameSpaceID_None
,
226 nsINode::TEXT_NODE
, nullptr);
227 // Hold a weak ref; the nodeinfo will let us know when it goes away
228 mTextNodeInfo
= nodeInfo
;
230 nodeInfo
= mTextNodeInfo
;
233 return nodeInfo
.forget();
236 already_AddRefed
<NodeInfo
> nsNodeInfoManager::GetCommentNodeInfo() {
237 RefPtr
<NodeInfo
> nodeInfo
;
239 if (!mCommentNodeInfo
) {
240 nodeInfo
= GetNodeInfo(nsGkAtoms::commentTagName
, nullptr,
241 kNameSpaceID_None
, nsINode::COMMENT_NODE
, nullptr);
242 // Hold a weak ref; the nodeinfo will let us know when it goes away
243 mCommentNodeInfo
= nodeInfo
;
245 nodeInfo
= mCommentNodeInfo
;
248 return nodeInfo
.forget();
251 already_AddRefed
<NodeInfo
> nsNodeInfoManager::GetDocumentNodeInfo() {
252 RefPtr
<NodeInfo
> nodeInfo
;
254 if (!mDocumentNodeInfo
) {
255 NS_ASSERTION(mDocument
, "Should have mDocument!");
256 nodeInfo
= GetNodeInfo(nsGkAtoms::documentNodeName
, nullptr,
257 kNameSpaceID_None
, nsINode::DOCUMENT_NODE
, nullptr);
258 // Hold a weak ref; the nodeinfo will let us know when it goes away
259 mDocumentNodeInfo
= nodeInfo
;
261 --mNonDocumentNodeInfos
;
262 if (!mNonDocumentNodeInfos
) {
263 mDocument
->Release(); // Don't set mDocument to null!
266 nodeInfo
= mDocumentNodeInfo
;
269 return nodeInfo
.forget();
272 void* nsNodeInfoManager::Allocate(size_t aSize
) {
273 if (!mHasAllocated
) {
274 if (mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
276 mozilla::dom::DocGroup
* docGroup
= GetDocument()->GetDocGroupOrCreate();
278 MOZ_ASSERT(!GetDocument()->HasChildren());
279 mArena
= docGroup
->ArenaAllocator();
284 mozilla::dom::DocGroup
* docGroup
= GetDocument()->GetDocGroup();
285 MOZ_ASSERT(docGroup
);
286 MOZ_ASSERT(mArena
== docGroup
->ArenaAllocator());
290 mHasAllocated
= true;
294 if (!mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
295 MOZ_ASSERT(!mArena
, "mArena should not set if the pref is not on");
300 return mArena
->Allocate(aSize
);
302 return malloc(aSize
);
305 void nsNodeInfoManager::SetArenaAllocator(mozilla::dom::DOMArena
* aArena
) {
306 MOZ_DIAGNOSTIC_ASSERT_IF(mArena
, mArena
== aArena
);
307 MOZ_DIAGNOSTIC_ASSERT(!mHasAllocated
);
308 MOZ_DIAGNOSTIC_ASSERT(
309 mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup());
316 void nsNodeInfoManager::SetDocumentPrincipal(nsIPrincipal
* aPrincipal
) {
317 mPrincipal
= nullptr;
319 aPrincipal
= mDefaultPrincipal
;
322 NS_ASSERTION(aPrincipal
, "Must have principal by this point!");
323 MOZ_DIAGNOSTIC_ASSERT(!nsContentUtils::IsExpandedPrincipal(aPrincipal
),
324 "Documents shouldn't have an expanded principal");
326 mPrincipal
= aPrincipal
;
329 void nsNodeInfoManager::RemoveNodeInfo(NodeInfo
* aNodeInfo
) {
330 MOZ_ASSERT(aNodeInfo
, "Trying to remove null nodeinfo from manager!");
332 if (aNodeInfo
== mDocumentNodeInfo
) {
333 mDocumentNodeInfo
= nullptr;
336 if (--mNonDocumentNodeInfos
== 0) {
338 // Note, whoever calls this method should keep NodeInfoManager alive,
339 // even if mDocument gets deleted.
340 mDocument
->Release();
343 // Drop weak reference if needed
344 if (aNodeInfo
== mTextNodeInfo
) {
345 mTextNodeInfo
= nullptr;
346 } else if (aNodeInfo
== mCommentNodeInfo
) {
347 mCommentNodeInfo
= nullptr;
351 mRecentlyUsedNodeInfos
.Remove(aNodeInfo
->mInner
);
352 DebugOnly
<bool> ret
= mNodeInfoHash
.Remove(&aNodeInfo
->mInner
);
353 MOZ_ASSERT(ret
, "Can't find mozilla::dom::NodeInfo to remove!!!");
356 static bool IsSystemOrAddonOrAboutPrincipal(nsIPrincipal
* aPrincipal
) {
357 return aPrincipal
->IsSystemPrincipal() ||
358 BasePrincipal::Cast(aPrincipal
)->AddonPolicy() ||
359 // NOTE: about:blank and about:srcdoc inherit the principal of their
360 // parent, so aPrincipal->SchemeIs("about") returns false for them.
361 aPrincipal
->SchemeIs("about");
364 bool nsNodeInfoManager::InternalSVGEnabled() {
365 MOZ_ASSERT(!mSVGEnabled
, "Caller should use the cached mSVGEnabled!");
367 // If the svg.disabled pref. is true, convert all SVG nodes into
368 // disabled SVG nodes by swapping the namespace.
369 nsNameSpaceManager
* nsmgr
= nsNameSpaceManager::GetInstance();
370 nsCOMPtr
<nsILoadInfo
> loadInfo
;
371 bool SVGEnabled
= false;
373 if (nsmgr
&& !nsmgr
->mSVGDisabled
) {
376 nsCOMPtr
<nsIChannel
> channel
= mDocument
->GetChannel();
377 // We don't have a channel for SVGs constructed inside a SVG script
379 loadInfo
= channel
->LoadInfo();
383 // We allow SVG (regardless of the pref) if this is a system or add-on
384 // principal or about: page, or if this load was requested for a system or
385 // add-on principal or about: page (e.g. a remote image being served as part
386 // of system or add-on UI or about: page)
388 (SVGEnabled
|| IsSystemOrAddonOrAboutPrincipal(mPrincipal
) ||
390 (loadInfo
->GetExternalContentPolicyType() ==
391 ExtContentPolicy::TYPE_IMAGE
||
392 loadInfo
->GetExternalContentPolicyType() ==
393 ExtContentPolicy::TYPE_OTHER
) &&
394 (IsSystemOrAddonOrAboutPrincipal(loadInfo
->GetLoadingPrincipal()) ||
395 IsSystemOrAddonOrAboutPrincipal(loadInfo
->TriggeringPrincipal()))));
396 mSVGEnabled
= Some(conclusion
);
400 bool nsNodeInfoManager::InternalMathMLEnabled() {
401 MOZ_ASSERT(!mMathMLEnabled
, "Caller should use the cached mMathMLEnabled!");
403 // If the mathml.disabled pref. is true, convert all MathML nodes into
404 // disabled MathML nodes by swapping the namespace.
405 nsNameSpaceManager
* nsmgr
= nsNameSpaceManager::GetInstance();
407 ((nsmgr
&& !nsmgr
->mMathMLDisabled
) || mPrincipal
->IsSystemPrincipal());
408 mMathMLEnabled
= Some(conclusion
);
412 void nsNodeInfoManager::AddSizeOfIncludingThis(nsWindowSizes
& aSizes
) const {
413 aSizes
.mDOMOtherSize
+= aSizes
.mState
.mMallocSizeOf(this);
415 // Measurement of the following members may be added later if DMD finds it