Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsNodeInfoManager.cpp
Line
Count
Source (jump to first uncovered line)
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/DebugOnly.h"
14
#include "mozilla/dom/NodeInfo.h"
15
#include "mozilla/dom/NodeInfoInlines.h"
16
#include "mozilla/NullPrincipal.h"
17
#include "nsCOMPtr.h"
18
#include "nsString.h"
19
#include "nsAtom.h"
20
#include "nsIDocument.h"
21
#include "nsIPrincipal.h"
22
#include "nsIURI.h"
23
#include "nsContentUtils.h"
24
#include "nsReadableUtils.h"
25
#include "nsGkAtoms.h"
26
#include "nsComponentManagerUtils.h"
27
#include "nsLayoutStatics.h"
28
#include "nsBindingManager.h"
29
#include "nsHashKeys.h"
30
#include "nsCCUncollectableMarker.h"
31
#include "nsNameSpaceManager.h"
32
#include "nsDocument.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()
44
  : mNodeInfoHash(kInitialNodeInfoHashSize),
45
    mDocument(nullptr),
46
    mNonDocumentNodeInfos(0),
47
    mTextNodeInfo(nullptr),
48
    mCommentNodeInfo(nullptr),
49
    mDocumentNodeInfo(nullptr),
50
    mRecentlyUsedNodeInfos(),
51
    mSVGEnabled(eTriUnset),
52
    mMathMLEnabled(eTriUnset)
53
0
{
54
0
  nsLayoutStatics::AddRef();
55
0
56
0
  if (gNodeInfoManagerLeakPRLog)
57
0
    MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug,
58
0
           ("NODEINFOMANAGER %p created", this));
59
0
}
60
61
62
nsNodeInfoManager::~nsNodeInfoManager()
63
0
{
64
0
  // Note: mPrincipal may be null here if we never got inited correctly
65
0
  mPrincipal = nullptr;
66
0
67
0
  mBindingManager = nullptr;
68
0
69
0
  if (gNodeInfoManagerLeakPRLog)
70
0
    MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug,
71
0
           ("NODEINFOMANAGER %p destroyed", this));
72
0
73
0
  nsLayoutStatics::Release();
74
0
}
75
76
NS_IMPL_CYCLE_COLLECTION_CLASS(nsNodeInfoManager)
77
78
NS_IMPL_CYCLE_COLLECTION_UNLINK_0(nsNodeInfoManager)
79
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsNodeInfoManager)
80
0
  if (tmp->mNonDocumentNodeInfos) {
81
0
    NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDocument)
82
0
  }
83
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBindingManager)
84
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
85
86
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsNodeInfoManager, AddRef)
87
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(nsNodeInfoManager, Release)
88
89
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsNodeInfoManager)
90
0
  if (tmp->mDocument) {
91
0
    return NS_CYCLE_COLLECTION_PARTICIPANT(nsDocument)->CanSkip(tmp->mDocument, aRemovingAllowed);
92
0
  }
93
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END
94
0
95
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(nsNodeInfoManager)
96
0
  if (tmp->mDocument) {
97
0
    return NS_CYCLE_COLLECTION_PARTICIPANT(nsDocument)->CanSkipInCC(tmp->mDocument);
98
0
  }
99
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END
100
0
101
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsNodeInfoManager)
102
0
  if (tmp->mDocument) {
103
0
    return NS_CYCLE_COLLECTION_PARTICIPANT(nsDocument)->CanSkipThis(tmp->mDocument);
104
0
  }
