tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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 }