Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/performance/PerformanceMainThread.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
#include "PerformanceMainThread.h"
8
#include "PerformanceNavigation.h"
9
#include "mozilla/StaticPrefs.h"
10
#include "nsICacheInfoChannel.h"
11
12
namespace mozilla {
13
namespace dom {
14
15
namespace {
16
17
void
18
GetURLSpecFromChannel(nsITimedChannel* aChannel, nsAString& aSpec)
19
0
{
20
0
  aSpec.AssignLiteral("document");
21
0
22
0
  nsCOMPtr<nsIChannel> channel = do_QueryInterface(aChannel);
23
0
  if (!channel) {
24
0
    return;
25
0
  }
26
0
27
0
  nsCOMPtr<nsIURI> uri;
28
0
  nsresult rv = channel->GetURI(getter_AddRefs(uri));
29
0
  if (NS_WARN_IF(NS_FAILED(rv)) || !uri) {
30
0
    return;
31
0
  }
32
0
33
0
  nsAutoCString spec;
34
0
  rv = uri->GetSpec(spec);
35
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
36
0
    return;
37
0
  }
38
0
39
0
  aSpec = NS_ConvertUTF8toUTF16(spec);
40
0
}
41
42
} // anonymous
43
44
NS_IMPL_CYCLE_COLLECTION_CLASS(PerformanceMainThread)
45
46
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(PerformanceMainThread,
47
0
                                                Performance)
48
0
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTiming,
49
0
                                mNavigation,
50
0
                                mDocEntry)
51
0
  tmp->mMozMemory = nullptr;
52
0
  mozilla::DropJSObjects(this);
53
0
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
54
55
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(PerformanceMainThread,
56
0
                                                  Performance)
57
0
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTiming,
58
0
                                    mNavigation,
59
0
                                    mDocEntry)
60
0
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
61
62
0
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(PerformanceMainThread,
63
0
                                                Performance)
64
0
  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mMozMemory)
65
0
NS_IMPL_CYCLE_COLLECTION_TRACE_END
66
67
NS_IMPL_ADDREF_INHERITED(PerformanceMainThread, Performance)
68
NS_IMPL_RELEASE_INHERITED(PerformanceMainThread, Performance)
69
70
// QueryInterface implementation for PerformanceMainThread
71
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PerformanceMainThread)
72
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
73
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
74
0
NS_INTERFACE_MAP_END_INHERITING(Performance)
75
76
PerformanceMainThread::PerformanceMainThread(nsPIDOMWindowInner* aWindow,
77
                                             nsDOMNavigationTiming* aDOMTiming,
78
                                             nsITimedChannel* aChannel,
79
                                             bool aPrincipal)
80
  : Performance(aWindow, aPrincipal)
81
  , mDOMTiming(aDOMTiming)
82
  , mChannel(aChannel)
83
0
{
84
0
  MOZ_ASSERT(aWindow, "Parent window object should be provided");
85
0
  CreateNavigationTimingEntry();
86
0
}
87
88
PerformanceMainThread::~PerformanceMainThread()
89
0
{
90
0
  mozilla::DropJSObjects(this);
91
0
}
92
93
void
94
PerformanceMainThread::GetMozMemory(JSContext *aCx,
95
                                    JS::MutableHandle<JSObject*> aObj)
96
0
{
97
0
  if (!mMozMemory) {
98
0
    mMozMemory = js::gc::NewMemoryInfoObject(aCx);
99
0
    if (mMozMemory) {
100
0
      mozilla::HoldJSObjects(this);
101
0
    }
102
0
  }
103
0
104
0
  aObj.set(mMozMemory);
105
0
}
106
107
PerformanceTiming*
108
PerformanceMainThread::Timing()
109
0
{
110
0
  if (!mTiming) {
111
0
    // For navigation timing, the third argument (an nsIHttpChannel) is null
112
0
    // since the cross-domain redirect were already checked.  The last argument
113
0
    // (zero time) for performance.timing is the navigation start value.
114
0
    mTiming = new PerformanceTiming(this, mChannel, nullptr,
115
0
                                    mDOMTiming->GetNavigationStart());
116
0
  }
117
0
118
0
  return mTiming;
119
0
}
120
121
void
122
PerformanceMainThread::DispatchBufferFullEvent()
123
0
{
124
0
  RefPtr<Event> event = NS_NewDOMEvent(this, nullptr, nullptr);
125
0
  // it bubbles, and it isn't cancelable
126
0
  event->InitEvent(NS_LITERAL_STRING("resourcetimingbufferfull"), true, false);
127
0
  event->SetTrusted(true);
128
0
  DispatchEvent(*event);
129
0
}
130
131
PerformanceNavigation*
132
PerformanceMainThread::Navigation()
133
0
{
134
0
  if (!mNavigation) {
135
0
    mNavigation = new PerformanceNavigation(this);
136
0
  }
137
0
138
0
  return mNavigation;
139
0
}
140
141
/**
142
 * An entry should be added only after the resource is loaded.
143
 * This method is not thread safe and can only be called on the main thread.
144
 */
