• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #if ENABLE(WEB_TIMING)
35 
36 #include "DocumentLoadTiming.h"
37 #include "DocumentLoader.h"
38 #include "DocumentTiming.h"
39 #include "Frame.h"
40 #include "ResourceLoadTiming.h"
41 #include "ResourceResponse.h"
42 #include <wtf/CurrentTime.h>
43 
44 namespace WebCore {
45 
toIntegerMilliseconds(double seconds)46 static unsigned long long toIntegerMilliseconds(double seconds)
47 {
48     ASSERT(seconds >= 0);
49     return static_cast<unsigned long long>(seconds * 1000.0);
50 }
51 
getPossiblySkewedTimeInKnownRange(double skewedTime,double lowerBound,double upperBound)52 static double getPossiblySkewedTimeInKnownRange(double skewedTime, double lowerBound, double upperBound)
53 {
54 #if PLATFORM(CHROMIUM)
55     // The chromium port's currentTime() implementation only syncs with the
56     // system clock every 60 seconds. So it is possible for timing marks
57     // collected in different threads or processes to have a small skew.
58     // FIXME: It may be possible to add a currentTimeFromSystemTime() method
59     // that eliminates the skew.
60     if (skewedTime <= lowerBound)
61         return lowerBound;
62 
63     if (upperBound <= 0.0)
64         upperBound = currentTime();
65 
66     if (skewedTime >= upperBound)
67         return upperBound;
68 #else
69     ASSERT_UNUSED(lowerBound, skewedTime >= lowerBound);
70     ASSERT_UNUSED(upperBound, skewedTime <= upperBound);
71 #endif
72 
73     return skewedTime;
74 }
75 
PerformanceTiming(Frame * frame)76 PerformanceTiming::PerformanceTiming(Frame* frame)
77     : m_frame(frame)
78 {
79 }
80 
frame() const81 Frame* PerformanceTiming::frame() const
82 {
83     return m_frame;
84 }
85 
disconnectFrame()86 void PerformanceTiming::disconnectFrame()
87 {
88     m_frame = 0;
89 }
90 
navigationStart() const91 unsigned long long PerformanceTiming::navigationStart() const
92 {
93     DocumentLoadTiming* timing = documentLoadTiming();
94     if (!timing)
95         return 0;
96 
97     return toIntegerMilliseconds(timing->navigationStart);
98 }
99 
unloadEventStart() const100 unsigned long long PerformanceTiming::unloadEventStart() const
101 {
102     DocumentLoadTiming* timing = documentLoadTiming();
103     if (!timing)
104         return 0;
105 
106     if (timing->hasCrossOriginRedirect || !timing->hasSameOriginAsPreviousDocument)
107         return 0;
108 
109     return toIntegerMilliseconds(timing->unloadEventStart);
110 }
111 
unloadEventEnd() const112 unsigned long long PerformanceTiming::unloadEventEnd() const
113 {
114     DocumentLoadTiming* timing = documentLoadTiming();
115     if (!timing)
116         return 0;
117 
118     if (timing->hasCrossOriginRedirect || !timing->hasSameOriginAsPreviousDocument)
119         return 0;
120 
121     return toIntegerMilliseconds(timing->unloadEventEnd);
122 }
123 
redirectStart() const124 unsigned long long PerformanceTiming::redirectStart() const
125 {
126     DocumentLoadTiming* timing = documentLoadTiming();
127     if (!timing)
128         return 0;
129 
130     if (timing->hasCrossOriginRedirect)
131         return 0;
132 
133     return toIntegerMilliseconds(timing->redirectStart);
134 }
135 
redirectEnd() const136 unsigned long long PerformanceTiming::redirectEnd() const
137 {
138     DocumentLoadTiming* timing = documentLoadTiming();
139     if (!timing)
140         return 0;
141 
142     if (timing->hasCrossOriginRedirect)
143         return 0;
144 
145     return toIntegerMilliseconds(timing->redirectEnd);
146 }
147 
fetchStart() const148 unsigned long long PerformanceTiming::fetchStart() const
149 {
150     DocumentLoadTiming* timing = documentLoadTiming();
151     if (!timing)
152         return 0;
153 
154     return toIntegerMilliseconds(timing->fetchStart);
155 }
156 
domainLookupStart() const157 unsigned long long PerformanceTiming::domainLookupStart() const
158 {
159     ResourceLoadTiming* timing = resourceLoadTiming();
160     if (!timing)
161         return fetchStart();
162 
163     // This will be -1 when a DNS request is not performed.
164     // Rather than exposing a special value that indicates no DNS, we "backfill" with fetchStart.
165     int dnsStart = timing->dnsStart;
166     if (dnsStart < 0)
167         return fetchStart();
168 
169     return resourceLoadTimeRelativeToAbsolute(dnsStart);
170 }
171 
domainLookupEnd() const172 unsigned long long PerformanceTiming::domainLookupEnd() const
173 {
174     ResourceLoadTiming* timing = resourceLoadTiming();
175     if (!timing)
176         return domainLookupStart();
177 
178     // This will be -1 when a DNS request is not performed.
179     // Rather than exposing a special value that indicates no DNS, we "backfill" with domainLookupStart.
180     int dnsEnd = timing->dnsEnd;
181     if (dnsEnd < 0)
182         return domainLookupStart();
183 
184     return resourceLoadTimeRelativeToAbsolute(dnsEnd);
185 }
186 
connectStart() const187 unsigned long long PerformanceTiming::connectStart() const
188 {
189     DocumentLoader* loader = documentLoader();
190     if (!loader)
191         return domainLookupEnd();
192 
193     ResourceLoadTiming* timing = loader->response().resourceLoadTiming();
194     if (!timing)
195         return domainLookupEnd();
196 
197     // connectStart will be -1 when a network request is not made.
198     // Rather than exposing a special value that indicates no new connection, we "backfill" with domainLookupEnd.
199     int connectStart = timing->connectStart;
200     if (connectStart < 0 || loader->response().connectionReused())
201         return domainLookupEnd();
202 
203     // ResourceLoadTiming's connect phase includes DNS, however Navigation Timing's
204     // connect phase should not. So if there is DNS time, trim it from the start.
205     if (timing->dnsEnd >= 0 && timing->dnsEnd > connectStart)
206         connectStart = timing->dnsEnd;
207 
208     return resourceLoadTimeRelativeToAbsolute(connectStart);
209 }
210 
connectEnd() const211 unsigned long long PerformanceTiming::connectEnd() const
212 {
213     DocumentLoader* loader = documentLoader();
214     if (!loader)
215         return connectStart();
216 
217     ResourceLoadTiming* timing = loader->response().resourceLoadTiming();
218     if (!timing)
219         return connectStart();
220 
221     // connectEnd will be -1 when a network request is not made.
222     // Rather than exposing a special value that indicates no new connection, we "backfill" with connectStart.
223     int connectEnd = timing->connectEnd;
224     if (connectEnd < 0 || loader->response().connectionReused())
225         return connectStart();
226 
227     return resourceLoadTimeRelativeToAbsolute(connectEnd);
228 }
229 
secureConnectionStart() const230 unsigned long long PerformanceTiming::secureConnectionStart() const
231 {
232     DocumentLoader* loader = documentLoader();
233     if (!loader)
234         return 0;
235 
236     ResourceLoadTiming* timing = loader->response().resourceLoadTiming();
237     if (!timing)
238         return 0;
239 
240     int sslStart = timing->sslStart;
241     if (sslStart < 0)
242         return 0;
243 
244     return resourceLoadTimeRelativeToAbsolute(sslStart);
245 }
246 
requestStart() const247 unsigned long long PerformanceTiming::requestStart() const
248 {
249     ResourceLoadTiming* timing = resourceLoadTiming();
250     if (!timing)
251         return connectEnd();
252 
253     ASSERT(timing->sendStart >= 0);
254     return resourceLoadTimeRelativeToAbsolute(timing->sendStart);
255 }
256 
responseStart() const257 unsigned long long PerformanceTiming::responseStart() const
258 {
259     ResourceLoadTiming* timing = resourceLoadTiming();
260     if (!timing)
261         return requestStart();
262 
263     // FIXME: Response start needs to be the time of the first received byte.
264     // However, the ResourceLoadTiming API currently only supports the time
265     // the last header byte was received. For many responses with reasonable
266     // sized cookies, the HTTP headers fit into a single packet so this time
267     // is basically equivalent. But for some responses, particularly those with
268     // headers larger than a single packet, this time will be too late.
269     ASSERT(timing->receiveHeadersEnd >= 0);
270     return resourceLoadTimeRelativeToAbsolute(timing->receiveHeadersEnd);
271 }
272 
responseEnd() const273 unsigned long long PerformanceTiming::responseEnd() const
274 {
275     DocumentLoadTiming* timing = documentLoadTiming();
276     if (!timing)
277         return 0;
278 
279     return toIntegerMilliseconds(timing->responseEnd);
280 }
281 
domLoading() const282 unsigned long long PerformanceTiming::domLoading() const
283 {
284     const DocumentTiming* timing = documentTiming();
285     if (!timing)
286         return fetchStart();
287 
288     return toIntegerMilliseconds(timing->domLoading);
289 }
290 
domInteractive() const291 unsigned long long PerformanceTiming::domInteractive() const
292 {
293     const DocumentTiming* timing = documentTiming();
294     if (!timing)
295         return 0;
296 
297     return toIntegerMilliseconds(timing->domInteractive);
298 }
299 
domContentLoadedEventStart() const300 unsigned long long PerformanceTiming::domContentLoadedEventStart() const
301 {
302     const DocumentTiming* timing = documentTiming();
303     if (!timing)
304         return 0;
305 
306     return toIntegerMilliseconds(timing->domContentLoadedEventStart);
307 }
308 
domContentLoadedEventEnd() const309 unsigned long long PerformanceTiming::domContentLoadedEventEnd() const
310 {
311     const DocumentTiming* timing = documentTiming();
312     if (!timing)
313         return 0;
314 
315     return toIntegerMilliseconds(timing->domContentLoadedEventEnd);
316 }
317 
domComplete() const318 unsigned long long PerformanceTiming::domComplete() const
319 {
320     const DocumentTiming* timing = documentTiming();
321     if (!timing)
322         return 0;
323 
324     return toIntegerMilliseconds(timing->domComplete);
325 }
326 
loadEventStart() const327 unsigned long long PerformanceTiming::loadEventStart() const
328 {
329     DocumentLoadTiming* timing = documentLoadTiming();
330     if (!timing)
331         return 0;
332 
333     return toIntegerMilliseconds(timing->loadEventStart);
334 }
335 
loadEventEnd() const336 unsigned long long PerformanceTiming::loadEventEnd() const
337 {
338     DocumentLoadTiming* timing = documentLoadTiming();
339     if (!timing)
340         return 0;
341 
342     return toIntegerMilliseconds(timing->loadEventEnd);
343 }
344 
documentLoader() const345 DocumentLoader* PerformanceTiming::documentLoader() const
346 {
347     if (!m_frame)
348         return 0;
349 
350     return m_frame->loader()->documentLoader();
351 }
352 
documentTiming() const353 const DocumentTiming* PerformanceTiming::documentTiming() const
354 {
355     if (!m_frame)
356         return 0;
357 
358     Document* document = m_frame->document();
359     if (!document)
360         return 0;
361 
362     return document->timing();
363 }
364 
documentLoadTiming() const365 DocumentLoadTiming* PerformanceTiming::documentLoadTiming() const
366 {
367     DocumentLoader* loader = documentLoader();
368     if (!loader)
369         return 0;
370 
371     return loader->timing();
372 }
373 
resourceLoadTiming() const374 ResourceLoadTiming* PerformanceTiming::resourceLoadTiming() const
375 {
376     DocumentLoader* loader = documentLoader();
377     if (!loader)
378         return 0;
379 
380     return loader->response().resourceLoadTiming();
381 }
382 
resourceLoadTimeRelativeToAbsolute(int relativeSeconds) const383 unsigned long long PerformanceTiming::resourceLoadTimeRelativeToAbsolute(int relativeSeconds) const
384 {
385     ASSERT(relativeSeconds >= 0);
386     ResourceLoadTiming* resourceTiming = resourceLoadTiming();
387     ASSERT(resourceTiming);
388     DocumentLoadTiming* documentTiming = documentLoadTiming();
389     ASSERT(documentTiming);
390 
391     // The ResourceLoadTiming API's requestTime is the base time to which all
392     // other marks are relative. So to get an absolute time, we must add it to
393     // the relative marks.
394     //
395     // Since ResourceLoadTimings came from the network platform layer, we must
396     // check them for skew because they may be from another thread/process.
397     double baseTime = getPossiblySkewedTimeInKnownRange(resourceTiming->requestTime, documentTiming->fetchStart, documentTiming->responseEnd);
398     return toIntegerMilliseconds(baseTime) + relativeSeconds;
399 }
400 
401 } // namespace WebCore
402 
403 #endif // ENABLE(WEB_TIMING)
404