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(mozilla::dom::Document
* aDocument
,
44 nsIPrincipal
* aPrincipal
)
45 : mNodeInfoHash(kInitialNodeInfoHashSize
),
47 mNonDocumentNodeInfos(0),
48 mTextNodeInfo(nullptr),
49 mCommentNodeInfo(nullptr),
50 mDocumentNodeInfo(nullptr),
51 mRecentlyUsedNodeInfos(),
53 nsLayoutStatics::AddRef();
56 mPrincipal
= aPrincipal
;
58 mPrincipal
= NullPrincipal::CreateWithoutOriginAttributes();
60 mDefaultPrincipal
= mPrincipal
;
62 if (gNodeInfoManagerLeakPRLog
) {
63 MOZ_LOG(gNodeInfoManagerLeakPRLog
, LogLevel::Debug
,
64 ("NODEINFOMANAGER %p created, document=%p", this, aDocument
));
68 nsNodeInfoManager::~nsNodeInfoManager() {
69 // Note: mPrincipal may be null here if we never got inited correctly
74 if (gNodeInfoManagerLeakPRLog
)
75 MOZ_LOG(gNodeInfoManagerLeakPRLog
, LogLevel::Debug
,
76 ("NODEINFOMANAGER %p destroyed", this));
78 nsLayoutStatics::Release();
81 NS_IMPL_CYCLE_COLLECTION_CLASS(nsNodeInfoManager
)
83 NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsNodeInfoManager
)
84 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNodeInfoManager
)
85 if (tmp
->mNonDocumentNodeInfos
) {
86 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDocument
)
88 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
90 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsNodeInfoManager
)
92 return NS_CYCLE_COLLECTION_PARTICIPANT(mozilla::dom::Document
)
93 ->CanSkip(tmp
->mDocument
, aRemovingAllowed
);
95 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
97 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsNodeInfoManager
)
99 return NS_CYCLE_COLLECTION_PARTICIPANT(mozilla::dom::Document
)
100 ->CanSkipInCC(tmp
->mDocument
);
102 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
104 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsNodeInfoManager
)
105 if (tmp
->mDocument
) {
106 return NS_CYCLE_COLLECTION_PARTICIPANT(mozilla::dom::Document
)
107 ->CanSkipThis(tmp
->mDocument
);
109 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
111 void nsNodeInfoManager::DropDocumentReference() {
112 // This is probably not needed anymore.
113 for (const auto& entry
: mNodeInfoHash
.Values()) {
114 entry
->mDocument
= nullptr;
117 NS_ASSERTION(!mNonDocumentNodeInfos
,
118 "Shouldn't have non-document nodeinfos!");
122 already_AddRefed
<mozilla::dom::NodeInfo
> nsNodeInfoManager::GetNodeInfo(
123 nsAtom
* aName
, nsAtom
* aPrefix
, int32_t aNamespaceID
, uint16_t aNodeType
,
124 nsAtom
* aExtraName
/* = nullptr */) {
125 CheckValidNodeInfo(aNodeType
, aName
, aNamespaceID
, aExtraName
);
127 NodeInfo::NodeInfoInner
tmpKey(aName
, aPrefix
, aNamespaceID
, aNodeType
,
130 auto p
= mRecentlyUsedNodeInfos
.Lookup(tmpKey
);
132 RefPtr
<NodeInfo
> nodeInfo
= p
.Data();
133 return nodeInfo
.forget();
136 // We don't use WithEntryHandle here as that would end up storing the
137 // temporary key instead of using `mInner`.
138 RefPtr
<NodeInfo
> nodeInfo
= mNodeInfoHash
.Get(&tmpKey
);
140 ++mNonDocumentNodeInfos
;
141 if (mNonDocumentNodeInfos
== 1) {
142 NS_IF_ADDREF(mDocument
);
146 new NodeInfo(aName
, aPrefix
, aNamespaceID
, aNodeType
, aExtraName
, this);
147 mNodeInfoHash
.InsertOrUpdate(&nodeInfo
->mInner
, nodeInfo
);
150 // Have to do the swap thing, because already_AddRefed<nsNodeInfo>
151 // doesn't cast to already_AddRefed<mozilla::dom::NodeInfo>
153 return nodeInfo
.forget();
156 nsresult
nsNodeInfoManager::GetNodeInfo(const nsAString
& aName
, nsAtom
* aPrefix
,
157 int32_t aNamespaceID
,
159 NodeInfo
** aNodeInfo
) {
160 // TODO(erahm): Combine this with the atom version.
163 RefPtr
<nsAtom
> nameAtom
= NS_Atomize(aName
);
164 CheckValidNodeInfo(aNodeType
, nameAtom
, aNamespaceID
, nullptr);
168 NodeInfo::NodeInfoInner
tmpKey(aName
, aPrefix
, aNamespaceID
, aNodeType
);
170 auto p
= mRecentlyUsedNodeInfos
.Lookup(tmpKey
);
172 RefPtr
<NodeInfo
> nodeInfo
= p
.Data();
173 nodeInfo
.forget(aNodeInfo
);
177 RefPtr
<NodeInfo
> nodeInfo
= mNodeInfoHash
.Get(&tmpKey
);
179 ++mNonDocumentNodeInfos
;
180 if (mNonDocumentNodeInfos
== 1) {
181 NS_IF_ADDREF(mDocument
);
184 RefPtr
<nsAtom
> nameAtom
= NS_Atomize(aName
);
186 new NodeInfo(nameAtom
, aPrefix
, aNamespaceID
, aNodeType
, nullptr, this);
187 mNodeInfoHash
.InsertOrUpdate(&nodeInfo
->mInner
, nodeInfo
);
191 nodeInfo
.forget(aNodeInfo
);
196 nsresult
nsNodeInfoManager::GetNodeInfo(const nsAString
& aName
, nsAtom
* aPrefix
,
197 const nsAString
& aNamespaceURI
,
199 NodeInfo
** aNodeInfo
) {
200 int32_t nsid
= kNameSpaceID_None
;
202 if (!aNamespaceURI
.IsEmpty()) {
203 nsresult rv
= nsNameSpaceManager::GetInstance()->RegisterNameSpace(
204 aNamespaceURI
, nsid
);
205 NS_ENSURE_SUCCESS(rv
, rv
);
208 return GetNodeInfo(aName
, aPrefix
, nsid
, aNodeType
, aNodeInfo
);
211 already_AddRefed
<NodeInfo
> nsNodeInfoManager::GetTextNodeInfo() {
212 RefPtr
<mozilla::dom::NodeInfo
> nodeInfo
;
214 if (!mTextNodeInfo
) {
215 nodeInfo
= GetNodeInfo(nsGkAtoms::textTagName
, nullptr, kNameSpaceID_None
,
216 nsINode::TEXT_NODE
, nullptr);
217 // Hold a weak ref; the nodeinfo will let us know when it goes away
218 mTextNodeInfo
= nodeInfo
;
220 nodeInfo
= mTextNodeInfo
;
223 return nodeInfo
.forget();
226 already_AddRefed
<NodeInfo
> nsNodeInfoManager::GetCommentNodeInfo() {
227 RefPtr
<NodeInfo
> nodeInfo
;
229 if (!mCommentNodeInfo
) {
230 nodeInfo
= GetNodeInfo(nsGkAtoms::commentTagName
, nullptr,
231 kNameSpaceID_None
, nsINode::COMMENT_NODE
, nullptr);
232 // Hold a weak ref; the nodeinfo will let us know when it goes away
233 mCommentNodeInfo
= nodeInfo
;
235 nodeInfo
= mCommentNodeInfo
;
238 return nodeInfo
.forget();
241 already_AddRefed
<NodeInfo
> nsNodeInfoManager::GetDocumentNodeInfo() {
242 RefPtr
<NodeInfo
> nodeInfo
;
244 if (!mDocumentNodeInfo
) {
245 NS_ASSERTION(mDocument
, "Should have mDocument!");
246 nodeInfo
= GetNodeInfo(nsGkAtoms::documentNodeName
, nullptr,
247 kNameSpaceID_None
, nsINode::DOCUMENT_NODE
, nullptr);
248 // Hold a weak ref; the nodeinfo will let us know when it goes away
249 mDocumentNodeInfo
= nodeInfo
;
251 --mNonDocumentNodeInfos
;
252 if (!mNonDocumentNodeInfos
) {
253 mDocument
->Release(); // Don't set mDocument to null!
256 nodeInfo
= mDocumentNodeInfo
;
259 return nodeInfo
.forget();
262 void* nsNodeInfoManager::Allocate(size_t aSize
) {
263 if (!mHasAllocated
) {
264 if (mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
266 mozilla::dom::DocGroup
* docGroup
= GetDocument()->GetDocGroupOrCreate();
268 MOZ_ASSERT(!GetDocument()->HasChildren());
269 mArena
= docGroup
->ArenaAllocator();
274 mozilla::dom::DocGroup
* docGroup
= GetDocument()->GetDocGroup();
275 MOZ_ASSERT(docGroup
);
276 MOZ_ASSERT(mArena
== docGroup
->ArenaAllocator());
280 mHasAllocated
= true;
284 if (!mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
285 MOZ_ASSERT(!mArena
, "mArena should not set if the pref is not on");
290 return mArena
->Allocate(aSize
);
292 return malloc(aSize
);
295 void nsNodeInfoManager::SetArenaAllocator(mozilla::dom::DOMArena
* aArena
) {
296 MOZ_DIAGNOSTIC_ASSERT_IF(mArena
, mArena
== aArena
);
297 MOZ_DIAGNOSTIC_ASSERT(!mHasAllocated
);
298 MOZ_DIAGNOSTIC_ASSERT(
299 mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup());
306 void nsNodeInfoManager::SetDocumentPrincipal(nsIPrincipal
* aPrincipal
) {
307 mPrincipal
= nullptr;
309 aPrincipal
= mDefaultPrincipal
;
312 NS_ASSERTION(aPrincipal
, "Must have principal by this point!");
313 MOZ_DIAGNOSTIC_ASSERT(!nsContentUtils::IsExpandedPrincipal(aPrincipal
),
314 "Documents shouldn't have an expanded principal");
316 mPrincipal
= aPrincipal
;
319 void nsNodeInfoManager::RemoveNodeInfo(NodeInfo
* aNodeInfo
) {
320 MOZ_ASSERT(aNodeInfo
, "Trying to remove null nodeinfo from manager!");
322 if (aNodeInfo
== mDocumentNodeInfo
) {
323 mDocumentNodeInfo
= nullptr;
326 if (--mNonDocumentNodeInfos
== 0) {
328 // Note, whoever calls this method should keep NodeInfoManager alive,
329 // even if mDocument gets deleted.
330 mDocument
->Release();
333 // Drop weak reference if needed
334 if (aNodeInfo
== mTextNodeInfo
) {
335 mTextNodeInfo
= nullptr;
336 } else if (aNodeInfo
== mCommentNodeInfo
) {
337 mCommentNodeInfo
= nullptr;
341 mRecentlyUsedNodeInfos
.Remove(aNodeInfo
->mInner
);
342 DebugOnly
<bool> ret
= mNodeInfoHash
.Remove(&aNodeInfo
->mInner
);
343 MOZ_ASSERT(ret
, "Can't find mozilla::dom::NodeInfo to remove!!!");
346 static bool IsSystemOrAddonOrAboutPrincipal(nsIPrincipal
* aPrincipal
) {
347 return aPrincipal
->IsSystemPrincipal() ||
348 BasePrincipal::Cast(aPrincipal
)->AddonPolicy() ||
349 // NOTE: about:blank and about:srcdoc inherit the principal of their
350 // parent, so aPrincipal->SchemeIs("about") returns false for them.
351 aPrincipal
->SchemeIs("about");
354 bool nsNodeInfoManager::InternalSVGEnabled() {
355 MOZ_ASSERT(!mSVGEnabled
, "Caller should use the cached mSVGEnabled!");
357 // If the svg.disabled pref. is true, convert all SVG nodes into
358 // disabled SVG nodes by swapping the namespace.
359 nsNameSpaceManager
* nsmgr
= nsNameSpaceManager::GetInstance();
360 nsCOMPtr
<nsILoadInfo
> loadInfo
;
361 bool SVGEnabled
= false;
363 if (nsmgr
&& !nsmgr
->mSVGDisabled
) {
366 nsCOMPtr
<nsIChannel
> channel
= mDocument
->GetChannel();
367 // We don't have a channel for SVGs constructed inside a SVG script
369 loadInfo
= channel
->LoadInfo();
373 // We allow SVG (regardless of the pref) if this is a system or add-on
374 // principal or about: page, or if this load was requested for a system or
375 // add-on principal or about: page (e.g. a remote image being served as part
376 // of system or add-on UI or about: page)
378 (SVGEnabled
|| IsSystemOrAddonOrAboutPrincipal(mPrincipal
) ||
380 (loadInfo
->GetExternalContentPolicyType() ==
381 ExtContentPolicy::TYPE_IMAGE
||
382 loadInfo
->GetExternalContentPolicyType() ==
383 ExtContentPolicy::TYPE_OTHER
) &&
384 (IsSystemOrAddonOrAboutPrincipal(loadInfo
->GetLoadingPrincipal()) ||
385 IsSystemOrAddonOrAboutPrincipal(loadInfo
->TriggeringPrincipal()))));
386 mSVGEnabled
= Some(conclusion
);
390 bool nsNodeInfoManager::InternalMathMLEnabled() {
391 MOZ_ASSERT(!mMathMLEnabled
, "Caller should use the cached mMathMLEnabled!");
393 // If the mathml.disabled pref. is true, convert all MathML nodes into
394 // disabled MathML nodes by swapping the namespace.
395 nsNameSpaceManager
* nsmgr
= nsNameSpaceManager::GetInstance();
397 ((nsmgr
&& !nsmgr
->mMathMLDisabled
) || mPrincipal
->IsSystemPrincipal());
398 mMathMLEnabled
= Some(conclusion
);
402 void nsNodeInfoManager::AddSizeOfIncludingThis(nsWindowSizes
& aSizes
) const {
403 aSizes
.mDOMSizes
.mDOMOtherSize
+= aSizes
.mState
.mMallocSizeOf(this);
405 // Measurement of the following members may be added later if DMD finds it