summaryrefslogtreecommitdiffstats
path: root/dom/base/nsNodeInfoManager.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dom/base/nsNodeInfoManager.cpp418
1 files changed, 418 insertions, 0 deletions
diff --git a/dom/base/nsNodeInfoManager.cpp b/dom/base/nsNodeInfoManager.cpp
new file mode 100644
index 0000000000..9c7d713a99
--- /dev/null
+++ b/dom/base/nsNodeInfoManager.cpp
@@ -0,0 +1,418 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ * A class for handing out nodeinfos and ensuring sharing of them as needed.
+ */
+
+#include "nsNodeInfoManager.h"
+
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/NodeInfo.h"
+#include "mozilla/dom/NodeInfoInlines.h"
+#include "mozilla/dom/DocGroup.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsAtom.h"
+#include "nsIPrincipal.h"
+#include "nsContentUtils.h"
+#include "nsReadableUtils.h"
+#include "nsGkAtoms.h"
+#include "nsComponentManagerUtils.h"
+#include "nsLayoutStatics.h"
+#include "nsHashKeys.h"
+#include "nsCCUncollectableMarker.h"
+#include "nsNameSpaceManager.h"
+#include "nsWindowSizes.h"
+
+using namespace mozilla;
+using mozilla::dom::NodeInfo;
+
+#include "mozilla/Logging.h"
+
+static LazyLogModule gNodeInfoManagerLeakPRLog("NodeInfoManagerLeak");
+static const uint32_t kInitialNodeInfoHashSize = 32;
+
+nsNodeInfoManager::nsNodeInfoManager()
+ : mNodeInfoHash(kInitialNodeInfoHashSize),
+ mDocument(nullptr),
+ mNonDocumentNodeInfos(0),
+ mTextNodeInfo(nullptr),
+ mCommentNodeInfo(nullptr),
+ mDocumentNodeInfo(nullptr),
+ mRecentlyUsedNodeInfos(),
+ mArena(nullptr) {
+ nsLayoutStatics::AddRef();
+
+ if (gNodeInfoManagerLeakPRLog)
+ MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug,
+ ("NODEINFOMANAGER %p created", this));
+}
+
+nsNodeInfoManager::~nsNodeInfoManager() {
+ // Note: mPrincipal may be null here if we never got inited correctly
+ mPrincipal = nullptr;
+
+ mArena = nullptr;
+
+ if (gNodeInfoManagerLeakPRLog)
+ MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug,
+ ("NODEINFOMANAGER %p destroyed", this));
+
+ nsLayoutStatics::Release();
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(nsNodeInfoManager)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsNodeInfoManager)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNodeInfoManager)
+ if (tmp->mNonDocumentNodeInfos) {
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDocument)
+ }
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsNodeInfoManager, AddRef)
+NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsNodeInfoManager, Release)
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsNodeInfoManager)
+ if (tmp->mDocument) {
+ return NS_CYCLE_COLLECTION_PARTICIPANT(mozilla::dom::Document)
+ ->CanSkip(tmp->mDocument, aRemovingAllowed);
+ }
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsNodeInfoManager)
+ if (tmp->mDocument) {
+ return NS_CYCLE_COLLECTION_PARTICIPANT(mozilla::dom::Document)
+ ->CanSkipInCC(tmp->mDocument);
+ }
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
+
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsNodeInfoManager)
+ if (tmp->mDocument) {
+ return NS_CYCLE_COLLECTION_PARTICIPANT(mozilla::dom::Document)
+ ->CanSkipThis(tmp->mDocument);
+ }
+NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
+
+nsresult nsNodeInfoManager::Init(mozilla::dom::Document* aDocument) {
+ MOZ_ASSERT(!mPrincipal, "Being inited when we already have a principal?");
+
+ mPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
+
+ mDefaultPrincipal = mPrincipal;
+
+ mDocument = aDocument;
+
+ if (gNodeInfoManagerLeakPRLog)
+ MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug,
+ ("NODEINFOMANAGER %p Init document=%p", this, aDocument));
+
+ return NS_OK;
+}
+
+void nsNodeInfoManager::DropDocumentReference() {
+ // This is probably not needed anymore.
+ for (auto iter = mNodeInfoHash.Iter(); !iter.Done(); iter.Next()) {
+ iter.Data()->mDocument = nullptr;
+ }
+
+ NS_ASSERTION(!mNonDocumentNodeInfos,
+ "Shouldn't have non-document nodeinfos!");
+ mDocument = nullptr;
+}
+
+already_AddRefed<mozilla::dom::NodeInfo> nsNodeInfoManager::GetNodeInfo(
+ nsAtom* aName, nsAtom* aPrefix, int32_t aNamespaceID, uint16_t aNodeType,
+ nsAtom* aExtraName /* = nullptr */) {
+ CheckValidNodeInfo(aNodeType, aName, aNamespaceID, aExtraName);
+
+ NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType,
+ aExtraName);
+
+ auto p = mRecentlyUsedNodeInfos.Lookup(tmpKey);
+ if (p) {
+ RefPtr<NodeInfo> nodeInfo = p.Data();
+ return nodeInfo.forget();
+ }
+
+ // We don't use LookupForAdd here as that would end up storing the temporary
+ // key instead of using `mInner`.
+ RefPtr<NodeInfo> nodeInfo = mNodeInfoHash.Get(&tmpKey);
+ if (!nodeInfo) {
+ ++mNonDocumentNodeInfos;
+ if (mNonDocumentNodeInfos == 1) {
+ NS_IF_ADDREF(mDocument);
+ }
+
+ nodeInfo =
+ new NodeInfo(aName, aPrefix, aNamespaceID, aNodeType, aExtraName, this);
+ mNodeInfoHash.Put(&nodeInfo->mInner, nodeInfo);
+ }
+
+ // Have to do the swap thing, because already_AddRefed<nsNodeInfo>
+ // doesn't cast to already_AddRefed<mozilla::dom::NodeInfo>
+ p.Set(nodeInfo);
+ return nodeInfo.forget();
+}
+
+nsresult nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsAtom* aPrefix,
+ int32_t aNamespaceID,
+ uint16_t aNodeType,
+ NodeInfo** aNodeInfo) {
+ // TODO(erahm): Combine this with the atom version.
+#ifdef DEBUG
+ {
+ RefPtr<nsAtom> nameAtom = NS_Atomize(aName);
+ CheckValidNodeInfo(aNodeType, nameAtom, aNamespaceID, nullptr);
+ }
+#endif
+
+ NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType);
+
+ auto p = mRecentlyUsedNodeInfos.Lookup(tmpKey);
+ if (p) {
+ RefPtr<NodeInfo> nodeInfo = p.Data();
+ nodeInfo.forget(aNodeInfo);
+ return NS_OK;
+ }
+
+ RefPtr<NodeInfo> nodeInfo = mNodeInfoHash.Get(&tmpKey);
+ if (!nodeInfo) {
+ ++mNonDocumentNodeInfos;
+ if (mNonDocumentNodeInfos == 1) {
+ NS_IF_ADDREF(mDocument);
+ }
+
+ RefPtr<nsAtom> nameAtom = NS_Atomize(aName);
+ nodeInfo =
+ new NodeInfo(nameAtom, aPrefix, aNamespaceID, aNodeType, nullptr, this);
+ mNodeInfoHash.Put(&nodeInfo->mInner, nodeInfo);
+ }
+
+ p.Set(nodeInfo);
+ nodeInfo.forget(aNodeInfo);
+
+ return NS_OK;
+}
+
+nsresult nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsAtom* aPrefix,
+ const nsAString& aNamespaceURI,
+ uint16_t aNodeType,
+ NodeInfo** aNodeInfo) {
+ int32_t nsid = kNameSpaceID_None;
+
+ if (!aNamespaceURI.IsEmpty()) {
+ nsresult rv = nsContentUtils::NameSpaceManager()->RegisterNameSpace(
+ aNamespaceURI, nsid);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+ return GetNodeInfo(aName, aPrefix, nsid, aNodeType, aNodeInfo);
+}
+
+already_AddRefed<NodeInfo> nsNodeInfoManager::GetTextNodeInfo() {
+ RefPtr<mozilla::dom::NodeInfo> nodeInfo;
+
+ if (!mTextNodeInfo) {
+ nodeInfo = GetNodeInfo(nsGkAtoms::textTagName, nullptr, kNameSpaceID_None,
+ nsINode::TEXT_NODE, nullptr);
+ // Hold a weak ref; the nodeinfo will let us know when it goes away
+ mTextNodeInfo = nodeInfo;
+ } else {
+ nodeInfo = mTextNodeInfo;
+ }
+
+ return nodeInfo.forget();
+}
+
+already_AddRefed<NodeInfo> nsNodeInfoManager::GetCommentNodeInfo() {
+ RefPtr<NodeInfo> nodeInfo;
+
+ if (!mCommentNodeInfo) {
+ nodeInfo = GetNodeInfo(nsGkAtoms::commentTagName, nullptr,
+ kNameSpaceID_None, nsINode::COMMENT_NODE, nullptr);
+ // Hold a weak ref; the nodeinfo will let us know when it goes away
+ mCommentNodeInfo = nodeInfo;
+ } else {
+ nodeInfo = mCommentNodeInfo;
+ }
+
+ return nodeInfo.forget();
+}
+
+already_AddRefed<NodeInfo> nsNodeInfoManager::GetDocumentNodeInfo() {
+ RefPtr<NodeInfo> nodeInfo;
+
+ if (!mDocumentNodeInfo) {
+ NS_ASSERTION(mDocument, "Should have mDocument!");
+ nodeInfo = GetNodeInfo(nsGkAtoms::documentNodeName, nullptr,
+ kNameSpaceID_None, nsINode::DOCUMENT_NODE, nullptr);
+ // Hold a weak ref; the nodeinfo will let us know when it goes away
+ mDocumentNodeInfo = nodeInfo;
+
+ --mNonDocumentNodeInfos;
+ if (!mNonDocumentNodeInfos) {
+ mDocument->Release(); // Don't set mDocument to null!
+ }
+ } else {
+ nodeInfo = mDocumentNodeInfo;
+ }
+
+ return nodeInfo.forget();
+}
+
+void* nsNodeInfoManager::Allocate(size_t aSize) {
+ if (!mHasAllocated) {
+ if (mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
+ if (!mArena) {
+ mozilla::dom::DocGroup* docGroup = GetDocument()->GetDocGroupOrCreate();
+ if (docGroup) {
+ MOZ_ASSERT(!GetDocument()->HasChildren());
+ mArena = docGroup->ArenaAllocator();
+ }
+ }
+#ifdef DEBUG
+ else {
+ mozilla::dom::DocGroup* docGroup = GetDocument()->GetDocGroup();
+ MOZ_ASSERT(docGroup);
+ MOZ_ASSERT(mArena == docGroup->ArenaAllocator());
+ }
+#endif
+ }
+ mHasAllocated = true;
+ }
+
+#ifdef DEBUG
+ if (!mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup()) {
+ MOZ_ASSERT(!mArena, "mArena should not set if the pref is not on");
+ };
+#endif
+
+ if (mArena) {
+ return mArena->Allocate(aSize);
+ }
+ return malloc(aSize);
+}
+
+void nsNodeInfoManager::SetArenaAllocator(mozilla::dom::DOMArena* aArena) {
+ MOZ_DIAGNOSTIC_ASSERT_IF(mArena, mArena == aArena);
+ MOZ_DIAGNOSTIC_ASSERT(!mHasAllocated);
+ MOZ_DIAGNOSTIC_ASSERT(
+ mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup());
+
+ if (!mArena) {
+ mArena = aArena;
+ }
+}
+
+void nsNodeInfoManager::SetDocumentPrincipal(nsIPrincipal* aPrincipal) {
+ mPrincipal = nullptr;
+ if (!aPrincipal) {
+ aPrincipal = mDefaultPrincipal;
+ }
+
+ NS_ASSERTION(aPrincipal, "Must have principal by this point!");
+ MOZ_DIAGNOSTIC_ASSERT(!nsContentUtils::IsExpandedPrincipal(aPrincipal),
+ "Documents shouldn't have an expanded principal");
+
+ mPrincipal = aPrincipal;
+}
+
+void nsNodeInfoManager::RemoveNodeInfo(NodeInfo* aNodeInfo) {
+ MOZ_ASSERT(aNodeInfo, "Trying to remove null nodeinfo from manager!");
+
+ if (aNodeInfo == mDocumentNodeInfo) {
+ mDocumentNodeInfo = nullptr;
+ mDocument = nullptr;
+ } else {
+ if (--mNonDocumentNodeInfos == 0) {
+ if (mDocument) {
+ // Note, whoever calls this method should keep NodeInfoManager alive,
+ // even if mDocument gets deleted.
+ mDocument->Release();
+ }
+ }
+ // Drop weak reference if needed
+ if (aNodeInfo == mTextNodeInfo) {
+ mTextNodeInfo = nullptr;
+ } else if (aNodeInfo == mCommentNodeInfo) {
+ mCommentNodeInfo = nullptr;
+ }
+ }
+
+ mRecentlyUsedNodeInfos.Remove(aNodeInfo->mInner);
+ DebugOnly<bool> ret = mNodeInfoHash.Remove(&aNodeInfo->mInner);
+ MOZ_ASSERT(ret, "Can't find mozilla::dom::NodeInfo to remove!!!");
+}
+
+static bool IsSystemOrAddonOrAboutPrincipal(nsIPrincipal* aPrincipal) {
+ return aPrincipal->IsSystemPrincipal() ||
+ BasePrincipal::Cast(aPrincipal)->AddonPolicy() ||
+ // NOTE: about:blank and about:srcdoc inherit the principal of their
+ // parent, so aPrincipal->SchemeIs("about") returns false for them.
+ aPrincipal->SchemeIs("about");
+}
+
+bool nsNodeInfoManager::InternalSVGEnabled() {
+ MOZ_ASSERT(!mSVGEnabled, "Caller should use the cached mSVGEnabled!");
+
+ // If the svg.disabled pref. is true, convert all SVG nodes into
+ // disabled SVG nodes by swapping the namespace.
+ nsNameSpaceManager* nsmgr = nsNameSpaceManager::GetInstance();
+ nsCOMPtr<nsILoadInfo> loadInfo;
+ bool SVGEnabled = false;
+
+ if (nsmgr && !nsmgr->mSVGDisabled) {
+ SVGEnabled = true;
+ } else {
+ nsCOMPtr<nsIChannel> channel = mDocument->GetChannel();
+ // We don't have a channel for SVGs constructed inside a SVG script
+ if (channel) {
+ loadInfo = channel->LoadInfo();
+ }
+ }
+
+ // We allow SVG (regardless of the pref) if this is a system or add-on
+ // principal or about: page, or if this load was requested for a system or
+ // add-on principal or about: page (e.g. a remote image being served as part
+ // of system or add-on UI or about: page)
+ bool conclusion =
+ (SVGEnabled || IsSystemOrAddonOrAboutPrincipal(mPrincipal) ||
+ (loadInfo &&
+ (loadInfo->GetExternalContentPolicyType() ==
+ ExtContentPolicy::TYPE_IMAGE ||
+ loadInfo->GetExternalContentPolicyType() ==
+ ExtContentPolicy::TYPE_OTHER) &&
+ (IsSystemOrAddonOrAboutPrincipal(loadInfo->GetLoadingPrincipal()) ||
+ IsSystemOrAddonOrAboutPrincipal(loadInfo->TriggeringPrincipal()))));
+ mSVGEnabled = Some(conclusion);
+ return conclusion;
+}
+
+bool nsNodeInfoManager::InternalMathMLEnabled() {
+ MOZ_ASSERT(!mMathMLEnabled, "Caller should use the cached mMathMLEnabled!");
+
+ // If the mathml.disabled pref. is true, convert all MathML nodes into
+ // disabled MathML nodes by swapping the namespace.
+ nsNameSpaceManager* nsmgr = nsNameSpaceManager::GetInstance();
+ bool conclusion =
+ ((nsmgr && !nsmgr->mMathMLDisabled) || mPrincipal->IsSystemPrincipal());
+ mMathMLEnabled = Some(conclusion);
+ return conclusion;
+}
+
+void nsNodeInfoManager::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const {
+ aSizes.mDOMOtherSize += aSizes.mState.mMallocSizeOf(this);
+
+ // Measurement of the following members may be added later if DMD finds it
+ // is worthwhile:
+ // - mNodeInfoHash
+}