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