Non-unified build fixes, late September 2020 edition
[WebKit-https.git] / Source / WebCore / page / PerformanceUserTiming.cpp
1 /*
2  * Copyright (C) 2012 Intel Inc. All rights reserved.
3  * Copyright (C) 2017 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include "config.h"
28 #include "PerformanceUserTiming.h"
29
30 #include "Document.h"
31 #include "MessagePort.h"
32 #include "PerformanceMarkOptions.h"
33 #include "PerformanceMeasureOptions.h"
34 #include "PerformanceTiming.h"
35 #include "SerializedScriptValue.h"
36 #include <JavaScriptCore/JSCJSValueInlines.h>
37 #include <wtf/NeverDestroyed.h>
38
39 namespace WebCore {
40
41 using NavigationTimingFunction = unsigned long long (PerformanceTiming::*)() const;
42 static const HashMap<String, NavigationTimingFunction>& restrictedMarkNamesToNavigationTimingFunctionMap()
43 {
44     ASSERT(isMainThread());
45
46     static auto map = makeNeverDestroyed<HashMap<String, NavigationTimingFunction>>({
47         { "connectEnd"_s, &PerformanceTiming::connectEnd },
48         { "connectStart"_s, &PerformanceTiming::connectStart },
49         { "domComplete"_s, &PerformanceTiming::domComplete },
50         { "domContentLoadedEventEnd"_s, &PerformanceTiming::domContentLoadedEventEnd },
51         { "domContentLoadedEventStart"_s, &PerformanceTiming::domContentLoadedEventStart },
52         { "domInteractive"_s, &PerformanceTiming::domInteractive },
53         { "domLoading"_s, &PerformanceTiming::domLoading },
54         { "domainLookupEnd"_s, &PerformanceTiming::domainLookupEnd },
55         { "domainLookupStart"_s, &PerformanceTiming::domainLookupStart },
56         { "fetchStart"_s, &PerformanceTiming::fetchStart },
57         { "loadEventEnd"_s, &PerformanceTiming::loadEventEnd },
58         { "loadEventStart"_s, &PerformanceTiming::loadEventStart },
59         { "navigationStart"_s, &PerformanceTiming::navigationStart },
60         { "redirectEnd"_s, &PerformanceTiming::redirectEnd },
61         { "redirectStart"_s, &PerformanceTiming::redirectStart },
62         { "requestStart"_s, &PerformanceTiming::requestStart },
63         { "responseEnd"_s, &PerformanceTiming::responseEnd },
64         { "responseStart"_s, &PerformanceTiming::responseStart },
65         { "secureConnectionStart"_s, &PerformanceTiming::secureConnectionStart },
66         { "unloadEventEnd"_s, &PerformanceTiming::unloadEventEnd },
67         { "unloadEventStart"_s, &PerformanceTiming::unloadEventStart },
68     });
69     
70     return map;
71 }
72
73 static NavigationTimingFunction restrictedMarkFunction(const String& markName)
74 {
75     ASSERT(isMainThread());
76     return restrictedMarkNamesToNavigationTimingFunctionMap().get(markName);
77 }
78
79 static bool isRestrictedMarkNameNonMainThread(const String& markName)
80 {
81     ASSERT(!isMainThread());
82
83     bool isRestricted;
84     callOnMainThreadAndWait([&isRestricted, markName = markName.isolatedCopy()] {
85         isRestricted = restrictedMarkNamesToNavigationTimingFunctionMap().contains(markName);
86     });
87     return isRestricted;
88 }
89
90 bool PerformanceUserTiming::isRestrictedMarkName(const String& markName)
91 {
92     ASSERT(isMainThread());
93     return restrictedMarkNamesToNavigationTimingFunctionMap().contains(markName);
94 }
95
96 PerformanceUserTiming::PerformanceUserTiming(Performance& performance)
97     : m_performance(performance)
98 {
99 }
100
101 static void clearPerformanceEntries(PerformanceEntryMap& map, const String& name)
102 {
103     if (name.isNull())
104         map.clear();
105     else
106         map.remove(name);
107 }
108
109 static void addPerformanceEntry(PerformanceEntryMap& map, const String& name, PerformanceEntry& entry)
110 {
111     auto& performanceEntryList = map.ensure(name, [] { return Vector<RefPtr<PerformanceEntry>>(); }).iterator->value;
112     performanceEntryList.append(&entry);
113 }
114
115 ExceptionOr<Ref<PerformanceMark>> PerformanceUserTiming::mark(JSC::JSGlobalObject& globalObject, const String& markName, Optional<PerformanceMarkOptions>&& markOptions)
116 {
117     auto mark = PerformanceMark::create(globalObject, *m_performance.scriptExecutionContext(), markName, WTFMove(markOptions));
118     if (mark.hasException())
119         return mark.releaseException();
120
121     addPerformanceEntry(m_marksMap, markName, mark.returnValue().get());
122     return mark.releaseReturnValue();
123 }
124
125 void PerformanceUserTiming::clearMarks(const String& markName)
126 {
127     clearPerformanceEntries(m_marksMap, markName);
128 }
129
130 ExceptionOr<double> PerformanceUserTiming::convertMarkToTimestamp(const Variant<String, double>& mark) const
131 {
132     return WTF::switchOn(mark, [&](auto& value) {
133         return convertMarkToTimestamp(value);
134     });
135 }
136
137 ExceptionOr<double> PerformanceUserTiming::convertMarkToTimestamp(const String& mark) const
138 {
139     if (!is<Document>(m_performance.scriptExecutionContext())) {
140         if (isRestrictedMarkNameNonMainThread(mark))
141             return Exception { TypeError };
142     } else {
143         if (auto function = restrictedMarkFunction(mark)) {
144             if (function == &PerformanceTiming::navigationStart)
145                 return 0.0;
146
147             // PerformanceTiming should always be non-null for the Document ScriptExecutionContext.
148             ASSERT(m_performance.timing());
149             auto timing = m_performance.timing();
150             auto startTime = timing->navigationStart();
151             auto endTime = ((*timing).*(function))();
152             if (!endTime)
153                 return Exception { InvalidAccessError };
154             return endTime - startTime;
155         }
156     }
157
158     auto iterator = m_marksMap.find(mark);
159     if (iterator != m_marksMap.end())
160         return iterator->value.last()->startTime();
161
162     return Exception { SyntaxError, makeString("No mark named '", mark, "' exists") };
163 }
164
165 ExceptionOr<double> PerformanceUserTiming::convertMarkToTimestamp(double mark) const
166 {
167     if (mark < 0)
168         return Exception { TypeError };
169     return mark;
170 }
171
172 ExceptionOr<Ref<PerformanceMeasure>> PerformanceUserTiming::measure(const String& measureName, const String& startMark, const String& endMark)
173 {
174     double endTime;
175     if (!endMark.isNull()) {
176         auto end = convertMarkToTimestamp(endMark);
177         if (end.hasException())
178             return end.releaseException();
179         endTime = end.returnValue();
180     } else
181         endTime = m_performance.now();
182
183     double startTime;
184     if (!startMark.isNull()) {
185         auto start = convertMarkToTimestamp(startMark);
186         if (start.hasException())
187             return start.releaseException();
188         startTime = start.returnValue();
189     } else
190         startTime = 0.0;
191         
192     auto measure = PerformanceMeasure::create(measureName, startTime, endTime, SerializedScriptValue::nullValue());
193     if (measure.hasException())
194         return measure.releaseException();
195
196     addPerformanceEntry(m_measuresMap, measureName, measure.returnValue().get());
197     return measure.releaseReturnValue();
198 }
199
200 ExceptionOr<Ref<PerformanceMeasure>> PerformanceUserTiming::measure(JSC::JSGlobalObject& globalObject, const String& measureName, const PerformanceMeasureOptions& measureOptions)
201 {
202     double endTime;
203     if (measureOptions.end) {
204         auto end = convertMarkToTimestamp(*measureOptions.end);
205         if (end.hasException())
206             return end.releaseException();
207         endTime = end.returnValue();
208     } else if (measureOptions.start && measureOptions.duration) {
209         auto start = convertMarkToTimestamp(*measureOptions.start);
210         if (start.hasException())
211             return start.releaseException();
212         auto duration = convertMarkToTimestamp(*measureOptions.duration);
213         if (duration.hasException())
214             return start.releaseException();
215         endTime = start.returnValue() + duration.returnValue();
216     } else
217         endTime = m_performance.now();
218
219     double startTime;
220     if (measureOptions.start) {
221         auto start = convertMarkToTimestamp(*measureOptions.start);
222         if (start.hasException())
223             return start.releaseException();
224         startTime = start.returnValue();
225     } else if (measureOptions.duration && measureOptions.end) {
226         auto duration = convertMarkToTimestamp(*measureOptions.duration);
227         if (duration.hasException())
228             return duration.releaseException();
229         auto end = convertMarkToTimestamp(*measureOptions.end);
230         if (end.hasException())
231             return end.releaseException();
232         startTime = end.returnValue() - duration.returnValue();
233     } else
234         startTime = 0;
235
236
237     JSC::JSValue detail = measureOptions.detail;
238     if (detail.isUndefined())
239         detail = JSC::jsNull();
240
241     Vector<RefPtr<MessagePort>> ignoredMessagePorts;
242     auto serializedDetail = SerializedScriptValue::create(globalObject, detail, { }, ignoredMessagePorts);
243     if (serializedDetail.hasException())
244         return serializedDetail.releaseException();
245
246     auto measure = PerformanceMeasure::create(measureName, startTime, endTime, serializedDetail.releaseReturnValue());
247     if (measure.hasException())
248         return measure.releaseException();
249
250     addPerformanceEntry(m_measuresMap, measureName, measure.returnValue().get());
251     return measure.releaseReturnValue();
252 }
253
254 static bool isNonEmptyDictionary(const PerformanceMeasureOptions& measureOptions)
255 {
256     return !measureOptions.detail.isUndefined() || measureOptions.start || measureOptions.duration || measureOptions.end;
257 }
258
259 ExceptionOr<Ref<PerformanceMeasure>> PerformanceUserTiming::measure(JSC::JSGlobalObject& globalObject, const String& measureName, Optional<StartOrMeasureOptions>&& startOrMeasureOptions, const String& endMark)
260 {
261     if (startOrMeasureOptions) {
262         return WTF::switchOn(*startOrMeasureOptions,
263             [&] (const PerformanceMeasureOptions& measureOptions) -> ExceptionOr<Ref<PerformanceMeasure>> {
264                 if (isNonEmptyDictionary(measureOptions)) {
265                     if (!endMark.isNull())
266                         return Exception { TypeError };
267                     if (!measureOptions.start && !measureOptions.end)
268                         return Exception { TypeError };
269                     if (measureOptions.start && measureOptions.duration && measureOptions.end)
270                         return Exception { TypeError };
271                 }
272
273                 return measure(globalObject, measureName, measureOptions);
274             },
275             [&] (const String& startMark) {
276                 return measure(measureName, startMark, endMark);
277             }
278         );
279     }
280
281     return measure(measureName, { }, endMark);
282 }
283
284 void PerformanceUserTiming::clearMeasures(const String& measureName)
285 {
286     clearPerformanceEntries(m_measuresMap, measureName);
287 }
288
289 static Vector<RefPtr<PerformanceEntry>> convertToEntrySequence(const PerformanceEntryMap& map)
290 {
291     Vector<RefPtr<PerformanceEntry>> entries;
292     for (auto& entry : map.values())
293         entries.appendVector(entry);
294     return entries;
295 }
296
297 Vector<RefPtr<PerformanceEntry>> PerformanceUserTiming::getMarks() const
298 {
299     return convertToEntrySequence(m_marksMap);
300 }
301
302 Vector<RefPtr<PerformanceEntry>> PerformanceUserTiming::getMarks(const String& name) const
303 {
304     return m_marksMap.get(name);
305 }
306
307 Vector<RefPtr<PerformanceEntry>> PerformanceUserTiming::getMeasures() const
308 {
309     return convertToEntrySequence(m_measuresMap);
310 }
311
312 Vector<RefPtr<PerformanceEntry>> PerformanceUserTiming::getMeasures(const String& name) const
313 {
314     return m_measuresMap.get(name);
315 }
316
317 } // namespace WebCore