[Resource Timing] Gather timing information with reliable responseEnd time
[WebKit-https.git] / Source / WebCore / page / PerformanceResourceTiming.cpp
1 /*
2  * Copyright (C) 2017 Apple Inc. All rights reserved.
3  * Copyright (C) 2012 Google Inc. All rights reserved.
4  * Copyright (C) 2012 Intel Inc. All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are
8  * met:
9  *
10  *     * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *     * Redistributions in binary form must reproduce the above
13  * copyright notice, this list of conditions and the following disclaimer
14  * in the documentation and/or other materials provided with the
15  * distribution.
16  *     * Neither the name of Google Inc. nor the names of its
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32
33 #include "config.h"
34 #include "PerformanceResourceTiming.h"
35
36 #if ENABLE(WEB_TIMING)
37
38 #include "Document.h"
39 #include "DocumentLoader.h"
40 #include "LoadTiming.h"
41 #include "ResourceResponse.h"
42 #include "ResourceTiming.h"
43 #include "URL.h"
44
45 namespace WebCore {
46
47 static double monotonicTimeToDOMHighResTimeStamp(MonotonicTime timeOrigin, MonotonicTime timeStamp)
48 {
49     ASSERT(timeStamp.secondsSinceEpoch().seconds() >= 0);
50     if (!timeStamp || !timeOrigin)
51         return 0;
52
53     Seconds seconds = timeStamp - timeOrigin;
54     return Performance::reduceTimeResolution(seconds).milliseconds();
55 }
56
57 static double entryStartTime(MonotonicTime timeOrigin, const ResourceTiming& resourceTiming)
58 {
59     return monotonicTimeToDOMHighResTimeStamp(timeOrigin, resourceTiming.loadTiming().startTime());
60 }
61
62 static double entryEndTime(MonotonicTime timeOrigin, const ResourceTiming& resourceTiming)
63 {
64     if (resourceTiming.networkLoadMetrics().isComplete()) {
65         Seconds endTime = (resourceTiming.loadTiming().fetchStart() + resourceTiming.networkLoadMetrics().responseEnd) - timeOrigin;
66         return Performance::reduceTimeResolution(endTime).milliseconds();
67     }
68
69     return monotonicTimeToDOMHighResTimeStamp(timeOrigin, resourceTiming.loadTiming().responseEnd());
70 }
71
72 Ref<PerformanceResourceTiming> PerformanceResourceTiming::create(MonotonicTime timeOrigin, ResourceTiming&& resourceTiming)
73 {
74     return adoptRef(*new PerformanceResourceTiming(timeOrigin, WTFMove(resourceTiming)));
75 }
76
77 PerformanceResourceTiming::PerformanceResourceTiming(MonotonicTime timeOrigin, ResourceTiming&& resourceTiming)
78     : PerformanceEntry(PerformanceEntry::Type::Resource, resourceTiming.url().string(), ASCIILiteral("resource"), entryStartTime(timeOrigin, resourceTiming), entryEndTime(timeOrigin, resourceTiming))
79     , m_initiatorType(resourceTiming.initiator())
80     , m_timeOrigin(timeOrigin)
81     , m_loadTiming(resourceTiming.loadTiming())
82     , m_networkLoadMetrics(resourceTiming.networkLoadMetrics())
83     , m_shouldReportDetails(resourceTiming.allowTimingDetails())
84 {
85 }
86
87 PerformanceResourceTiming::~PerformanceResourceTiming()
88 {
89 }
90
91 String PerformanceResourceTiming::nextHopProtocol() const
92 {
93     return m_networkLoadMetrics.protocol;
94 }
95
96 double PerformanceResourceTiming::workerStart() const
97 {
98     return 0.0;
99 }
100
101 double PerformanceResourceTiming::redirectStart() const
102 {
103     if (!m_shouldReportDetails)
104         return 0.0;
105
106     return monotonicTimeToDOMHighResTimeStamp(m_timeOrigin, m_loadTiming.redirectStart());
107 }
108
109 double PerformanceResourceTiming::redirectEnd() const
110 {
111     if (!m_shouldReportDetails)
112         return 0.0;
113
114     return monotonicTimeToDOMHighResTimeStamp(m_timeOrigin, m_loadTiming.redirectEnd());
115 }
116
117 double PerformanceResourceTiming::fetchStart() const
118 {
119     // fetchStart is a required property.
120     ASSERT(m_loadTiming.fetchStart());
121
122     return monotonicTimeToDOMHighResTimeStamp(m_timeOrigin, m_loadTiming.fetchStart());
123 }
124
125 double PerformanceResourceTiming::domainLookupStart() const
126 {
127     if (!m_shouldReportDetails)
128         return 0.0;
129
130     if (m_networkLoadMetrics.domainLookupStart <= 0_ms)
131         return fetchStart();
132
133     return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.domainLookupStart);
134 }
135
136 double PerformanceResourceTiming::domainLookupEnd() const
137 {
138     if (!m_shouldReportDetails)
139         return 0.0;
140
141     if (m_networkLoadMetrics.domainLookupEnd <= 0_ms)
142         return domainLookupStart();
143
144     return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.domainLookupEnd);
145 }
146
147 double PerformanceResourceTiming::connectStart() const
148 {
149     if (!m_shouldReportDetails)
150         return 0.0;
151
152     if (m_networkLoadMetrics.connectStart <= 0_ms)
153         return domainLookupEnd();
154
155     return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.connectStart);
156 }
157
158 double PerformanceResourceTiming::connectEnd() const
159 {
160     if (!m_shouldReportDetails)
161         return 0.0;
162
163     if (m_networkLoadMetrics.connectEnd <= 0_ms)
164         return connectStart();
165
166     return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.connectEnd);
167 }
168
169 double PerformanceResourceTiming::secureConnectionStart() const
170 {
171     if (!m_shouldReportDetails)
172         return 0.0;
173
174     if (m_networkLoadMetrics.secureConnectionStart <= 0_ms)
175         return 0.0;
176
177     return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.secureConnectionStart);
178 }
179
180 double PerformanceResourceTiming::requestStart() const
181 {
182     if (!m_shouldReportDetails)
183         return 0.0;
184
185     // requestStart is 0 when a network request is not made.
186     if (m_networkLoadMetrics.requestStart <= 0_ms)
187         return connectEnd();
188
189     return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.requestStart);
190 }
191
192 double PerformanceResourceTiming::responseStart() const
193 {
194     if (!m_shouldReportDetails)
195         return 0.0;
196
197     // responseStart is 0 when a network request is not made.
198     if (m_networkLoadMetrics.responseStart <= 0_ms)
199         return requestStart();
200
201     return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.responseStart);
202 }
203
204 double PerformanceResourceTiming::responseEnd() const
205 {
206     // responseEnd is a required property.
207     ASSERT(m_networkLoadMetrics.isComplete() || m_loadTiming.responseEnd());
208
209     if (m_networkLoadMetrics.isComplete()) {
210         // responseEnd is 0 when a network request is not made.
211         // This should mean all other properties are empty.
212         if (m_networkLoadMetrics.responseEnd <= 0_ms) {
213             ASSERT(m_networkLoadMetrics.responseStart <= 0_ms);
214             ASSERT(m_networkLoadMetrics.requestStart <= 0_ms);
215             ASSERT(m_networkLoadMetrics.requestStart <= 0_ms);
216             ASSERT(m_networkLoadMetrics.secureConnectionStart <= 0_ms);
217             ASSERT(m_networkLoadMetrics.connectEnd <= 0_ms);
218             ASSERT(m_networkLoadMetrics.connectStart <= 0_ms);
219             ASSERT(m_networkLoadMetrics.domainLookupEnd <= 0_ms);
220             ASSERT(m_networkLoadMetrics.domainLookupStart <= 0_ms);
221             return fetchStart();
222         }
223
224         return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.responseEnd);
225     }
226
227     return monotonicTimeToDOMHighResTimeStamp(m_timeOrigin, m_loadTiming.responseEnd());
228 }
229
230 double PerformanceResourceTiming::networkLoadTimeToDOMHighResTimeStamp(Seconds delta) const
231 {
232     ASSERT(delta);
233     Seconds final = (m_loadTiming.fetchStart() + delta) - m_timeOrigin;
234     return Performance::reduceTimeResolution(final).milliseconds();
235 }
236
237 } // namespace WebCore
238
239 #endif // ENABLE(WEB_TIMING)