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