nsNodeInfoManager.cpp (13559B)
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/BasePrincipal.h" 14 #include "mozilla/DebugOnly.h" 15 #include "mozilla/NullPrincipal.h" 16 #include "mozilla/StaticPrefs_dom.h" 17 #include "mozilla/dom/DocGroup.h" 18 #include "mozilla/dom/Document.h" 19 #include "mozilla/dom/NodeInfo.h" 20 #include "mozilla/dom/NodeInfoInlines.h" 21 #include "nsAtom.h" 22 #include "nsCCUncollectableMarker.h" 23 #include "nsCOMPtr.h" 24 #include "nsComponentManagerUtils.h" 25 #include "nsContentUtils.h" 26 #include "nsGkAtoms.h" 27 #include "nsHashKeys.h" 28 #include "nsIPrincipal.h" 29 #include "nsLayoutStatics.h" 30 #include "nsNameSpaceManager.h" 31 #include "nsReadableUtils.h" 32 #include "nsString.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(mozilla::dom::Document* aDocument, 44 nsIPrincipal* aPrincipal) 45 : mNodeInfoHash(kInitialNodeInfoHashSize), 46 mDocument(aDocument), 47 mNonDocumentNodeInfos(0), 48 mTextNodeInfo(nullptr), 49 mCommentNodeInfo(nullptr), 50 mDocumentNodeInfo(nullptr), 51 mRecentlyUsedNodeInfos(), 52 mArena(nullptr) { 53 nsLayoutStatics::AddRef(); 54 55 if (aPrincipal) { 56 mPrincipal = aPrincipal; 57 } else { 58 mPrincipal = NullPrincipal::CreateWithoutOriginAttributes(); 59 } 60 mDefaultPrincipal = mPrincipal; 61 62 if (gNodeInfoManagerLeakPRLog) { 63 MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug, 64 ("NODEINFOMANAGER %p created, document=%p", this, aDocument)); 65 } 66 } 67 68 nsNodeInfoManager::~nsNodeInfoManager() { 69 // Note: mPrincipal may be null here if we never got inited correctly 70 mPrincipal = nullptr; 71 72 mArena = nullptr; 73 74 if (gNodeInfoManagerLeakPRLog) 75 MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug, 76 ("NODEINFOMANAGER %p destroyed", this)); 77 78 nsLayoutStatics::Release(); 79 } 80 81 NS_IMPL_CYCLE_COLLECTION_CLASS(nsNodeInfoManager) 82 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) 87 } 88 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 89 90 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsNodeInfoManager) 91 if (tmp->mDocument) { 92 return NS_CYCLE_COLLECTION_PARTICIPANT(mozilla::dom::Document) 93 ->CanSkip(tmp->mDocument, aRemovingAllowed); 94 } 95 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END 96 97 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsNodeInfoManager) 98 if (tmp->mDocument) { 99 return NS_CYCLE_COLLECTION_PARTICIPANT(mozilla::dom::Document) 100 ->CanSkipInCC(tmp->mDocument); 101 } 102 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END 103 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); 108 } 109 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 110 111 void nsNodeInfoManager::DropDocumentReference() { 112 // This is probably not needed anymore. 113 for (const auto& entry : mNodeInfoHash.Values()) { 114 entry->mDocument = nullptr; 115 } 116 117 NS_ASSERTION(!mNonDocumentNodeInfos, 118 "Shouldn't have non-document nodeinfos!"); 119 mDocument = nullptr; 120 } 121 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); 126 127 NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType, 128 aExtraName); 129 130 auto p = mRecentlyUsedNodeInfos.Lookup(tmpKey); 131 if (p) { 132 RefPtr<NodeInfo> nodeInfo = p.Data(); 133 return nodeInfo.forget(); 134 } 135 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); 139 if (!nodeInfo) { 140 ++mNonDocumentNodeInfos; 141 if (mNonDocumentNodeInfos == 1) { 142 NS_IF_ADDREF(mDocument); 143 } 144 145 nodeInfo = 146 new NodeInfo(aName, aPrefix, aNamespaceID, aNodeType, aExtraName, this); 147 mNodeInfoHash.InsertOrUpdate(&nodeInfo->mInner, nodeInfo); 148 } 149 150 // Have to do the swap thing, because already_AddRefed<nsNodeInfo> 151 // doesn't cast to already_AddRefed<mozilla::dom::NodeInfo> 152 p.Set(nodeInfo); 153 return nodeInfo.forget(); 154 } 155 156 nsresult nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsAtom* aPrefix, 157 int32_t aNamespaceID, 158 uint16_t aNodeType, 159 NodeInfo** aNodeInfo) { 160 // TODO(erahm): Combine this with the atom version. 161 #ifdef DEBUG 162 { 163 RefPtr<nsAtom> nameAtom = NS_Atomize(aName); 164 CheckValidNodeInfo(aNodeType, nameAtom, aNamespaceID, nullptr); 165 } 166 #endif 167 168 NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType); 169 170 auto p = mRecentlyUsedNodeInfos.Lookup(tmpKey); 171 if (p) { 172 RefPtr<NodeInfo> nodeInfo = p.Data(); 173 nodeInfo.forget(aNodeInfo); 174 return NS_OK; 175 } 176 177 RefPtr<NodeInfo> nodeInfo = mNodeInfoHash.Get(&tmpKey); 178 if (!nodeInfo) { 179 ++mNonDocumentNodeInfos; 180 if (mNonDocumentNodeInfos == 1) { 181 NS_IF_ADDREF(mDocument); 182 } 183 184 RefPtr<nsAtom> nameAtom = NS_Atomize(aName); 185 nodeInfo = 186 new NodeInfo(nameAtom, aPrefix, aNamespaceID, aNodeType, nullptr, this); 187 mNodeInfoHash.InsertOrUpdate(&nodeInfo->mInner, nodeInfo); 188 } 189 190 p.Set(nodeInfo); 191 nodeInfo.forget(aNodeInfo); 192 193 return NS_OK; 194 } 195 196 nsresult nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsAtom* aPrefix, 197 const nsAString& aNamespaceURI, 198 uint16_t aNodeType, 199 NodeInfo** aNodeInfo) { 200 int32_t nsid = kNameSpaceID_None; 201 202 if (!aNamespaceURI.IsEmpty()) { 203 nsresult rv = nsNameSpaceManager::GetInstance()->RegisterNameSpace( 204 aNamespaceURI, nsid); 205 NS_ENSURE_SUCCESS(rv, rv); 206 } 207 208 return GetNodeInfo(aName, aPrefix, nsid, aNodeType, aNodeInfo); 209 } 210 211 already_AddRefed<NodeInfo> nsNodeInfoManager::GetTextNodeInfo() { 212 RefPtr<mozilla::dom::NodeInfo> nodeInfo; 213 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; 219 } else { 220 nodeInfo = mTextNodeInfo; 221 } 222 223 return nodeInfo.forget(); 224 } 225 226 already_AddRefed<NodeInfo> nsNodeInfoManager::GetCommentNodeInfo() { 227 RefPtr<NodeInfo> nodeInfo; 228 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; 234 } else { 235 nodeInfo = mCommentNodeInfo; 236 } 237 238 return nodeInfo.forget(); 239 } 240 241 already_AddRefed<NodeInfo> nsNodeInfoManager::GetDocumentNodeInfo() { 242 RefPtr<NodeInfo> nodeInfo; 243 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; 250 251 --mNonDocumentNodeInfos; 252 if (!mNonDocumentNodeInfos) { 253 mDocument->Release(); // Don't set mDocument to null! 254 } 255 } else { 256 nodeInfo = mDocumentNodeInfo; 257 } 258 259 return nodeInfo.forget(); 260 } 261 262 void* nsNodeInfoManager::Allocate(size_t aSize) { 263 if (!mHasAllocated) { 264 if (mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup()) { 265 if (!mArena) { 266 mozilla::dom::DocGroup* docGroup = GetDocument()->GetDocGroupOrCreate(); 267 if (docGroup) { 268 MOZ_ASSERT(!GetDocument()->HasChildren()); 269 mArena = docGroup->ArenaAllocator(); 270 } 271 } 272 #ifdef DEBUG 273 else { 274 mozilla::dom::DocGroup* docGroup = GetDocument()->GetDocGroup(); 275 MOZ_ASSERT(docGroup); 276 MOZ_ASSERT(mArena == docGroup->ArenaAllocator()); 277 } 278 #endif 279 } 280 mHasAllocated = true; 281 } 282 283 #ifdef DEBUG 284 if (!mozilla::StaticPrefs::dom_arena_allocator_enabled_AtStartup()) { 285 MOZ_ASSERT(!mArena, "mArena should not set if the pref is not on"); 286 }; 287 #endif 288 289 if (mArena) { 290 return mArena->Allocate(aSize); 291 } 292 return malloc(aSize); 293 } 294 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()); 300 301 if (!mArena) { 302 mArena = aArena; 303 } 304 } 305 306 void nsNodeInfoManager::SetDocumentPrincipal(nsIPrincipal* aPrincipal) { 307 mPrincipal = nullptr; 308 if (!aPrincipal) { 309 aPrincipal = mDefaultPrincipal; 310 } 311 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"); 315 316 mPrincipal = aPrincipal; 317 } 318 319 void nsNodeInfoManager::RemoveNodeInfo(NodeInfo* aNodeInfo) { 320 MOZ_ASSERT(aNodeInfo, "Trying to remove null nodeinfo from manager!"); 321 322 if (aNodeInfo == mDocumentNodeInfo) { 323 mDocumentNodeInfo = nullptr; 324 mDocument = nullptr; 325 } else { 326 if (--mNonDocumentNodeInfos == 0) { 327 if (mDocument) { 328 // Note, whoever calls this method should keep NodeInfoManager alive, 329 // even if mDocument gets deleted. 330 mDocument->Release(); 331 } 332 } 333 // Drop weak reference if needed 334 if (aNodeInfo == mTextNodeInfo) { 335 mTextNodeInfo = nullptr; 336 } else if (aNodeInfo == mCommentNodeInfo) { 337 mCommentNodeInfo = nullptr; 338 } 339 } 340 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!!!"); 344 } 345 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"); 352 } 353 354 static bool IsAndroidResource(nsIURI* aURI) { 355 #ifdef ANDROID 356 if (aURI->SchemeIs("resource")) { 357 nsAutoCString host, path; 358 aURI->GetHost(host); 359 aURI->GetFilePath(path); 360 if (host.EqualsLiteral("android") && 361 StringBeginsWith(path, "/assets/"_ns)) { 362 return true; 363 } 364 } 365 #endif 366 return false; 367 } 368 369 bool nsNodeInfoManager::InternalSVGEnabled() { 370 MOZ_ASSERT(!mSVGEnabled, "Caller should use the cached mSVGEnabled!"); 371 372 // If the svg.disabled pref. is true, convert all SVG nodes into 373 // disabled SVG nodes by swapping the namespace. 374 nsNameSpaceManager* nsmgr = nsNameSpaceManager::GetInstance(); 375 nsCOMPtr<nsILoadInfo> loadInfo; 376 bool SVGEnabled = false; 377 378 if (nsmgr && !nsmgr->mSVGDisabled) { 379 SVGEnabled = true; 380 } else { 381 nsCOMPtr<nsIChannel> channel = mDocument->GetChannel(); 382 // We don't have a channel for SVGs constructed inside a SVG script 383 if (channel) { 384 loadInfo = channel->LoadInfo(); 385 } 386 } 387 388 // We allow SVG (regardless of the pref) if this is a system or add-on 389 // principal or about: page, or if this load was requested for a system or 390 // add-on principal or about: page (e.g. a remote image being served as part 391 // of system or add-on UI or about: page) 392 bool conclusion = 393 (SVGEnabled || IsSystemOrAddonOrAboutPrincipal(mPrincipal) || 394 IsAndroidResource(mDocument->GetDocumentURI()) || 395 (loadInfo && 396 (loadInfo->GetExternalContentPolicyType() == 397 ExtContentPolicy::TYPE_IMAGE || 398 loadInfo->GetExternalContentPolicyType() == 399 ExtContentPolicy::TYPE_OTHER) && 400 (IsSystemOrAddonOrAboutPrincipal(loadInfo->GetLoadingPrincipal()) || 401 IsSystemOrAddonOrAboutPrincipal(loadInfo->TriggeringPrincipal())))); 402 mSVGEnabled = Some(conclusion); 403 return conclusion; 404 } 405 406 bool nsNodeInfoManager::InternalMathMLEnabled() { 407 MOZ_ASSERT(!mMathMLEnabled, "Caller should use the cached mMathMLEnabled!"); 408 409 // If the mathml.disabled pref. is true, convert all MathML nodes into 410 // disabled MathML nodes by swapping the namespace. 411 nsNameSpaceManager* nsmgr = nsNameSpaceManager::GetInstance(); 412 bool conclusion = 413 ((nsmgr && !nsmgr->mMathMLDisabled) || mPrincipal->IsSystemPrincipal()); 414 mMathMLEnabled = Some(conclusion); 415 return conclusion; 416 } 417 418 void nsNodeInfoManager::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const { 419 aSizes.mDOMSizes.mDOMOtherSize += aSizes.mState.mMallocSizeOf(this); 420 421 // Measurement of the following members may be added later if DMD finds it 422 // is worthwhile: 423 // - mNodeInfoHash 424 }