105
0
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
106
0
107
0
nsresult
108
0
nsNodeInfoManager::Init(nsIDocument *aDocument)
109
0
{
110
0
  MOZ_ASSERT(!mPrincipal,
111
0
                  "Being inited when we already have a principal?");
112
0
113
0
  mPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
114
0
115
0
  if (aDocument) {
116
0
    mBindingManager = new nsBindingManager(aDocument);
117
0
  }
118
0
119
0
  mDefaultPrincipal = mPrincipal;
120
0
121
0
  mDocument = aDocument;
122
0
123
0
  if (gNodeInfoManagerLeakPRLog)
124
0
    MOZ_LOG(gNodeInfoManagerLeakPRLog, LogLevel::Debug,
125
0
           ("NODEINFOMANAGER %p Init document=%p", this, aDocument));
126
0
127
0
  return NS_OK;
128
0
}
129
130
void
131
nsNodeInfoManager::DropDocumentReference()
132
0
{
133
0
  if (mBindingManager) {
134
0
    mBindingManager->DropDocumentReference();
135
0
  }
136
0
137
0
  // This is probably not needed anymore.
138
0
  for (auto iter = mNodeInfoHash.Iter(); !iter.Done(); iter.Next()) {
139
0
    iter.Data()->mDocument = nullptr;
140
0
  }
141
0
142
0
  NS_ASSERTION(!mNonDocumentNodeInfos, "Shouldn't have non-document nodeinfos!");
143
0
  mDocument = nullptr;
144
0
}
145
146
147
already_AddRefed<mozilla::dom::NodeInfo>
148
nsNodeInfoManager::GetNodeInfo(nsAtom *aName, nsAtom *aPrefix,
149
                               int32_t aNamespaceID, uint16_t aNodeType,
150
                               nsAtom* aExtraName /* = nullptr */)
151
0
{
152
0
  CheckValidNodeInfo(aNodeType, aName, aNamespaceID, aExtraName);
153
0
154
0
  NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType,
155
0
                                 aExtraName);
156
0
157
0
  auto p = mRecentlyUsedNodeInfos.Lookup(tmpKey);
158
0
  if (p) {
159
0
    RefPtr<NodeInfo> nodeInfo = p.Data();
160
0
    return nodeInfo.forget();
161
0
  }
162
0
163
0
  // We don't use LookupForAdd here as that would end up storing the temporary
164
0
  // key instead of using `mInner`.
165
0
  RefPtr<NodeInfo> nodeInfo = mNodeInfoHash.Get(&tmpKey);
166
0
  if (!nodeInfo) {
167
0
    ++mNonDocumentNodeInfos;
168
0
    if (mNonDocumentNodeInfos == 1) {
169
0
      NS_IF_ADDREF(mDocument);
170
0
    }
171
0
172
0
    nodeInfo = new NodeInfo(aName, aPrefix, aNamespaceID, aNodeType, aExtraName, this);
173
0
    mNodeInfoHash.Put(&nodeInfo->mInner, nodeInfo);
174
0
  }
175
0
176
0
  // Have to do the swap thing, because already_AddRefed<nsNodeInfo>
177
0
  // doesn't cast to already_AddRefed<mozilla::dom::NodeInfo>
178
0
  p.Set(nodeInfo);
179
0
  return nodeInfo.forget();
180
0
}
181
182
183
nsresult
184
nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsAtom *aPrefix,
185
                               int32_t aNamespaceID, uint16_t aNodeType,
186
                               NodeInfo** aNodeInfo)
187
0
{
188
0
  // TODO(erahm): Combine this with the atom version.
189
#ifdef DEBUG
190
  {
191
    RefPtr<nsAtom> nameAtom = NS_Atomize(aName);
192
    CheckValidNodeInfo(aNodeType, nameAtom, aNamespaceID, nullptr);
193
  }
194
#endif
195
196
0
  NodeInfo::NodeInfoInner tmpKey(aName, aPrefix, aNamespaceID, aNodeType);
197
0
198
0
  auto p = mRecentlyUsedNodeInfos.Lookup(tmpKey);
199
0
  if (p) {
200
0
    RefPtr<NodeInfo> nodeInfo = p.Data();
201
0
    nodeInfo.forget(aNodeInfo);
202
0
    return NS_OK;
203
0
  }
204
0
205
0
  RefPtr<NodeInfo> nodeInfo = mNodeInfoHash.Get(&tmpKey);
206
0
  if (!nodeInfo) {
207
0
    ++mNonDocumentNodeInfos;
208
0
    if (mNonDocumentNodeInfos == 1) {
209
0
      NS_IF_ADDREF(mDocument);
210
0
    }
211
0
212
0
    RefPtr<nsAtom> nameAtom = NS_Atomize(aName);
213
0
    nodeInfo = new NodeInfo(nameAtom, aPrefix, aNamespaceID, aNodeType, nullptr, this);
214
0
    mNodeInfoHash.Put(&nodeInfo->mInner, nodeInfo);
215
0
  }
216
0
217
0
  p.Set(nodeInfo);
218
0
  nodeInfo.forget(aNodeInfo);
219
0
220
0
  return NS_OK;
221
0
}
222
223
224
nsresult
225
nsNodeInfoManager::GetNodeInfo(const nsAString& aName, nsAtom *aPrefix,
226
                               const nsAString& aNamespaceURI,
227
                               uint16_t aNodeType,
228
                               NodeInfo** aNodeInfo)
