Improve use of NeverDestroyed
[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 #if ENABLE(WEB_TIMING)
31
32 #include "Document.h"
33 #include "ExceptionCode.h"
34 #include "PerformanceTiming.h"
35 #include <wtf/NeverDestroyed.h>
36
37 namespace WebCore {
38
39 using NavigationTimingFunction = unsigned long long (PerformanceTiming::*)() const;
40
41 static NavigationTimingFunction restrictedMarkFunction(const String& markName)
42 {
43     ASSERT(isMainThread());
44
45     static const auto map = makeNeverDestroyed([] {
46         static const std::pair<const char*, NavigationTimingFunction> pairs[] = {
47             { "connectEnd", &PerformanceTiming::connectEnd },
48             { "connectStart", &PerformanceTiming::connectStart },
49             { "domComplete", &PerformanceTiming::domComplete },
50             { "domContentLoadedEventEnd", &PerformanceTiming::domContentLoadedEventEnd },
51             { "domContentLoadedEventStart", &PerformanceTiming::domContentLoadedEventStart },
52             { "domInteractive", &PerformanceTiming::domInteractive },
53             { "domLoading", &PerformanceTiming::domLoading },
54             { "domainLookupEnd", &PerformanceTiming::domainLookupEnd },
55             { "domainLookupStart", &PerformanceTiming::domainLookupStart },
56             { "fetchStart", &PerformanceTiming::fetchStart },
57             { "loadEventEnd", &PerformanceTiming::loadEventEnd },
58             { "loadEventStart", &PerformanceTiming::loadEventStart },
59             { "navigationStart", &PerformanceTiming::navigationStart },
60             { "redirectEnd", &PerformanceTiming::redirectEnd },
61             { "redirectStart", &PerformanceTiming::redirectStart },
62             { "requestStart", &PerformanceTiming::requestStart },
63             { "responseEnd", &PerformanceTiming::responseEnd },
64             { "responseStart", &PerformanceTiming::responseStart },
65             { "secureConnectionStart", &PerformanceTiming::secureConnectionStart },
66             { "unloadEventEnd", &PerformanceTiming::unloadEventEnd },
67             { "unloadEventStart", &PerformanceTiming::unloadEventStart },
68         };
69         HashMap<String, NavigationTimingFunction> map;
70         for (auto& pair : pairs)
71             map.add(ASCIILiteral { pair.first }, pair.second);
72         return map;
73     }());
74
75     return map.get().get(markName);
76 }
77
78 UserTiming::UserTiming(Performance& performance)
79     : m_performance(performance)
80 {
81 }
82
83 static void clearPerformanceEntries(PerformanceEntryMap& map, const String& name)
84 {
85     if (name.isNull())
86         map.clear();
87     else
88         map.remove(name);
89 }
90
91 ExceptionOr<Ref<PerformanceMark>> UserTiming::mark(const String& markName)
92 {
93     if (is<Document>(m_performance.scriptExecutionContext()) && restrictedMarkFunction(markName))
94         return Exception { SYNTAX_ERR };
95
96     auto& performanceEntryList = m_marksMap.ensure(markName, [] { return Vector<RefPtr<PerformanceEntry>>(); }).iterator->value;
97     auto entry = PerformanceMark::create(markName, m_performance.now());
98     performanceEntryList.append(entry.copyRef());
99     return WTFMove(entry);
100 }
101
102 void UserTiming::clearMarks(const String& markName)
103 {
104     clearPerformanceEntries(m_marksMap, markName);
105 }
106
107 ExceptionOr<double> UserTiming::findExistingMarkStartTime(const String& markName)
108 {
109     auto iterator = m_marksMap.find(markName);
110     if (iterator != m_marksMap.end())
111         return iterator->value.last()->startTime();
112
113     auto* timing = m_performance.timing();
114     if (!timing)
115         return Exception { SYNTAX_ERR, makeString("No mark named '", markName, "' exists") };
116
117     if (auto function = restrictedMarkFunction(markName)) {
118         double value = ((*timing).*(function))();
119         if (!value)
120             return Exception { INVALID_ACCESS_ERR };
121         return value - timing->navigationStart();
122     }
123
124     return Exception { SYNTAX_ERR };
125 }
126
127 ExceptionOr<Ref<PerformanceMeasure>> UserTiming::measure(const String& measureName, const String& startMark, const String& endMark)
128 {
129     double startTime = 0.0;
130     double endTime = 0.0;
131
132     if (startMark.isNull())
133         endTime = m_performance.now();
134     else if (endMark.isNull()) {
135         endTime = m_performance.now();
136         auto startMarkResult = findExistingMarkStartTime(startMark);
137         if (startMarkResult.hasException())
138             return startMarkResult.releaseException();
139         startTime = startMarkResult.releaseReturnValue();
140     } else {
141         auto endMarkResult = findExistingMarkStartTime(endMark);
142         if (endMarkResult.hasException())
143             return endMarkResult.releaseException();
144         auto startMarkResult = findExistingMarkStartTime(startMark);
145         if (startMarkResult.hasException())
146             return startMarkResult.releaseException();
147         startTime = startMarkResult.releaseReturnValue();
148         endTime = endMarkResult.releaseReturnValue();
149     }
150
151     auto& performanceEntryList = m_measuresMap.ensure(measureName, [] { return Vector<RefPtr<PerformanceEntry>>(); }).iterator->value;
152     auto entry = PerformanceMeasure::create(measureName, startTime, endTime);
153     performanceEntryList.append(entry.copyRef());
154     return WTFMove(entry);
155 }
156
157 void UserTiming::clearMeasures(const String& measureName)
158 {
159     clearPerformanceEntries(m_measuresMap, measureName);
160 }
161
162 static Vector<RefPtr<PerformanceEntry>> convertToEntrySequence(const PerformanceEntryMap& map)
163 {
164     Vector<RefPtr<PerformanceEntry>> entries;
165     for (auto& entry : map.values())
166         entries.appendVector(entry);
167     return entries;
168 }
169
170 Vector<RefPtr<PerformanceEntry>> UserTiming::getMarks() const
171 {
172     return convertToEntrySequence(m_marksMap);
173 }
174
175 Vector<RefPtr<PerformanceEntry>> UserTiming::getMarks(const String& name) const
176 {
177     return m_marksMap.get(name);
178 }
179
180 Vector<RefPtr<PerformanceEntry>> UserTiming::getMeasures() const
181 {
182     return convertToEntrySequence(m_measuresMap);
183 }
184
185 Vector<RefPtr<PerformanceEntry>> UserTiming::getMeasures(const String& name) const
186 {
187     return m_measuresMap.get(name);
188 }
189
190 } // namespace WebCore
191
192 #endif // ENABLE(WEB_TIMING)