145
void
146
PerformanceMainThread::AddEntry(nsIHttpChannel* channel,
147
                                nsITimedChannel* timedChannel)
148
0
{
149
0
  MOZ_ASSERT(NS_IsMainThread());
150
0
151
0
  nsAutoString initiatorType;
152
0
  nsAutoString entryName;
153
0
154
0
  UniquePtr<PerformanceTimingData> performanceTimingData(
155
0
    PerformanceTimingData::Create(timedChannel, channel, 0, initiatorType,
156
0
                                  entryName));
157
0
  if (!performanceTimingData) {
158
0
    return;
159
0
  }
160
0
161
0
  // The PerformanceResourceTiming object will use the PerformanceTimingData
162
0
  // object to get all the required timings.
163
0
  RefPtr<PerformanceResourceTiming> performanceEntry =
164
0
    new PerformanceResourceTiming(std::move(performanceTimingData), this,
165
0
                                  entryName);
166
0
167
0
  performanceEntry->SetInitiatorType(initiatorType);
168
0
  InsertResourceEntry(performanceEntry);
169
0
}
170
171
// To be removed once bug 1124165 lands
172
bool
173
PerformanceMainThread::IsPerformanceTimingAttribute(const nsAString& aName)
174
0
{
175
0
  // Note that toJSON is added to this list due to bug 1047848
176
0
  static const char* attributes[] =
177
0
    {"navigationStart", "unloadEventStart", "unloadEventEnd", "redirectStart",
178
0
     "redirectEnd", "fetchStart", "domainLookupStart", "domainLookupEnd",
179
0
     "connectStart", "secureConnectionStart", "connectEnd", "requestStart", "responseStart",
180
0
     "responseEnd", "domLoading", "domInteractive",
181
0
     "domContentLoadedEventStart", "domContentLoadedEventEnd", "domComplete",
182
0
     "loadEventStart", "loadEventEnd", nullptr};
183
0
184
0
  for (uint32_t i = 0; attributes[i]; ++i) {
185
0
    if (aName.EqualsASCII(attributes[i])) {
186
0
      return true;
187
0
    }
188
0
  }
189
0
190
0
  return false;
191
0
}
192
193
DOMHighResTimeStamp
194
PerformanceMainThread::GetPerformanceTimingFromString(const nsAString& aProperty)
195
0
{
196
0
  if (!IsPerformanceTimingAttribute(aProperty)) {
197
0
    return 0;
198
0
  }
199
0
  if (aProperty.EqualsLiteral("navigationStart")) {
200
0
    // DOMHighResTimeStamp is in relation to navigationStart, so this will be
201
0
    // zero.
202
0
    return GetDOMTiming()->GetNavigationStart();
203
0
  }
204
0
  if (aProperty.EqualsLiteral("unloadEventStart")) {
205
0
    return GetDOMTiming()->GetUnloadEventStart();
206
0
  }
207
0
  if (aProperty.EqualsLiteral("unloadEventEnd")) {
208
0
    return GetDOMTiming()->GetUnloadEventEnd();
209
0
  }
210
0
  if (aProperty.EqualsLiteral("redirectStart")) {
211
0
    return Timing()->RedirectStart();
212
0
  }
213
0
  if (aProperty.EqualsLiteral("redirectEnd")) {
214
0
    return Timing()->RedirectEnd();
215
0
  }
216
0
  if (aProperty.EqualsLiteral("fetchStart")) {
217
0
    return Timing()->FetchStart();
218
0
  }
219
0
  if (aProperty.EqualsLiteral("domainLookupStart")) {
220
0
    return Timing()->DomainLookupStart();
221
0
  }
222
0
  if (aProperty.EqualsLiteral("domainLookupEnd")) {
223
0
    return Timing()->DomainLookupEnd();
224
0
  }
225
0
  if (aProperty.EqualsLiteral("connectStart")) {
226
0
    return Timing()->ConnectStart();
227
0
  }
228
0
  if (aProperty.EqualsLiteral("secureConnectionStart")) {
229
0
    return Timing()->SecureConnectionStart();
230
0
  }
231
0
  if (aProperty.EqualsLiteral("connectEnd")) {
232
0
    return Timing()->ConnectEnd();
233
0
  }
234
0
  if (aProperty.EqualsLiteral("requestStart")) {
235
0
    return Timing()->RequestStart();
236
0
  }
237
0
  if (aProperty.EqualsLiteral("responseStart")) {
238
0
    return Timing()->ResponseStart();
239
0
  }
240
0
  if (aProperty.EqualsLiteral("responseEnd")) {
241
0
    return Timing()->ResponseEnd();
242
0
  }
243
0
  if (aProperty.EqualsLiteral("domLoading")) {
244
0
    return GetDOMTiming()->GetDomLoading();
245
0
  }
246
0
  if (aProperty.EqualsLiteral("domInteractive")) {
247
0
    return GetDOMTiming()->GetDomInteractive();
248
0
  }
249
0
  if (aProperty.EqualsLiteral("domContentLoadedEventStart")) {
250
0
    return GetDOMTiming()->GetDomContentLoadedEventStart();
251
0
  }
252
0
  if (aProperty.EqualsLiteral("domContentLoadedEventEnd")) {
253
0
    return GetDOMTiming()->GetDomContentLoadedEventEnd();
254
0
  }
255
0
  if (aProperty.EqualsLiteral("domComplete")) {
256
0
    return GetDOMTiming()->GetDomComplete();
257
0
  }
258
0
  if (aProperty.EqualsLiteral("loadEventStart")) {
259
0
    return GetDOMTiming()->GetLoadEventStart();
260
0
  }
261
0
  if (aProperty.EqualsLiteral("loadEventEnd"))  {
262
0
    return GetDOMTiming()->GetLoadEventEnd();
263
0
  }
264
0
  MOZ_CRASH("IsPerformanceTimingAttribute and GetPerformanceTimingFromString are out of sync");
265
0
  return 0;
266
0
}
267
268
void
269
PerformanceMainThread::InsertUserEntry(PerformanceEntry* aEntry)
270
0
{
271
0
  MOZ_ASSERT(NS_IsMainThread());
272
0
273
0
  nsAutoCString uri;
274
0
  uint64_t markCreationEpoch = 0;
275
0
276
0
  if (StaticPrefs::dom_performance_enable_user_timing_logging() ||
277
0
      nsContentUtils::SendPerformanceTimingNotifications()) {
278
0
    nsresult rv = NS_ERROR_FAILURE;
279
0
    nsCOMPtr<nsPIDOMWindowInner> owner = GetOwner();
280
0
    if (owner && owner->GetDocumentURI()) {
281
0
      rv = owner->GetDocumentURI()->GetHost(uri);
282
0
    }
283
0
284
0
    if(NS_FAILED(rv)) {
285
0
      // If we have no URI, just put in "none".
286
0
      uri.AssignLiteral("none");
287
0
    }
288
0
    markCreationEpoch = static_cast<uint64_t>(PR_Now() / PR_USEC_PER_MSEC);
289
0
290
0
    if (StaticPrefs::dom_performance_enable_user_timing_logging()) {
291
0
      Performance::LogEntry(aEntry, uri);
292
0
    }
293
0
  }
294
0
295
0
  if (nsContentUtils::SendPerformanceTimingNotifications()) {
296
0
    TimingNotification(aEntry, uri, markCreationEpoch);
297
0
  }
298
0
299
0
  Performance::InsertUserEntry(aEntry);
300
0
}
301
302
TimeStamp
303
PerformanceMainThread::CreationTimeStamp() const
304
0
{
305
0
  return GetDOMTiming()->GetNavigationStartTimeStamp();
306
0
}
307
308
DOMHighResTimeStamp
309
PerformanceMainThread::CreationTime() const
310
0
{
311
0
  return GetDOMTiming()->GetNavigationStart();
312
0
}
313
314
void
315
PerformanceMainThread::CreateNavigationTimingEntry()
316
0
{
317
0
  MOZ_ASSERT(!mDocEntry, "mDocEntry should be null.");
318
0
319
0
  if (!nsContentUtils::IsPerformanceNavigationTimingEnabled()) {
320
0
    return;
321
0
  }
322
0
323
0
  nsAutoString name;
324
0
  GetURLSpecFromChannel(mChannel, name);
325
0
326
0
  UniquePtr<PerformanceTimingData> timing(
327
0
    new PerformanceTimingData(mChannel, nullptr, 0));
328
0
329
0
  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
330
0
  if (httpChannel) {
331
0
    timing->SetPropertiesFromHttpChannel(httpChannel, mChannel);
332
0
  }
333
0
334
0
  mDocEntry = new PerformanceNavigationTiming(std::move(timing), this, name);
335
0
}
336
337
void
338
PerformanceMainThread::QueueNavigationTimingEntry()
339
0
{
340
0
  if (!mDocEntry) {
341
0
    return;
342
0
  }
343
0
344
0
  // Let's update some values.
345
0
  nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
346
0
  if (httpChannel) {
347
0
    mDocEntry->UpdatePropertiesFromHttpChannel(httpChannel, mChannel);
348
0
  }
349
0
350
0
  QueueEntry(mDocEntry);
351
0
}
352
353
void
354
PerformanceMainThread::GetEntries(nsTArray<RefPtr<PerformanceEntry>>& aRetval)
355
0
{
356
0
  // We return an empty list when 'privacy.resistFingerprinting' is on.
357
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
358
0
    aRetval.Clear();
359
0
    return;
360
0
  }
