1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32#include "PerformanceTiming.h"
33
34#include "Document.h"
35#include "DocumentLoader.h"
36#include "DocumentTiming.h"
37#include "Frame.h"
38#include "FrameLoader.h"
39#include "LoadTiming.h"
40#include "NetworkLoadMetrics.h"
41#include "Performance.h"
42#include "ResourceResponse.h"
43
44namespace WebCore {
45
46PerformanceTiming::PerformanceTiming(DOMWindow* window)
47 : DOMWindowProperty(window)
48{
49}
50
51unsigned long long PerformanceTiming::navigationStart() const
52{
53 LoadTiming* timing = loadTiming();
54 if (!timing)
55 return 0;
56
57 return monotonicTimeToIntegerMilliseconds(timing->startTime());
58}
59
60unsigned long long PerformanceTiming::unloadEventStart() const
61{
62 LoadTiming* timing = loadTiming();
63 if (!timing)
64 return 0;
65
66 if (timing->hasCrossOriginRedirect() || !timing->hasSameOriginAsPreviousDocument())
67 return 0;
68
69 return monotonicTimeToIntegerMilliseconds(timing->unloadEventStart());
70}
71
72unsigned long long PerformanceTiming::unloadEventEnd() const
73{
74 LoadTiming* timing = loadTiming();
75 if (!timing)
76 return 0;
77
78 if (timing->hasCrossOriginRedirect() || !timing->hasSameOriginAsPreviousDocument())
79 return 0;
80
81 return monotonicTimeToIntegerMilliseconds(timing->unloadEventEnd());
82}
83
84unsigned long long PerformanceTiming::redirectStart() const
85{
86 LoadTiming* timing = loadTiming();
87 if (!timing)
88 return 0;
89
90 if (timing->hasCrossOriginRedirect())
91 return 0;
92
93 return monotonicTimeToIntegerMilliseconds(timing->redirectStart());
94}
95
96unsigned long long PerformanceTiming::redirectEnd() const
97{
98 LoadTiming* timing = loadTiming();
99 if (!timing)
100 return 0;
101
102 if (timing->hasCrossOriginRedirect())
103 return 0;
104
105 return monotonicTimeToIntegerMilliseconds(timing->redirectEnd());
106}
107
108unsigned long long PerformanceTiming::fetchStart() const
109{
110 LoadTiming* timing = loadTiming();
111 if (!timing)
112 return 0;
113
114 return monotonicTimeToIntegerMilliseconds(timing->fetchStart());
115}
116
117unsigned long long PerformanceTiming::domainLookupStart() const
118{
119 DocumentLoader* loader = documentLoader();
120 if (!loader)
121 return fetchStart();
122
123 const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics();
124
125 // This will be -1 when a DNS request is not performed.
126 // Rather than exposing a special value that indicates no DNS, we "backfill" with fetchStart.
127 if (timing.domainLookupStart < 0_ms)
128 return fetchStart();
129
130 return resourceLoadTimeRelativeToFetchStart(timing.domainLookupStart);
131}
132
133unsigned long long PerformanceTiming::domainLookupEnd() const
134{
135 DocumentLoader* loader = documentLoader();
136 if (!loader)
137 return domainLookupStart();
138
139 const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics();
140
141 // This will be -1 when a DNS request is not performed.
142 // Rather than exposing a special value that indicates no DNS, we "backfill" with domainLookupStart.
143 if (timing.domainLookupEnd < 0_ms)
144 return domainLookupStart();
145
146 return resourceLoadTimeRelativeToFetchStart(timing.domainLookupEnd);
147}
148
149unsigned long long PerformanceTiming::connectStart() const
150{
151 DocumentLoader* loader = documentLoader();
152 if (!loader)
153 return domainLookupEnd();
154
155 const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics();
156
157 // connectStart will be -1 when a network request is not made.
158 // Rather than exposing a special value that indicates no new connection, we "backfill" with domainLookupEnd.
159 Seconds connectStart = timing.connectStart;
160 if (connectStart < 0_ms)
161 return domainLookupEnd();
162
163 // NetworkLoadMetrics's connect phase includes DNS, however Navigation Timing's
164 // connect phase should not. So if there is DNS time, trim it from the start.
165 if (timing.domainLookupEnd >= 0_ms && timing.domainLookupEnd > connectStart)
166 connectStart = timing.domainLookupEnd;
167
168 return resourceLoadTimeRelativeToFetchStart(connectStart);
169}
170
171unsigned long long PerformanceTiming::connectEnd() const
172{
173 DocumentLoader* loader = documentLoader();
174 if (!loader)
175 return connectStart();
176
177 const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics();
178
179 // connectEnd will be -1 when a network request is not made.
180 // Rather than exposing a special value that indicates no new connection, we "backfill" with connectStart.
181 if (timing.connectEnd < 0_ms)
182 return connectStart();
183
184 return resourceLoadTimeRelativeToFetchStart(timing.connectEnd);
185}
186
187unsigned long long PerformanceTiming::secureConnectionStart() const
188{
189 DocumentLoader* loader = documentLoader();
190 if (!loader)
191 return 0;
192
193 const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics();
194
195 if (timing.secureConnectionStart < 0_ms)
196 return 0;
197
198 return resourceLoadTimeRelativeToFetchStart(timing.secureConnectionStart);
199}
200
201unsigned long long PerformanceTiming::requestStart() const
202{
203 DocumentLoader* loader = documentLoader();
204 if (!loader)
205 return connectEnd();
206
207 const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics();
208
209 ASSERT(timing.requestStart >= 0_ms);
210 return resourceLoadTimeRelativeToFetchStart(timing.requestStart);
211}
212
213unsigned long long PerformanceTiming::responseStart() const
214{
215 DocumentLoader* loader = documentLoader();
216 if (!loader)
217 return requestStart();
218
219 const NetworkLoadMetrics& timing = loader->response().deprecatedNetworkLoadMetrics();
220
221 ASSERT(timing.responseStart >= 0_ms);
222 return resourceLoadTimeRelativeToFetchStart(timing.responseStart);
223}
224
225unsigned long long PerformanceTiming::responseEnd() const
226{
227 LoadTiming* timing = loadTiming();
228 if (!timing)
229 return 0;
230
231 return monotonicTimeToIntegerMilliseconds(timing->responseEnd());
232}
233
234unsigned long long PerformanceTiming::domLoading() const
235{
236 const DocumentTiming* timing = documentTiming();
237 if (!timing)
238 return fetchStart();
239
240 return monotonicTimeToIntegerMilliseconds(timing->domLoading);
241}
242
243unsigned long long PerformanceTiming::domInteractive() const
244{
245 const DocumentTiming* timing = documentTiming();
246 if (!timing)
247 return 0;
248
249 return monotonicTimeToIntegerMilliseconds(timing->domInteractive);
250}
251
252unsigned long long PerformanceTiming::domContentLoadedEventStart() const
253{
254 const DocumentTiming* timing = documentTiming();
255 if (!timing)
256 return 0;
257
258 return monotonicTimeToIntegerMilliseconds(timing->domContentLoadedEventStart);
259}
260
261unsigned long long PerformanceTiming::domContentLoadedEventEnd() const
262{
263 const DocumentTiming* timing = documentTiming();
264 if (!timing)
265 return 0;
266
267 return monotonicTimeToIntegerMilliseconds(timing->domContentLoadedEventEnd);
268}
269
270unsigned long long PerformanceTiming::domComplete() const
271{
272 const DocumentTiming* timing = documentTiming();
273 if (!timing)
274 return 0;
275
276 return monotonicTimeToIntegerMilliseconds(timing->domComplete);
277}
278
279unsigned long long PerformanceTiming::loadEventStart() const
280{
281 LoadTiming* timing = loadTiming();
282 if (!timing)
283 return 0;
284
285 return monotonicTimeToIntegerMilliseconds(timing->loadEventStart());
286}
287
288unsigned long long PerformanceTiming::loadEventEnd() const
289{
290 LoadTiming* timing = loadTiming();
291 if (!timing)
292 return 0;
293
294 return monotonicTimeToIntegerMilliseconds(timing->loadEventEnd());
295}
296
297DocumentLoader* PerformanceTiming::documentLoader() const
298{
299 auto* frame = this->frame();
300 if (!frame)
301 return nullptr;
302
303 return frame->loader().documentLoader();
304}
305
306const DocumentTiming* PerformanceTiming::documentTiming() const
307{
308 auto* frame = this->frame();
309 if (!frame)
310 return nullptr;
311
312 Document* document = frame->document();
313 if (!document)
314 return nullptr;
315
316 return &document->timing();
317}
318
319LoadTiming* PerformanceTiming::loadTiming() const
320{
321 DocumentLoader* loader = documentLoader();
322 if (!loader)
323 return nullptr;
324
325 return &loader->timing();
326}
327
328unsigned long long PerformanceTiming::resourceLoadTimeRelativeToFetchStart(Seconds delta) const
329{
330 ASSERT(delta >= 0_ms);
331
332 LoadTiming* timing = loadTiming();
333 if (!timing)
334 return 0;
335
336 WallTime fetchStart = timing->monotonicTimeToPseudoWallTime(timing->fetchStart());
337 WallTime combined = fetchStart + delta;
338 Seconds reduced = Performance::reduceTimeResolution(combined.secondsSinceEpoch());
339 return static_cast<unsigned long long>(reduced.milliseconds());
340}
341
342unsigned long long PerformanceTiming::monotonicTimeToIntegerMilliseconds(MonotonicTime timeStamp) const
343{
344 ASSERT(timeStamp.secondsSinceEpoch().seconds() >= 0);
345
346 LoadTiming* timing = loadTiming();
347 if (!timing)
348 return 0;
349
350 WallTime wallTime = timing->monotonicTimeToPseudoWallTime(timeStamp);
351 Seconds reduced = Performance::reduceTimeResolution(wallTime.secondsSinceEpoch());
352 return static_cast<unsigned long long>(reduced.milliseconds());
353}
354
355} // namespace WebCore
356