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#include "Document.h"
37#include "DocumentLoader.h"
38#include "LoadTiming.h"
39#include "PerformanceServerTiming.h"
40#include "ResourceResponse.h"
41#include "ResourceTiming.h"
42#include <wtf/URL.h>
43
44namespace WebCore {
45
46static double monotonicTimeToDOMHighResTimeStamp(MonotonicTime timeOrigin, MonotonicTime timeStamp)
47{
48 ASSERT(timeStamp.secondsSinceEpoch().seconds() >= 0);
49 if (!timeStamp || !timeOrigin)
50 return 0;
51
52 Seconds seconds = timeStamp - timeOrigin;
53 return Performance::reduceTimeResolution(seconds).milliseconds();
54}
55
56static double entryStartTime(MonotonicTime timeOrigin, const ResourceTiming& resourceTiming)
57{
58 if (!resourceTiming.allowTimingDetails())
59 return monotonicTimeToDOMHighResTimeStamp(timeOrigin, resourceTiming.loadTiming().fetchStart());
60
61 return monotonicTimeToDOMHighResTimeStamp(timeOrigin, resourceTiming.loadTiming().startTime());
62}
63
64static double entryEndTime(MonotonicTime timeOrigin, const ResourceTiming& resourceTiming)
65{
66 if (!resourceTiming.allowTimingDetails())
67 return entryStartTime(timeOrigin, resourceTiming);
68
69 if (resourceTiming.networkLoadMetrics().isComplete()) {
70 Seconds endTime = (resourceTiming.loadTiming().fetchStart() + resourceTiming.networkLoadMetrics().responseEnd) - timeOrigin;
71 return Performance::reduceTimeResolution(endTime).milliseconds();
72 }
73
74 return monotonicTimeToDOMHighResTimeStamp(timeOrigin, resourceTiming.loadTiming().responseEnd());
75}
76
77Ref<PerformanceResourceTiming> PerformanceResourceTiming::create(MonotonicTime timeOrigin, ResourceTiming&& resourceTiming)
78{
79 return adoptRef(*new PerformanceResourceTiming(timeOrigin, WTFMove(resourceTiming)));
80}
81
82PerformanceResourceTiming::PerformanceResourceTiming(MonotonicTime timeOrigin, ResourceTiming&& resourceTiming)
83 : PerformanceEntry(PerformanceEntry::Type::Resource, resourceTiming.url().string(), "resource"_s, entryStartTime(timeOrigin, resourceTiming), entryEndTime(timeOrigin, resourceTiming))
84 , m_initiatorType(resourceTiming.initiator())
85 , m_timeOrigin(timeOrigin)
86 , m_loadTiming(resourceTiming.loadTiming())
87 , m_networkLoadMetrics(resourceTiming.networkLoadMetrics())
88 , m_shouldReportDetails(resourceTiming.allowTimingDetails())
89 , m_serverTiming(resourceTiming.populateServerTiming())
90{
91 m_networkLoadMetrics.clearNonTimingData();
92}
93
94PerformanceResourceTiming::~PerformanceResourceTiming() = default;
95
96String PerformanceResourceTiming::nextHopProtocol() const
97{
98 return m_networkLoadMetrics.protocol;
99}
100
101double PerformanceResourceTiming::workerStart() const
102{
103 // FIXME: <https://webkit.org/b/179377> Implement PerformanceResourceTiming.workerStart in ServiceWorkers
104 return 0.0;
105}
106
107double PerformanceResourceTiming::redirectStart() const
108{
109 if (!m_shouldReportDetails)
110 return 0.0;
111
112 return monotonicTimeToDOMHighResTimeStamp(m_timeOrigin, m_loadTiming.redirectStart());
113}
114
115double PerformanceResourceTiming::redirectEnd() const
116{
117 if (!m_shouldReportDetails)
118 return 0.0;
119
120 return monotonicTimeToDOMHighResTimeStamp(m_timeOrigin, m_loadTiming.redirectEnd());
121}
122
123double PerformanceResourceTiming::fetchStart() const
124{
125 // fetchStart is a required property.
126 ASSERT(m_loadTiming.fetchStart());
127
128 return monotonicTimeToDOMHighResTimeStamp(m_timeOrigin, m_loadTiming.fetchStart());
129}
130
131double PerformanceResourceTiming::domainLookupStart() const
132{
133 if (!m_shouldReportDetails)
134 return 0.0;
135
136 if (m_networkLoadMetrics.domainLookupStart <= 0_ms)
137 return fetchStart();
138
139 return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.domainLookupStart);
140}
141
142double PerformanceResourceTiming::domainLookupEnd() const
143{
144 if (!m_shouldReportDetails)
145 return 0.0;
146
147 if (m_networkLoadMetrics.domainLookupEnd <= 0_ms)
148 return domainLookupStart();
149
150 return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.domainLookupEnd);
151}
152
153double PerformanceResourceTiming::connectStart() const
154{
155 if (!m_shouldReportDetails)
156 return 0.0;
157
158 if (m_networkLoadMetrics.connectStart <= 0_ms)
159 return domainLookupEnd();
160
161 return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.connectStart);
162}
163
164double PerformanceResourceTiming::connectEnd() const
165{
166 if (!m_shouldReportDetails)
167 return 0.0;
168
169 if (m_networkLoadMetrics.connectEnd <= 0_ms)
170 return connectStart();
171
172 return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.connectEnd);
173}
174
175double PerformanceResourceTiming::secureConnectionStart() const
176{
177 if (!m_shouldReportDetails)
178 return 0.0;
179
180 if (m_networkLoadMetrics.secureConnectionStart <= 0_ms)
181 return 0.0;
182
183 return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.secureConnectionStart);
184}
185
186double PerformanceResourceTiming::requestStart() const
187{
188 if (!m_shouldReportDetails)
189 return 0.0;
190
191 // requestStart is 0 when a network request is not made.
192 if (m_networkLoadMetrics.requestStart <= 0_ms)
193 return connectEnd();
194
195 return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.requestStart);
196}
197
198double PerformanceResourceTiming::responseStart() const
199{
200 if (!m_shouldReportDetails)
201 return 0.0;
202
203 // responseStart is 0 when a network request is not made.
204 if (m_networkLoadMetrics.responseStart <= 0_ms)
205 return requestStart();
206
207 return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.responseStart);
208}
209
210double PerformanceResourceTiming::responseEnd() const
211{
212 // responseEnd is a required property.
213 ASSERT(m_networkLoadMetrics.isComplete() || m_loadTiming.responseEnd());
214
215 if (m_networkLoadMetrics.isComplete()) {
216 // responseEnd is 0 when a network request is not made.
217 // This should mean all other properties are empty.
218 if (m_networkLoadMetrics.responseEnd <= 0_ms) {
219 ASSERT(m_networkLoadMetrics.responseStart <= 0_ms);
220 ASSERT(m_networkLoadMetrics.requestStart <= 0_ms);
221 ASSERT(m_networkLoadMetrics.requestStart <= 0_ms);
222 ASSERT(m_networkLoadMetrics.secureConnectionStart <= 0_ms);
223 ASSERT(m_networkLoadMetrics.connectEnd <= 0_ms);
224 ASSERT(m_networkLoadMetrics.connectStart <= 0_ms);
225 ASSERT(m_networkLoadMetrics.domainLookupEnd <= 0_ms);
226 ASSERT(m_networkLoadMetrics.domainLookupStart <= 0_ms);
227 return fetchStart();
228 }
229
230 return networkLoadTimeToDOMHighResTimeStamp(m_networkLoadMetrics.responseEnd);
231 }
232
233 return monotonicTimeToDOMHighResTimeStamp(m_timeOrigin, m_loadTiming.responseEnd());
234}
235
236double PerformanceResourceTiming::networkLoadTimeToDOMHighResTimeStamp(Seconds delta) const
237{
238 ASSERT(delta);
239 Seconds final = (m_loadTiming.fetchStart() + delta) - m_timeOrigin;
240 return Performance::reduceTimeResolution(final).milliseconds();
241}
242
243} // namespace WebCore
244