229
0
{
230
0
  int32_t nsid = kNameSpaceID_None;
231
0
232
0
  if (!aNamespaceURI.IsEmpty()) {
233
0
    nsresult rv = nsContentUtils::NameSpaceManager()->
234
0
      RegisterNameSpace(aNamespaceURI, nsid);
235
0
    NS_ENSURE_SUCCESS(rv, rv);
236
0
  }
237
0
238
0
  return GetNodeInfo(aName, aPrefix, nsid, aNodeType, aNodeInfo);
239
0
}
240
241
already_AddRefed<NodeInfo>
242
nsNodeInfoManager::GetTextNodeInfo()
243
0
{
244
0
  RefPtr<mozilla::dom::NodeInfo> nodeInfo;
245
0
246
0
  if (!mTextNodeInfo) {
247
0
    nodeInfo = GetNodeInfo(nsGkAtoms::textTagName, nullptr, kNameSpaceID_None,
248
0
                           nsINode::TEXT_NODE, nullptr);
249
0
    // Hold a weak ref; the nodeinfo will let us know when it goes away
250
0
    mTextNodeInfo = nodeInfo;
251
0
  } else {
252
0
    nodeInfo = mTextNodeInfo;
253
0
  }
254
0
255
0
  return nodeInfo.forget();
256
0
}
257
258
already_AddRefed<NodeInfo>
259
nsNodeInfoManager::GetCommentNodeInfo()
260
0
{
261
0
  RefPtr<NodeInfo> nodeInfo;
262
0
263
0
  if (!mCommentNodeInfo) {
264
0
    nodeInfo = GetNodeInfo(nsGkAtoms::commentTagName, nullptr,
265
0
                           kNameSpaceID_None, nsINode::COMMENT_NODE,
266
0
                           nullptr);
267
0
    // Hold a weak ref; the nodeinfo will let us know when it goes away
268
0
    mCommentNodeInfo = nodeInfo;
269
0
  }
270
0
  else {
271
0
    nodeInfo = mCommentNodeInfo;
272
0
  }
273
0
274
0
  return nodeInfo.forget();
275
0
}
276
277
already_AddRefed<NodeInfo>
278
nsNodeInfoManager::GetDocumentNodeInfo()
279
0
{
280
0
  RefPtr<NodeInfo> nodeInfo;
281
0
282
0
  if (!mDocumentNodeInfo) {
283
0
    NS_ASSERTION(mDocument, "Should have mDocument!");
284
0
    nodeInfo = GetNodeInfo(nsGkAtoms::documentNodeName, nullptr,
285
0
                           kNameSpaceID_None, nsINode::DOCUMENT_NODE,
286
0
                           nullptr);
287
0
    // Hold a weak ref; the nodeinfo will let us know when it goes away
288
0
    mDocumentNodeInfo = nodeInfo;
289
0
290
0
    --mNonDocumentNodeInfos;
291
0
    if (!mNonDocumentNodeInfos) {
292
0
      mDocument->Release(); // Don't set mDocument to null!
293
0
    }
294
0
  }
295
0
  else {
296
0
    nodeInfo = mDocumentNodeInfo;
297
0
  }
298
0
299
0
  return nodeInfo.forget();
300
0
}
301
302
void
303
nsNodeInfoManager::SetDocumentPrincipal(nsIPrincipal *aPrincipal)
304
0
{
305
0
  mPrincipal = nullptr;
306
0
  if (!aPrincipal) {
307
0
    aPrincipal = mDefaultPrincipal;
308
0
  }
309
0
310
0
  NS_ASSERTION(aPrincipal, "Must have principal by this point!");
311
0
  MOZ_DIAGNOSTIC_ASSERT(!nsContentUtils::IsExpandedPrincipal(aPrincipal),
312
0
                        "Documents shouldn't have an expanded principal");
313
0
314
0
  mPrincipal = aPrincipal;
315
0
}
316
317
void
318
nsNodeInfoManager::RemoveNodeInfo(NodeInfo *aNodeInfo)
319
0
{
320
0
  MOZ_ASSERT(aNodeInfo, "Trying to remove null nodeinfo from manager!");
321
0
322
0
  if (aNodeInfo == mDocumentNodeInfo) {
323
0
    mDocumentNodeInfo = nullptr;
324
0
    mDocument = nullptr;
325
0
  } else {
326
0
    if (--mNonDocumentNodeInfos == 0) {
327
0
      if (mDocument) {
328
0
        // Note, whoever calls this method should keep NodeInfoManager alive,
329
0
        // even if mDocument gets deleted.
330
0
        mDocument->Release();
331
0
      }
332
0
    }
333
0
    // Drop weak reference if needed
334
0
    if (aNodeInfo == mTextNodeInfo) {
335
0
      mTextNodeInfo = nullptr;
336
0
    }
337
0
    else if (aNodeInfo == mCommentNodeInfo) {
338
0
      mCommentNodeInfo = nullptr;
339
0
    }
340
0
  }
341
0
342
0
  mRecentlyUsedNodeInfos.Remove(aNodeInfo->mInner);
343
0
  DebugOnly<bool> ret = mNodeInfoHash.Remove(&aNodeInfo->mInner);
344
0
  MOZ_ASSERT(ret, "Can't find mozilla::dom::NodeInfo to remove!!!");
345
0
}
346
347
bool
348
nsNodeInfoManager::InternalSVGEnabled()
349
0
{
350
0
  // If the svg.disabled pref. is true, convert all SVG nodes into
351
0
  // disabled SVG nodes by swapping the namespace.
352
0
  nsNameSpaceManager* nsmgr = nsNameSpaceManager::GetInstance();
353
0
  nsCOMPtr<nsILoadInfo> loadInfo;
354
0
  bool SVGEnabled = false;
355
0
356
0
  if (nsmgr && !nsmgr->mSVGDisabled) {
357
0
    SVGEnabled = true;
358
0
  } else {
359
0
    nsCOMPtr<nsIChannel> channel = mDocument->GetChannel();
360
0
    // We don't have a channel for SVGs constructed inside a SVG script
361
0
    if (channel) {
362
0
      loadInfo = channel->GetLoadInfo();
363
0
    }
364
0
  }
365
0
  bool conclusion =
366
0
    (SVGEnabled || nsContentUtils::IsSystemPrincipal(mPrincipal) ||
367
0
     (loadInfo &&
368
0
      (loadInfo->GetExternalContentPolicyType() ==
369
0
         nsIContentPolicy::TYPE_IMAGE ||
370
0
       loadInfo->GetExternalContentPolicyType() ==
371
0
         nsIContentPolicy::TYPE_OTHER) &&
372
0
      (nsContentUtils::IsSystemPrincipal(loadInfo->LoadingPrincipal()) ||
373
0
       nsContentUtils::IsSystemPrincipal(loadInfo->TriggeringPrincipal()))));
374
0
  mSVGEnabled = conclusion ? eTriTrue : eTriFalse;
375
0
  return conclusion;
376
0
}
377
378
bool
379
nsNodeInfoManager::InternalMathMLEnabled()
380
0
{
381
0
  // If the mathml.disabled pref. is true, convert all MathML nodes into
382
0
  // disabled MathML nodes by swapping the namespace.
383
0
  nsNameSpaceManager* nsmgr = nsNameSpaceManager::GetInstance();
384
0
  bool conclusion = ((nsmgr && !nsmgr->mMathMLDisabled) ||
385
0
                     nsContentUtils::IsSystemPrincipal(mPrincipal));
386
0
  mMathMLEnabled = conclusion ? eTriTrue : eTriFalse;
387
0
  return conclusion;
388
0
}
389
390
void
391
nsNodeInfoManager::AddSizeOfIncludingThis(nsWindowSizes& aSizes) const
392
0
{
393
0
  aSizes.mDOMOtherSize += aSizes.mState.mMallocSizeOf(this);
394
0
395
0
  if (mBindingManager) {
396
0
    aSizes.mBindingsSize +=
397
0
      mBindingManager->SizeOfIncludingThis(aSizes.mState.mMallocSizeOf);
398
0
  }
399
0
400
0
  // Measurement of the following members may be added later if DMD finds it
401
0
  // is worthwhile:
402
0
  // - mNodeInfoHash
403
0
}