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 "nsDOMNavigationTiming.h"
8 
9 #include "GeckoProfiler.h"
10 #include "ipc/IPCMessageUtilsSpecializations.h"
11 #include "mozilla/ProfilerMarkers.h"
12 #include "mozilla/Telemetry.h"
13 #include "mozilla/TimeStamp.h"
14 #include "mozilla/dom/Document.h"
15 #include "mozilla/dom/PerformanceNavigation.h"
16 #include "mozilla/ipc/IPDLParamTraits.h"
17 #include "mozilla/ipc/URIUtils.h"
18 #include "nsCOMPtr.h"
19 #include "nsContentUtils.h"
20 #include "nsDocShell.h"
21 #include "nsHttp.h"
22 #include "nsIScriptSecurityManager.h"
23 #include "nsIURI.h"
24 #include "nsPrintfCString.h"
25 #include "prtime.h"
26 
27 using namespace mozilla;
28 
29 namespace mozilla {
30 
31 LazyLogModule gPageLoadLog("PageLoad");
32 #define PAGELOAD_LOG(args) MOZ_LOG(gPageLoadLog, LogLevel::Debug, args)
33 #define PAGELOAD_LOG_ENABLED() MOZ_LOG_TEST(gPageLoadLog, LogLevel::Error)
34 
35 }  // namespace mozilla
36 
nsDOMNavigationTiming(nsDocShell * aDocShell)37 nsDOMNavigationTiming::nsDOMNavigationTiming(nsDocShell* aDocShell) {
38   Clear();
39 
40   mDocShell = aDocShell;
41 }
42 
43 nsDOMNavigationTiming::~nsDOMNavigationTiming() = default;
44 
Clear()45 void nsDOMNavigationTiming::Clear() {
46   mNavigationType = TYPE_RESERVED;
47   mNavigationStartHighRes = 0;
48 
49   mBeforeUnloadStart = TimeStamp();
50   mUnloadStart = TimeStamp();
51   mUnloadEnd = TimeStamp();
52   mLoadEventStart = TimeStamp();
53   mLoadEventEnd = TimeStamp();
54   mDOMLoading = TimeStamp();
55   mDOMInteractive = TimeStamp();
56   mDOMContentLoadedEventStart = TimeStamp();
57   mDOMContentLoadedEventEnd = TimeStamp();
58   mDOMComplete = TimeStamp();
59   mContentfulPaint = TimeStamp();
60   mNonBlankPaint = TimeStamp();
61 
62   mDocShellHasBeenActiveSinceNavigationStart = false;
63 }
64 
Anonymize(nsIURI * aFinalURI)65 void nsDOMNavigationTiming::Anonymize(nsIURI* aFinalURI) {
66   mLoadedURI = aFinalURI;
67   mUnloadedURI = nullptr;
68   mBeforeUnloadStart = TimeStamp();
69   mUnloadStart = TimeStamp();
70   mUnloadEnd = TimeStamp();
71 }
72 
TimeStampToDOM(TimeStamp aStamp) const73 DOMTimeMilliSec nsDOMNavigationTiming::TimeStampToDOM(TimeStamp aStamp) const {
74   if (aStamp.IsNull()) {
75     return 0;
76   }
77 
78   TimeDuration duration = aStamp - mNavigationStart;
79   return GetNavigationStart() + static_cast<int64_t>(duration.ToMilliseconds());
80 }
81 
NotifyNavigationStart(DocShellState aDocShellState)82 void nsDOMNavigationTiming::NotifyNavigationStart(
83     DocShellState aDocShellState) {
84   mNavigationStartHighRes = (double)PR_Now() / PR_USEC_PER_MSEC;
85   mNavigationStart = TimeStamp::Now();
86   mDocShellHasBeenActiveSinceNavigationStart =
87       (aDocShellState == DocShellState::eActive);
88   PROFILER_MARKER_UNTYPED("Navigation::Start", DOM,
89                           MarkerInnerWindowIdFromDocShell(mDocShell));
90 }
91 
NotifyFetchStart(nsIURI * aURI,Type aNavigationType)92 void nsDOMNavigationTiming::NotifyFetchStart(nsIURI* aURI,
93                                              Type aNavigationType) {
94   mNavigationType = aNavigationType;
95   // At the unload event time we don't really know the loading uri.
96   // Need it for later check for unload timing access.
97   mLoadedURI = aURI;
98 }
99 
NotifyRestoreStart()100 void nsDOMNavigationTiming::NotifyRestoreStart() {
101   mNavigationType = TYPE_BACK_FORWARD;
102 }
103 
NotifyBeforeUnload()104 void nsDOMNavigationTiming::NotifyBeforeUnload() {
105   mBeforeUnloadStart = TimeStamp::Now();
106 }
107 
NotifyUnloadAccepted(nsIURI * aOldURI)108 void nsDOMNavigationTiming::NotifyUnloadAccepted(nsIURI* aOldURI) {
109   mUnloadStart = mBeforeUnloadStart;
110   mUnloadedURI = aOldURI;
111 }
112 
NotifyUnloadEventStart()113 void nsDOMNavigationTiming::NotifyUnloadEventStart() {
114   mUnloadStart = TimeStamp::Now();
115   PROFILER_MARKER("Unload", NETWORK,
116                   MarkerOptions(MarkerTiming::IntervalStart(),
117                                 MarkerInnerWindowIdFromDocShell(mDocShell)),
118                   Tracing, "Navigation");
119 }
120 
NotifyUnloadEventEnd()121 void nsDOMNavigationTiming::NotifyUnloadEventEnd() {
122   mUnloadEnd = TimeStamp::Now();
123   PROFILER_MARKER("Unload", NETWORK,
124                   MarkerOptions(MarkerTiming::IntervalEnd(),
125                                 MarkerInnerWindowIdFromDocShell(mDocShell)),
126                   Tracing, "Navigation");
127 }
128 
NotifyLoadEventStart()129 void nsDOMNavigationTiming::NotifyLoadEventStart() {
130   if (!mLoadEventStart.IsNull()) {
131     return;
132   }
133   mLoadEventStart = TimeStamp::Now();
134 
135   PROFILER_MARKER("Load", NETWORK,
136                   MarkerOptions(MarkerTiming::IntervalStart(),
137                                 MarkerInnerWindowIdFromDocShell(mDocShell)),
138                   Tracing, "Navigation");
139 
140   if (IsTopLevelContentDocumentInContentProcess()) {
141     TimeStamp now = TimeStamp::Now();
142 
143     Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_LOAD_EVENT_START_MS,
144                                    mNavigationStart, now);
145 
146     if (mDocShellHasBeenActiveSinceNavigationStart) {
147       if (net::nsHttp::IsBeforeLastActiveTabLoadOptimization(
148               mNavigationStart)) {
149         Telemetry::AccumulateTimeDelta(
150             Telemetry::TIME_TO_LOAD_EVENT_START_ACTIVE_NETOPT_MS,
151             mNavigationStart, now);
152       } else {
153         Telemetry::AccumulateTimeDelta(
154             Telemetry::TIME_TO_LOAD_EVENT_START_ACTIVE_MS, mNavigationStart,
155             now);
156       }
157     }
158   }
159 }
160 
NotifyLoadEventEnd()161 void nsDOMNavigationTiming::NotifyLoadEventEnd() {
162   if (!mLoadEventEnd.IsNull()) {
163     return;
164   }
165   mLoadEventEnd = TimeStamp::Now();
166 
167   PROFILER_MARKER("Load", NETWORK,
168                   MarkerOptions(MarkerTiming::IntervalEnd(),
169                                 MarkerInnerWindowIdFromDocShell(mDocShell)),
170                   Tracing, "Navigation");
171 
172   if (IsTopLevelContentDocumentInContentProcess()) {
173     if (profiler_can_accept_markers() || PAGELOAD_LOG_ENABLED()) {
174       TimeDuration elapsed = mLoadEventEnd - mNavigationStart;
175       TimeDuration duration = mLoadEventEnd - mLoadEventStart;
176       nsAutoCString spec;
177       if (mLoadedURI) {
178         mLoadedURI->GetSpec(spec);
179       }
180       nsPrintfCString marker(
181           "Document %s loaded after %dms, load event duration %dms", spec.get(),
182           int(elapsed.ToMilliseconds()), int(duration.ToMilliseconds()));
183       PAGELOAD_LOG(("%s", marker.get()));
184       PROFILER_MARKER_TEXT(
185           "DocumentLoad", DOM,
186           MarkerOptions(MarkerTiming::Interval(mNavigationStart, mLoadEventEnd),
187                         MarkerInnerWindowIdFromDocShell(mDocShell)),
188           marker);
189     }
190     Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_LOAD_EVENT_END_MS,
191                                    mNavigationStart);
192   }
193 }
194 
SetDOMLoadingTimeStamp(nsIURI * aURI,TimeStamp aValue)195 void nsDOMNavigationTiming::SetDOMLoadingTimeStamp(nsIURI* aURI,
196                                                    TimeStamp aValue) {
197   if (!mDOMLoading.IsNull()) {
198     return;
199   }
200   mLoadedURI = aURI;
201   mDOMLoading = aValue;
202 }
203 
NotifyDOMLoading(nsIURI * aURI)204 void nsDOMNavigationTiming::NotifyDOMLoading(nsIURI* aURI) {
205   if (!mDOMLoading.IsNull()) {
206     return;
207   }
208   mLoadedURI = aURI;
209   mDOMLoading = TimeStamp::Now();
210 
211   PROFILER_MARKER_UNTYPED("Navigation::DOMLoading", DOM,
212                           MarkerInnerWindowIdFromDocShell(mDocShell));
213 }
214 
NotifyDOMInteractive(nsIURI * aURI)215 void nsDOMNavigationTiming::NotifyDOMInteractive(nsIURI* aURI) {
216   if (!mDOMInteractive.IsNull()) {
217     return;
218   }
219   mLoadedURI = aURI;
220   mDOMInteractive = TimeStamp::Now();
221 
222   PROFILER_MARKER_UNTYPED("Navigation::DOMInteractive", DOM,
223                           MarkerInnerWindowIdFromDocShell(mDocShell));
224 }
225 
NotifyDOMComplete(nsIURI * aURI)226 void nsDOMNavigationTiming::NotifyDOMComplete(nsIURI* aURI) {
227   if (!mDOMComplete.IsNull()) {
228     return;
229   }
230   mLoadedURI = aURI;
231   mDOMComplete = TimeStamp::Now();
232 
233   PROFILER_MARKER_UNTYPED("Navigation::DOMComplete", DOM,
234                           MarkerInnerWindowIdFromDocShell(mDocShell));
235 }
236 
NotifyDOMContentLoadedStart(nsIURI * aURI)237 void nsDOMNavigationTiming::NotifyDOMContentLoadedStart(nsIURI* aURI) {
238   if (!mDOMContentLoadedEventStart.IsNull()) {
239     return;
240   }
241 
242   mLoadedURI = aURI;
243   mDOMContentLoadedEventStart = TimeStamp::Now();
244 
245   PROFILER_MARKER("DOMContentLoaded", NETWORK,
246                   MarkerOptions(MarkerTiming::IntervalStart(),
247                                 MarkerInnerWindowIdFromDocShell(mDocShell)),
248                   Tracing, "Navigation");
249 
250   if (IsTopLevelContentDocumentInContentProcess()) {
251     TimeStamp now = TimeStamp::Now();
252 
253     Telemetry::AccumulateTimeDelta(
254         Telemetry::TIME_TO_DOM_CONTENT_LOADED_START_MS, mNavigationStart, now);
255 
256     if (mDocShellHasBeenActiveSinceNavigationStart) {
257       if (net::nsHttp::IsBeforeLastActiveTabLoadOptimization(
258               mNavigationStart)) {
259         Telemetry::AccumulateTimeDelta(
260             Telemetry::TIME_TO_DOM_CONTENT_LOADED_START_ACTIVE_NETOPT_MS,
261             mNavigationStart, now);
262       } else {
263         Telemetry::AccumulateTimeDelta(
264             Telemetry::TIME_TO_DOM_CONTENT_LOADED_START_ACTIVE_MS,
265             mNavigationStart, now);
266       }
267     }
268   }
269 }
270 
NotifyDOMContentLoadedEnd(nsIURI * aURI)271 void nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI) {
272   if (!mDOMContentLoadedEventEnd.IsNull()) {
273     return;
274   }
275 
276   mLoadedURI = aURI;
277   mDOMContentLoadedEventEnd = TimeStamp::Now();
278 
279   PROFILER_MARKER("DOMContentLoaded", NETWORK,
280                   MarkerOptions(MarkerTiming::IntervalEnd(),
281                                 MarkerInnerWindowIdFromDocShell(mDocShell)),
282                   Tracing, "Navigation");
283 
284   if (IsTopLevelContentDocumentInContentProcess()) {
285     Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_CONTENT_LOADED_END_MS,
286                                    mNavigationStart);
287   }
288 }
289 
290 // static
TTITimeoutCallback(nsITimer * aTimer,void * aClosure)291 void nsDOMNavigationTiming::TTITimeoutCallback(nsITimer* aTimer,
292                                                void* aClosure) {
293   nsDOMNavigationTiming* self = static_cast<nsDOMNavigationTiming*>(aClosure);
294   self->TTITimeout(aTimer);
295 }
296 
297 #define TTI_WINDOW_SIZE_MS (5 * 1000)
298 
TTITimeout(nsITimer * aTimer)299 void nsDOMNavigationTiming::TTITimeout(nsITimer* aTimer) {
300   // Check TTI: see if it's been 5 seconds since the last Long Task
301   TimeStamp now = TimeStamp::Now();
302   MOZ_RELEASE_ASSERT(!mContentfulPaint.IsNull(),
303                      "TTI timeout with no contentful-paint?");
304 
305   nsCOMPtr<nsIThread> mainThread = do_GetMainThread();
306   TimeStamp lastLongTaskEnded;
307   mainThread->GetLastLongNonIdleTaskEnd(&lastLongTaskEnded);
308   // Window starts at mContentfulPaint; any long task before that is ignored
309   if (lastLongTaskEnded.IsNull() || lastLongTaskEnded < mContentfulPaint) {
310     PAGELOAD_LOG(
311         ("no longtask (last was %g ms before ContentfulPaint)",
312          lastLongTaskEnded.IsNull()
313              ? 0
314              : (mContentfulPaint - lastLongTaskEnded).ToMilliseconds()));
315     lastLongTaskEnded = mContentfulPaint;
316   }
317   TimeDuration delta = now - lastLongTaskEnded;
318   PAGELOAD_LOG(("TTI delta: %g ms", delta.ToMilliseconds()));
319   if (delta.ToMilliseconds() < TTI_WINDOW_SIZE_MS) {
320     // Less than 5 seconds since the last long task or start of the window.
321     // Schedule another check.
322     PAGELOAD_LOG(("TTI: waiting additional %g ms",
323                   (TTI_WINDOW_SIZE_MS + 100) - delta.ToMilliseconds()));
324     aTimer->InitWithNamedFuncCallback(
325         TTITimeoutCallback, this,
326         (TTI_WINDOW_SIZE_MS + 100) -
327             delta.ToMilliseconds(),  // slightly after the window ends
328         nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
329         "nsDOMNavigationTiming::TTITimeout");
330     return;
331   }
332 
333   // To correctly implement TTI/TTFI as proposed, we'd need to not
334   // fire it until there are no more than 2 network loads.  By the
335   // proposed definition, without that we're closer to
336   // TimeToFirstInteractive.  There are also arguments about what sort
337   // of loads should qualify.
338 
339   // XXX check number of network loads, and if > 2 mark to check if loads
340   // decreases to 2 (or record that point and let the normal timer here
341   // handle it)
342 
343   // TTI has occurred!  TTI is either FCP (if there are no longtasks and no
344   // DCLEnd in the window that starts at FCP), or at the end of the last
345   // Long Task or DOMContentLoadedEnd (whichever is later). lastLongTaskEnded
346   // is >= FCP here.
347 
348   if (mTTFI.IsNull()) {
349     // lastLongTaskEnded is >= mContentfulPaint
350     mTTFI = (mDOMContentLoadedEventEnd.IsNull() ||
351              lastLongTaskEnded > mDOMContentLoadedEventEnd)
352                 ? lastLongTaskEnded
353                 : mDOMContentLoadedEventEnd;
354     PAGELOAD_LOG(
355         ("TTFI after %dms (LongTask was at %dms, DCL was %dms)",
356          int((mTTFI - mNavigationStart).ToMilliseconds()),
357          lastLongTaskEnded.IsNull()
358              ? 0
359              : int((lastLongTaskEnded - mNavigationStart).ToMilliseconds()),
360          mDOMContentLoadedEventEnd.IsNull()
361              ? 0
362              : int((mDOMContentLoadedEventEnd - mNavigationStart)
363                        .ToMilliseconds())));
364   }
365   // XXX Implement TTI via check number of network loads, and if > 2 mark
366   // to check if loads decreases to 2 (or record that point and let the
367   // normal timer here handle it)
368 
369   mTTITimer = nullptr;
370 
371   if (profiler_can_accept_markers() || PAGELOAD_LOG_ENABLED()) {
372     TimeDuration elapsed = mTTFI - mNavigationStart;
373     MOZ_ASSERT(elapsed.ToMilliseconds() > 0);
374     TimeDuration elapsedLongTask =
375         lastLongTaskEnded.IsNull() ? 0 : lastLongTaskEnded - mNavigationStart;
376     nsAutoCString spec;
377     if (mLoadedURI) {
378       mLoadedURI->GetSpec(spec);
379     }
380     nsPrintfCString marker("TTFI after %dms (LongTask was at %dms) for URL %s",
381                            int(elapsed.ToMilliseconds()),
382                            int(elapsedLongTask.ToMilliseconds()), spec.get());
383 
384     PROFILER_MARKER_TEXT(
385         "TimeToFirstInteractive (TTFI)", DOM,
386         MarkerOptions(MarkerTiming::Interval(mNavigationStart, mTTFI),
387                       MarkerInnerWindowIdFromDocShell(mDocShell)),
388         marker);
389   }
390 }
391 
NotifyNonBlankPaintForRootContentDocument()392 void nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument() {
393   MOZ_ASSERT(NS_IsMainThread());
394   MOZ_ASSERT(!mNavigationStart.IsNull());
395 
396   if (!mNonBlankPaint.IsNull()) {
397     return;
398   }
399 
400   mNonBlankPaint = TimeStamp::Now();
401 
402   if (profiler_thread_is_being_profiled() || PAGELOAD_LOG_ENABLED()) {
403     TimeDuration elapsed = mNonBlankPaint - mNavigationStart;
404     nsAutoCString spec;
405     if (mLoadedURI) {
406       mLoadedURI->GetSpec(spec);
407     }
408     nsPrintfCString marker(
409         "Non-blank paint after %dms for URL %s, %s",
410         int(elapsed.ToMilliseconds()), spec.get(),
411         mDocShellHasBeenActiveSinceNavigationStart
412             ? "foreground tab"
413             : "this tab was inactive some of the time between navigation start "
414               "and first non-blank paint");
415     PAGELOAD_LOG(("%s", marker.get()));
416     PROFILER_MARKER_TEXT(
417         "FirstNonBlankPaint", DOM,
418         MarkerOptions(MarkerTiming::Interval(mNavigationStart, mNonBlankPaint),
419                       MarkerInnerWindowIdFromDocShell(mDocShell)),
420         marker);
421   }
422 
423   if (mDocShellHasBeenActiveSinceNavigationStart) {
424     if (net::nsHttp::IsBeforeLastActiveTabLoadOptimization(mNavigationStart)) {
425       Telemetry::AccumulateTimeDelta(
426           Telemetry::TIME_TO_NON_BLANK_PAINT_NETOPT_MS, mNavigationStart,
427           mNonBlankPaint);
428     } else {
429       Telemetry::AccumulateTimeDelta(
430           Telemetry::TIME_TO_NON_BLANK_PAINT_NO_NETOPT_MS, mNavigationStart,
431           mNonBlankPaint);
432     }
433 
434     Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_NON_BLANK_PAINT_MS,
435                                    mNavigationStart, mNonBlankPaint);
436   }
437 }
438 
NotifyContentfulPaintForRootContentDocument(const mozilla::TimeStamp & aCompositeEndTime)439 void nsDOMNavigationTiming::NotifyContentfulPaintForRootContentDocument(
440     const mozilla::TimeStamp& aCompositeEndTime) {
441   MOZ_ASSERT(NS_IsMainThread());
442   MOZ_ASSERT(!mNavigationStart.IsNull());
443 
444   if (!mContentfulPaint.IsNull()) {
445     return;
446   }
447 
448   mContentfulPaint = aCompositeEndTime;
449 
450   if (profiler_can_accept_markers() || PAGELOAD_LOG_ENABLED()) {
451     TimeDuration elapsed = mContentfulPaint - mNavigationStart;
452     nsAutoCString spec;
453     if (mLoadedURI) {
454       mLoadedURI->GetSpec(spec);
455     }
456     nsPrintfCString marker(
457         "Contentful paint after %dms for URL %s, %s",
458         int(elapsed.ToMilliseconds()), spec.get(),
459         mDocShellHasBeenActiveSinceNavigationStart
460             ? "foreground tab"
461             : "this tab was inactive some of the time between navigation start "
462               "and first non-blank paint");
463     PAGELOAD_LOG(("%s", marker.get()));
464     PROFILER_MARKER_TEXT(
465         "FirstContentfulPaint", DOM,
466         MarkerOptions(
467             MarkerTiming::Interval(mNavigationStart, mContentfulPaint),
468             MarkerInnerWindowIdFromDocShell(mDocShell)),
469         marker);
470   }
471 
472   if (!mTTITimer) {
473     mTTITimer = NS_NewTimer();
474   }
475 
476   // TTI is first checked 5 seconds after the FCP (non-blank-paint is very close
477   // to FCP).
478   mTTITimer->InitWithNamedFuncCallback(TTITimeoutCallback, this,
479                                        TTI_WINDOW_SIZE_MS,
480                                        nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
481                                        "nsDOMNavigationTiming::TTITimeout");
482 
483   if (mDocShellHasBeenActiveSinceNavigationStart) {
484     Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_FIRST_CONTENTFUL_PAINT_MS,
485                                    mNavigationStart, mContentfulPaint);
486   }
487 }
488 
NotifyDOMContentFlushedForRootContentDocument()489 void nsDOMNavigationTiming::NotifyDOMContentFlushedForRootContentDocument() {
490   MOZ_ASSERT(NS_IsMainThread());
491   MOZ_ASSERT(!mNavigationStart.IsNull());
492 
493   if (!mDOMContentFlushed.IsNull()) {
494     return;
495   }
496 
497   mDOMContentFlushed = TimeStamp::Now();
498 
499   if (profiler_thread_is_being_profiled() || PAGELOAD_LOG_ENABLED()) {
500     TimeDuration elapsed = mDOMContentFlushed - mNavigationStart;
501     nsAutoCString spec;
502     if (mLoadedURI) {
503       mLoadedURI->GetSpec(spec);
504     }
505     nsPrintfCString marker(
506         "DOMContentFlushed after %dms for URL %s, %s",
507         int(elapsed.ToMilliseconds()), spec.get(),
508         mDocShellHasBeenActiveSinceNavigationStart
509             ? "foreground tab"
510             : "this tab was inactive some of the time between navigation start "
511               "and DOMContentFlushed");
512     PAGELOAD_LOG(("%s", marker.get()));
513     PROFILER_MARKER_TEXT(
514         "DOMContentFlushed", DOM,
515         MarkerOptions(
516             MarkerTiming::Interval(mNavigationStart, mDOMContentFlushed),
517             MarkerInnerWindowIdFromDocShell(mDocShell)),
518         marker);
519   }
520 }
521 
NotifyDocShellStateChanged(DocShellState aDocShellState)522 void nsDOMNavigationTiming::NotifyDocShellStateChanged(
523     DocShellState aDocShellState) {
524   mDocShellHasBeenActiveSinceNavigationStart &=
525       (aDocShellState == DocShellState::eActive);
526 }
527 
GetUnloadEventStartTimeStamp() const528 mozilla::TimeStamp nsDOMNavigationTiming::GetUnloadEventStartTimeStamp() const {
529   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
530   // todo: if you intend to update CheckSameOriginURI to log the error to the
531   // console you also need to update the 'aFromPrivateWindow' argument.
532   nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false, false);
533   if (NS_SUCCEEDED(rv)) {
534     return mUnloadStart;
535   }
536   return mozilla::TimeStamp();
537 }
538 
GetUnloadEventEndTimeStamp() const539 mozilla::TimeStamp nsDOMNavigationTiming::GetUnloadEventEndTimeStamp() const {
540   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
541   // todo: if you intend to update CheckSameOriginURI to log the error to the
542   // console you also need to update the 'aFromPrivateWindow' argument.
543   nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false, false);
544   if (NS_SUCCEEDED(rv)) {
545     return mUnloadEnd;
546   }
547   return mozilla::TimeStamp();
548 }
549 
IsTopLevelContentDocumentInContentProcess() const550 bool nsDOMNavigationTiming::IsTopLevelContentDocumentInContentProcess() const {
551   if (!mDocShell) {
552     return false;
553   }
554   if (!XRE_IsContentProcess()) {
555     return false;
556   }
557   return mDocShell->GetBrowsingContext()->IsTopContent();
558 }
559 
nsDOMNavigationTiming(nsDocShell * aDocShell,nsDOMNavigationTiming * aOther)560 nsDOMNavigationTiming::nsDOMNavigationTiming(nsDocShell* aDocShell,
561                                              nsDOMNavigationTiming* aOther)
562     : mDocShell(aDocShell),
563       mUnloadedURI(aOther->mUnloadedURI),
564       mLoadedURI(aOther->mLoadedURI),
565       mNavigationType(aOther->mNavigationType),
566       mNavigationStartHighRes(aOther->mNavigationStartHighRes),
567       mNavigationStart(aOther->mNavigationStart),
568       mNonBlankPaint(aOther->mNonBlankPaint),
569       mContentfulPaint(aOther->mContentfulPaint),
570       mDOMContentFlushed(aOther->mDOMContentFlushed),
571       mBeforeUnloadStart(aOther->mBeforeUnloadStart),
572       mUnloadStart(aOther->mUnloadStart),
573       mUnloadEnd(aOther->mUnloadEnd),
574       mLoadEventStart(aOther->mLoadEventStart),
575       mLoadEventEnd(aOther->mLoadEventEnd),
576       mDOMLoading(aOther->mDOMLoading),
577       mDOMInteractive(aOther->mDOMInteractive),
578       mDOMContentLoadedEventStart(aOther->mDOMContentLoadedEventStart),
579       mDOMContentLoadedEventEnd(aOther->mDOMContentLoadedEventEnd),
580       mDOMComplete(aOther->mDOMComplete),
581       mTTFI(aOther->mTTFI),
582       mDocShellHasBeenActiveSinceNavigationStart(
583           aOther->mDocShellHasBeenActiveSinceNavigationStart) {}
584 
585 /* static */
Write(IPC::Message * aMsg,IProtocol * aActor,nsDOMNavigationTiming * aParam)586 void mozilla::ipc::IPDLParamTraits<nsDOMNavigationTiming*>::Write(
587     IPC::Message* aMsg, IProtocol* aActor, nsDOMNavigationTiming* aParam) {
588   RefPtr<nsIURI> unloadedURI = aParam->mUnloadedURI.get();
589   RefPtr<nsIURI> loadedURI = aParam->mLoadedURI.get();
590   WriteIPDLParam(aMsg, aActor, unloadedURI ? Some(unloadedURI) : Nothing());
591   WriteIPDLParam(aMsg, aActor, loadedURI ? Some(loadedURI) : Nothing());
592   WriteIPDLParam(aMsg, aActor, uint32_t(aParam->mNavigationType));
593   WriteIPDLParam(aMsg, aActor, aParam->mNavigationStartHighRes);
594   WriteIPDLParam(aMsg, aActor, aParam->mNavigationStart);
595   WriteIPDLParam(aMsg, aActor, aParam->mNonBlankPaint);
596   WriteIPDLParam(aMsg, aActor, aParam->mContentfulPaint);
597   WriteIPDLParam(aMsg, aActor, aParam->mDOMContentFlushed);
598   WriteIPDLParam(aMsg, aActor, aParam->mBeforeUnloadStart);
599   WriteIPDLParam(aMsg, aActor, aParam->mUnloadStart);
600   WriteIPDLParam(aMsg, aActor, aParam->mUnloadEnd);
601   WriteIPDLParam(aMsg, aActor, aParam->mLoadEventStart);
602   WriteIPDLParam(aMsg, aActor, aParam->mLoadEventEnd);
603   WriteIPDLParam(aMsg, aActor, aParam->mDOMLoading);
604   WriteIPDLParam(aMsg, aActor, aParam->mDOMInteractive);
605   WriteIPDLParam(aMsg, aActor, aParam->mDOMContentLoadedEventStart);
606   WriteIPDLParam(aMsg, aActor, aParam->mDOMContentLoadedEventEnd);
607   WriteIPDLParam(aMsg, aActor, aParam->mDOMComplete);
608   WriteIPDLParam(aMsg, aActor, aParam->mTTFI);
609   WriteIPDLParam(aMsg, aActor,
610                  aParam->mDocShellHasBeenActiveSinceNavigationStart);
611 }
612 
613 /* static */
Read(const IPC::Message * aMsg,PickleIterator * aIter,IProtocol * aActor,RefPtr<nsDOMNavigationTiming> * aResult)614 bool mozilla::ipc::IPDLParamTraits<nsDOMNavigationTiming*>::Read(
615     const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor,
616     RefPtr<nsDOMNavigationTiming>* aResult) {
617   auto timing = MakeRefPtr<nsDOMNavigationTiming>(nullptr);
618   uint32_t type;
619   Maybe<RefPtr<nsIURI>> unloadedURI;
620   Maybe<RefPtr<nsIURI>> loadedURI;
621   if (!ReadIPDLParam(aMsg, aIter, aActor, &unloadedURI) ||
622       !ReadIPDLParam(aMsg, aIter, aActor, &loadedURI) ||
623       !ReadIPDLParam(aMsg, aIter, aActor, &type) ||
624       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mNavigationStartHighRes) ||
625       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mNavigationStart) ||
626       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mNonBlankPaint) ||
627       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mContentfulPaint) ||
628       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mDOMContentFlushed) ||
629       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mBeforeUnloadStart) ||
630       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mUnloadStart) ||
631       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mUnloadEnd) ||
632       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mLoadEventStart) ||
633       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mLoadEventEnd) ||
634       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mDOMLoading) ||
635       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mDOMInteractive) ||
636       !ReadIPDLParam(aMsg, aIter, aActor,
637                      &timing->mDOMContentLoadedEventStart) ||
638       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mDOMContentLoadedEventEnd) ||
639       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mDOMComplete) ||
640       !ReadIPDLParam(aMsg, aIter, aActor, &timing->mTTFI) ||
641       !ReadIPDLParam(aMsg, aIter, aActor,
642                      &timing->mDocShellHasBeenActiveSinceNavigationStart)) {
643     return false;
644   }
645   timing->mNavigationType = nsDOMNavigationTiming::Type(type);
646   if (unloadedURI) {
647     timing->mUnloadedURI = std::move(*unloadedURI);
648   }
649   if (loadedURI) {
650     timing->mLoadedURI = std::move(*loadedURI);
651   }
652   *aResult = std::move(timing);
653   return true;
654 }
655