361
0
362
0
  aRetval = mResourceEntries;
363
0
  aRetval.AppendElements(mUserEntries);
364
0
365
0
  if (mDocEntry) {
366
0
    aRetval.AppendElement(mDocEntry);
367
0
  }
368
0
369
0
  aRetval.Sort(PerformanceEntryComparator());
370
0
}
371
372
void
373
PerformanceMainThread::GetEntriesByType(const nsAString& aEntryType,
374
                                        nsTArray<RefPtr<PerformanceEntry>>& aRetval)
375
0
{
376
0
  // We return an empty list when 'privacy.resistFingerprinting' is on.
377
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
378
0
    aRetval.Clear();
379
0
    return;
380
0
  }
381
0
382
0
  if (aEntryType.EqualsLiteral("navigation")) {
383
0
    aRetval.Clear();
384
0
385
0
    if (mDocEntry) {
386
0
      aRetval.AppendElement(mDocEntry);
387
0
    }
388
0
    return;
389
0
  }
390
0
391
0
  Performance::GetEntriesByType(aEntryType, aRetval);
392
0
}
393
394
void
395
PerformanceMainThread::GetEntriesByName(const nsAString& aName,
396
                                        const Optional<nsAString>& aEntryType,
397
                                        nsTArray<RefPtr<PerformanceEntry>>& aRetval)
398
0
{
399
0
  // We return an empty list when 'privacy.resistFingerprinting' is on.
400
0
  if (nsContentUtils::ShouldResistFingerprinting()) {
401
0
    aRetval.Clear();
402
0
    return;
403
0
  }
404
0
405
0
  Performance::GetEntriesByName(aName, aEntryType, aRetval);
406
0
407
0
  // The navigation entry is the first one. If it exists and the name matches,
408
0
  // let put it in front.
409
0
  if (mDocEntry && mDocEntry->GetName().Equals(aName)) {
410
0
    aRetval.InsertElementAt(0, mDocEntry);
411
0
    return;
412
0
  }
413
0
}
414
415
} // dom namespace
416
} // mozilla namespace