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 "nsCOMPtr.h"
11 #include "nsContentUtils.h"
12 #include "nsIScriptSecurityManager.h"
13 #include "prtime.h"
14 #include "nsIURI.h"
15 #include "nsPrintfCString.h"
16 #include "mozilla/dom/PerformanceNavigation.h"
17 #include "mozilla/TimeStamp.h"
18 
nsDOMNavigationTiming()19 nsDOMNavigationTiming::nsDOMNavigationTiming()
20 {
21   Clear();
22 }
23 
~nsDOMNavigationTiming()24 nsDOMNavigationTiming::~nsDOMNavigationTiming()
25 {
26 }
27 
28 void
Clear()29 nsDOMNavigationTiming::Clear()
30 {
31   mNavigationType = TYPE_RESERVED;
32   mNavigationStartHighRes = 0;
33   mBeforeUnloadStart = 0;
34   mUnloadStart = 0;
35   mUnloadEnd = 0;
36   mLoadEventStart = 0;
37   mLoadEventEnd = 0;
38   mDOMLoading = 0;
39   mDOMInteractive = 0;
40   mDOMContentLoadedEventStart = 0;
41   mDOMContentLoadedEventEnd = 0;
42   mDOMComplete = 0;
43 
44   mLoadEventStartSet = false;
45   mLoadEventEndSet = false;
46   mDOMLoadingSet = false;
47   mDOMInteractiveSet = false;
48   mDOMContentLoadedEventStartSet = false;
49   mDOMContentLoadedEventEndSet = false;
50   mDOMCompleteSet = false;
51   mDocShellHasBeenActiveSinceNavigationStart = false;
52 }
53 
54 DOMTimeMilliSec
TimeStampToDOM(mozilla::TimeStamp aStamp) const55 nsDOMNavigationTiming::TimeStampToDOM(mozilla::TimeStamp aStamp) const
56 {
57   if (aStamp.IsNull()) {
58     return 0;
59   }
60   mozilla::TimeDuration duration = aStamp - mNavigationStartTimeStamp;
61   return GetNavigationStart() + static_cast<int64_t>(duration.ToMilliseconds());
62 }
63 
DurationFromStart()64 DOMTimeMilliSec nsDOMNavigationTiming::DurationFromStart()
65 {
66   return TimeStampToDOM(mozilla::TimeStamp::Now());
67 }
68 
69 void
NotifyNavigationStart(DocShellState aDocShellState)70 nsDOMNavigationTiming::NotifyNavigationStart(DocShellState aDocShellState)
71 {
72   mNavigationStartHighRes = (double)PR_Now() / PR_USEC_PER_MSEC;
73   mNavigationStartTimeStamp = mozilla::TimeStamp::Now();
74   mDocShellHasBeenActiveSinceNavigationStart = (aDocShellState == DocShellState::eActive);
75 }
76 
77 void
NotifyFetchStart(nsIURI * aURI,Type aNavigationType)78 nsDOMNavigationTiming::NotifyFetchStart(nsIURI* aURI, Type aNavigationType)
79 {
80   mNavigationType = aNavigationType;
81   // At the unload event time we don't really know the loading uri.
82   // Need it for later check for unload timing access.
83   mLoadedURI = aURI;
84 }
85 
86 void
NotifyBeforeUnload()87 nsDOMNavigationTiming::NotifyBeforeUnload()
88 {
89   mBeforeUnloadStart = DurationFromStart();
90 }
91 
92 void
NotifyUnloadAccepted(nsIURI * aOldURI)93 nsDOMNavigationTiming::NotifyUnloadAccepted(nsIURI* aOldURI)
94 {
95   mUnloadStart = mBeforeUnloadStart;
96   mUnloadedURI = aOldURI;
97 }
98 
99 void
NotifyUnloadEventStart()100 nsDOMNavigationTiming::NotifyUnloadEventStart()
101 {
102   mUnloadStart = DurationFromStart();
103 }
104 
105 void
NotifyUnloadEventEnd()106 nsDOMNavigationTiming::NotifyUnloadEventEnd()
107 {
108   mUnloadEnd = DurationFromStart();
109 }
110 
111 void
NotifyLoadEventStart()112 nsDOMNavigationTiming::NotifyLoadEventStart()
113 {
114   if (!mLoadEventStartSet) {
115     mLoadEventStart = DurationFromStart();
116     mLoadEventStartSet = true;
117   }
118 }
119 
120 void
NotifyLoadEventEnd()121 nsDOMNavigationTiming::NotifyLoadEventEnd()
122 {
123   if (!mLoadEventEndSet) {
124     mLoadEventEnd = DurationFromStart();
125     mLoadEventEndSet = true;
126   }
127 }
128 
129 void
SetDOMLoadingTimeStamp(nsIURI * aURI,mozilla::TimeStamp aValue)130 nsDOMNavigationTiming::SetDOMLoadingTimeStamp(nsIURI* aURI, mozilla::TimeStamp aValue)
131 {
132   if (!mDOMLoadingSet) {
133     mLoadedURI = aURI;
134     mDOMLoading = TimeStampToDOM(aValue);
135     mDOMLoadingSet = true;
136   }
137 }
138 
139 void
NotifyDOMLoading(nsIURI * aURI)140 nsDOMNavigationTiming::NotifyDOMLoading(nsIURI* aURI)
141 {
142   if (!mDOMLoadingSet) {
143     mLoadedURI = aURI;
144     mDOMLoading = DurationFromStart();
145     mDOMLoadingSet = true;
146   }
147 }
148 
149 void
NotifyDOMInteractive(nsIURI * aURI)150 nsDOMNavigationTiming::NotifyDOMInteractive(nsIURI* aURI)
151 {
152   if (!mDOMInteractiveSet) {
153     mLoadedURI = aURI;
154     mDOMInteractive = DurationFromStart();
155     mDOMInteractiveSet = true;
156   }
157 }
158 
159 void
NotifyDOMComplete(nsIURI * aURI)160 nsDOMNavigationTiming::NotifyDOMComplete(nsIURI* aURI)
161 {
162   if (!mDOMCompleteSet) {
163     mLoadedURI = aURI;
164     mDOMComplete = DurationFromStart();
165     mDOMCompleteSet = true;
166   }
167 }
168 
169 void
NotifyDOMContentLoadedStart(nsIURI * aURI)170 nsDOMNavigationTiming::NotifyDOMContentLoadedStart(nsIURI* aURI)
171 {
172   if (!mDOMContentLoadedEventStartSet) {
173     mLoadedURI = aURI;
174     mDOMContentLoadedEventStart = DurationFromStart();
175     mDOMContentLoadedEventStartSet = true;
176   }
177 }
178 
179 void
NotifyDOMContentLoadedEnd(nsIURI * aURI)180 nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI)
181 {
182   if (!mDOMContentLoadedEventEndSet) {
183     mLoadedURI = aURI;
184     mDOMContentLoadedEventEnd = DurationFromStart();
185     mDOMContentLoadedEventEndSet = true;
186   }
187 }
188 
189 void
NotifyNonBlankPaintForRootContentDocument()190 nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument()
191 {
192   MOZ_ASSERT(NS_IsMainThread());
193   MOZ_ASSERT(!mNavigationStartTimeStamp.IsNull());
194 
195   if (!mNonBlankPaintTimeStamp.IsNull()) {
196     return;
197   }
198 
199   mNonBlankPaintTimeStamp = TimeStamp::Now();
200   TimeDuration elapsed = mNonBlankPaintTimeStamp - mNavigationStartTimeStamp;
201 
202   if (profiler_is_active()) {
203     nsAutoCString spec;
204     if (mLoadedURI) {
205       mLoadedURI->GetSpec(spec);
206     }
207     nsPrintfCString marker("Non-blank paint after %dms for URL %s, %s",
208                            int(elapsed.ToMilliseconds()), spec.get(),
209                            mDocShellHasBeenActiveSinceNavigationStart ? "foreground tab" : "this tab was inactive some of the time between navigation start and first non-blank paint");
210     PROFILER_MARKER(marker.get());
211   }
212 
213   if (mDocShellHasBeenActiveSinceNavigationStart) {
214     Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_NON_BLANK_PAINT_MS,
215                                    mNavigationStartTimeStamp,
216                                    mNonBlankPaintTimeStamp);
217   }
218 }
219 
220 void
NotifyDocShellStateChanged(DocShellState aDocShellState)221 nsDOMNavigationTiming::NotifyDocShellStateChanged(DocShellState aDocShellState)
222 {
223   mDocShellHasBeenActiveSinceNavigationStart &=
224     (aDocShellState == DocShellState::eActive);
225 }
226 
227 DOMTimeMilliSec
GetUnloadEventStart()228 nsDOMNavigationTiming::GetUnloadEventStart()
229 {
230   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
231   nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false);
232   if (NS_SUCCEEDED(rv)) {
233     return mUnloadStart;
234   }
235   return 0;
236 }
237 
238 DOMTimeMilliSec
GetUnloadEventEnd()239 nsDOMNavigationTiming::GetUnloadEventEnd()
240 {
241   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
242   nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false);
243   if (NS_SUCCEEDED(rv)) {
244     return mUnloadEnd;
245   }
246   return 0;
247 }
248