Line data Source code
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 "nsDocShell.h"
13 : #include "nsIDocShellTreeItem.h"
14 : #include "nsIScriptSecurityManager.h"
15 : #include "prtime.h"
16 : #include "nsIURI.h"
17 : #include "nsPrintfCString.h"
18 : #include "mozilla/dom/PerformanceNavigation.h"
19 : #include "mozilla/TimeStamp.h"
20 : #include "mozilla/Telemetry.h"
21 :
22 : using namespace mozilla;
23 :
24 27 : nsDOMNavigationTiming::nsDOMNavigationTiming(nsDocShell* aDocShell)
25 : {
26 27 : Clear();
27 :
28 27 : mDocShell = aDocShell;
29 27 : }
30 :
31 0 : nsDOMNavigationTiming::~nsDOMNavigationTiming()
32 : {
33 0 : }
34 :
35 : void
36 27 : nsDOMNavigationTiming::Clear()
37 : {
38 27 : mNavigationType = TYPE_RESERVED;
39 27 : mNavigationStartHighRes = 0;
40 27 : mBeforeUnloadStart = 0;
41 27 : mUnloadStart = 0;
42 27 : mUnloadEnd = 0;
43 27 : mLoadEventStart = 0;
44 27 : mLoadEventEnd = 0;
45 27 : mDOMLoading = 0;
46 27 : mDOMInteractive = 0;
47 27 : mDOMContentLoadedEventStart = 0;
48 27 : mDOMContentLoadedEventEnd = 0;
49 27 : mDOMComplete = 0;
50 :
51 27 : mLoadEventStartSet = false;
52 27 : mLoadEventEndSet = false;
53 27 : mDOMLoadingSet = false;
54 27 : mDOMInteractiveSet = false;
55 27 : mDOMContentLoadedEventStartSet = false;
56 27 : mDOMContentLoadedEventEndSet = false;
57 27 : mDOMCompleteSet = false;
58 27 : mDocShellHasBeenActiveSinceNavigationStart = false;
59 27 : }
60 :
61 : DOMTimeMilliSec
62 124 : nsDOMNavigationTiming::TimeStampToDOM(TimeStamp aStamp) const
63 : {
64 124 : if (aStamp.IsNull()) {
65 0 : return 0;
66 : }
67 :
68 124 : TimeDuration duration = aStamp - mNavigationStartTimeStamp;
69 124 : return GetNavigationStart() + static_cast<int64_t>(duration.ToMilliseconds());
70 : }
71 :
72 100 : DOMTimeMilliSec nsDOMNavigationTiming::DurationFromStart()
73 : {
74 100 : return TimeStampToDOM(TimeStamp::Now());
75 : }
76 :
77 : void
78 31 : nsDOMNavigationTiming::NotifyNavigationStart(DocShellState aDocShellState)
79 : {
80 31 : mNavigationStartHighRes = (double)PR_Now() / PR_USEC_PER_MSEC;
81 31 : mNavigationStartTimeStamp = TimeStamp::Now();
82 31 : mDocShellHasBeenActiveSinceNavigationStart = (aDocShellState == DocShellState::eActive);
83 31 : profiler_add_marker("Navigation::Start");
84 31 : }
85 :
86 : void
87 6 : nsDOMNavigationTiming::NotifyFetchStart(nsIURI* aURI, Type aNavigationType)
88 : {
89 6 : mNavigationType = aNavigationType;
90 : // At the unload event time we don't really know the loading uri.
91 : // Need it for later check for unload timing access.
92 6 : mLoadedURI = aURI;
93 6 : }
94 :
95 : void
96 6 : nsDOMNavigationTiming::NotifyBeforeUnload()
97 : {
98 6 : mBeforeUnloadStart = DurationFromStart();
99 6 : }
100 :
101 : void
102 6 : nsDOMNavigationTiming::NotifyUnloadAccepted(nsIURI* aOldURI)
103 : {
104 6 : mUnloadStart = mBeforeUnloadStart;
105 6 : mUnloadedURI = aOldURI;
106 6 : }
107 :
108 : void
109 4 : nsDOMNavigationTiming::NotifyUnloadEventStart()
110 : {
111 4 : mUnloadStart = DurationFromStart();
112 4 : profiler_tracing("Navigation", "Unload", TRACING_INTERVAL_START);
113 4 : }
114 :
115 : void
116 4 : nsDOMNavigationTiming::NotifyUnloadEventEnd()
117 : {
118 4 : mUnloadEnd = DurationFromStart();
119 4 : profiler_tracing("Navigation", "Unload", TRACING_INTERVAL_END);
120 4 : }
121 :
122 : void
123 4 : nsDOMNavigationTiming::NotifyLoadEventStart()
124 : {
125 4 : if (!mLoadEventStartSet) {
126 4 : mLoadEventStart = DurationFromStart();
127 4 : mLoadEventStartSet = true;
128 :
129 4 : profiler_tracing("Navigation", "Load", TRACING_INTERVAL_START);
130 :
131 4 : if (IsTopLevelContentDocument()) {
132 1 : Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_LOAD_EVENT_START_MS,
133 1 : mNavigationStartTimeStamp);
134 : }
135 : }
136 4 : }
137 :
138 : void
139 4 : nsDOMNavigationTiming::NotifyLoadEventEnd()
140 : {
141 4 : if (!mLoadEventEndSet) {
142 4 : mLoadEventEnd = DurationFromStart();
143 4 : mLoadEventEndSet = true;
144 :
145 4 : profiler_tracing("Navigation", "Load", TRACING_INTERVAL_END);
146 :
147 4 : if (IsTopLevelContentDocument()) {
148 1 : Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_LOAD_EVENT_END_MS,
149 1 : mNavigationStartTimeStamp);
150 : }
151 : }
152 4 : }
153 :
154 : void
155 24 : nsDOMNavigationTiming::SetDOMLoadingTimeStamp(nsIURI* aURI, TimeStamp aValue)
156 : {
157 24 : if (!mDOMLoadingSet) {
158 24 : mLoadedURI = aURI;
159 24 : mDOMLoading = TimeStampToDOM(aValue);
160 24 : mDOMLoadingSet = true;
161 : }
162 24 : }
163 :
164 : void
165 0 : nsDOMNavigationTiming::NotifyDOMLoading(nsIURI* aURI)
166 : {
167 0 : if (!mDOMLoadingSet) {
168 0 : mLoadedURI = aURI;
169 0 : mDOMLoading = DurationFromStart();
170 0 : mDOMLoadingSet = true;
171 :
172 0 : profiler_add_marker("Navigation::DOMLoading");
173 :
174 0 : if (IsTopLevelContentDocument()) {
175 0 : Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_LOADING_MS,
176 0 : mNavigationStartTimeStamp);
177 : }
178 : }
179 0 : }
180 :
181 : void
182 24 : nsDOMNavigationTiming::NotifyDOMInteractive(nsIURI* aURI)
183 : {
184 24 : if (!mDOMInteractiveSet) {
185 24 : mLoadedURI = aURI;
186 24 : mDOMInteractive = DurationFromStart();
187 24 : mDOMInteractiveSet = true;
188 :
189 24 : profiler_add_marker("Navigation::DOMInteractive");
190 :
191 24 : if (IsTopLevelContentDocument()) {
192 1 : Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_INTERACTIVE_MS,
193 1 : mNavigationStartTimeStamp);
194 : }
195 : }
196 24 : }
197 :
198 : void
199 4 : nsDOMNavigationTiming::NotifyDOMComplete(nsIURI* aURI)
200 : {
201 4 : if (!mDOMCompleteSet) {
202 4 : mLoadedURI = aURI;
203 4 : mDOMComplete = DurationFromStart();
204 4 : mDOMCompleteSet = true;
205 :
206 4 : profiler_add_marker("Navigation::DOMComplete");
207 :
208 4 : if (IsTopLevelContentDocument()) {
209 1 : Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_COMPLETE_MS,
210 1 : mNavigationStartTimeStamp);
211 : }
212 : }
213 4 : }
214 :
215 : void
216 25 : nsDOMNavigationTiming::NotifyDOMContentLoadedStart(nsIURI* aURI)
217 : {
218 25 : if (!mDOMContentLoadedEventStartSet) {
219 25 : mLoadedURI = aURI;
220 25 : mDOMContentLoadedEventStart = DurationFromStart();
221 25 : mDOMContentLoadedEventStartSet = true;
222 :
223 25 : profiler_tracing("Navigation", "DOMContentLoaded", TRACING_INTERVAL_START);
224 :
225 25 : if (IsTopLevelContentDocument()) {
226 1 : Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_CONTENT_LOADED_START_MS,
227 1 : mNavigationStartTimeStamp);
228 : }
229 : }
230 25 : }
231 :
232 : void
233 25 : nsDOMNavigationTiming::NotifyDOMContentLoadedEnd(nsIURI* aURI)
234 : {
235 25 : if (!mDOMContentLoadedEventEndSet) {
236 25 : mLoadedURI = aURI;
237 25 : mDOMContentLoadedEventEnd = DurationFromStart();
238 25 : mDOMContentLoadedEventEndSet = true;
239 :
240 25 : profiler_tracing("Navigation", "DOMContentLoaded", TRACING_INTERVAL_END);
241 :
242 25 : if (IsTopLevelContentDocument()) {
243 1 : Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_DOM_CONTENT_LOADED_END_MS,
244 1 : mNavigationStartTimeStamp);
245 : }
246 : }
247 25 : }
248 :
249 : void
250 1 : nsDOMNavigationTiming::NotifyNonBlankPaintForRootContentDocument()
251 : {
252 1 : MOZ_ASSERT(NS_IsMainThread());
253 1 : MOZ_ASSERT(!mNavigationStartTimeStamp.IsNull());
254 :
255 1 : if (!mNonBlankPaintTimeStamp.IsNull()) {
256 0 : return;
257 : }
258 :
259 1 : mNonBlankPaintTimeStamp = TimeStamp::Now();
260 1 : TimeDuration elapsed = mNonBlankPaintTimeStamp - mNavigationStartTimeStamp;
261 :
262 1 : if (profiler_is_active()) {
263 0 : nsAutoCString spec;
264 0 : if (mLoadedURI) {
265 0 : mLoadedURI->GetSpec(spec);
266 : }
267 : nsPrintfCString marker("Non-blank paint after %dms for URL %s, %s",
268 0 : int(elapsed.ToMilliseconds()), spec.get(),
269 0 : mDocShellHasBeenActiveSinceNavigationStart ? "foreground tab" : "this tab was inactive some of the time between navigation start and first non-blank paint");
270 0 : profiler_add_marker(marker.get());
271 : }
272 :
273 1 : if (mDocShellHasBeenActiveSinceNavigationStart) {
274 : Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_NON_BLANK_PAINT_MS,
275 : mNavigationStartTimeStamp,
276 1 : mNonBlankPaintTimeStamp);
277 : }
278 : }
279 :
280 : void
281 0 : nsDOMNavigationTiming::NotifyDocShellStateChanged(DocShellState aDocShellState)
282 : {
283 0 : mDocShellHasBeenActiveSinceNavigationStart &=
284 0 : (aDocShellState == DocShellState::eActive);
285 0 : }
286 :
287 : DOMTimeMilliSec
288 0 : nsDOMNavigationTiming::GetUnloadEventStart()
289 : {
290 0 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
291 0 : nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false);
292 0 : if (NS_SUCCEEDED(rv)) {
293 0 : return mUnloadStart;
294 : }
295 0 : return 0;
296 : }
297 :
298 : DOMTimeMilliSec
299 0 : nsDOMNavigationTiming::GetUnloadEventEnd()
300 : {
301 0 : nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
302 0 : nsresult rv = ssm->CheckSameOriginURI(mLoadedURI, mUnloadedURI, false);
303 0 : if (NS_SUCCEEDED(rv)) {
304 0 : return mUnloadEnd;
305 : }
306 0 : return 0;
307 : }
308 :
309 : bool
310 86 : nsDOMNavigationTiming::IsTopLevelContentDocument() const
311 : {
312 86 : if (!mDocShell) {
313 63 : return false;
314 : }
315 46 : nsCOMPtr<nsIDocShellTreeItem> rootItem;
316 23 : Unused << mDocShell->GetSameTypeRootTreeItem(getter_AddRefs(rootItem));
317 23 : if (rootItem.get() != static_cast<nsIDocShellTreeItem*>(mDocShell.get())) {
318 6 : return false;
319 : }
320 17 : return rootItem->ItemType() == nsIDocShellTreeItem::typeContent;
321 : }
|