dcb057a35aee50ea8a89d41bdc25da5a02bd3c0e
[WebKit-https.git] / Source / WebCore / page / PerformanceUserTiming.cpp
1 /*
2  * Copyright (C) 2012 Intel 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
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "PerformanceUserTiming.h"
28
29 #if ENABLE(WEB_TIMING)
30
31 #include "ExceptionCode.h"
32 #include "Performance.h"
33 #include "PerformanceTiming.h"
34 #include <array>
35 #include <wtf/NeverDestroyed.h>
36 #include <wtf/dtoa/utils.h>
37
38 namespace WebCore {
39
40 namespace {
41
42 typedef unsigned long long (PerformanceTiming::*NavigationTimingFunction)() const;
43
44 static NavigationTimingFunction restrictedMarkFunction(const String& markName)
45 {
46     // FIXME: Update this list when moving to Navigation Timing Level 2.
47     using MapPair = std::pair<ASCIILiteral, NavigationTimingFunction>;
48     static const std::array<MapPair, 21> pairs = { {
49         MapPair { ASCIILiteral("navigationStart"), &PerformanceTiming::navigationStart },
50         MapPair { ASCIILiteral("unloadEventStart"), &PerformanceTiming::unloadEventStart },
51         MapPair { ASCIILiteral("unloadEventEnd"), &PerformanceTiming::unloadEventEnd },
52         MapPair { ASCIILiteral("redirectStart"), &PerformanceTiming::redirectStart },
53         MapPair { ASCIILiteral("redirectEnd"), &PerformanceTiming::redirectEnd },
54         MapPair { ASCIILiteral("fetchStart"), &PerformanceTiming::fetchStart },
55         MapPair { ASCIILiteral("domainLookupStart"), &PerformanceTiming::domainLookupStart },
56         MapPair { ASCIILiteral("domainLookupEnd"), &PerformanceTiming::domainLookupEnd },
57         MapPair { ASCIILiteral("connectStart"), &PerformanceTiming::connectStart },
58         MapPair { ASCIILiteral("connectEnd"), &PerformanceTiming::connectEnd },
59         MapPair { ASCIILiteral("secureConnectionStart"), &PerformanceTiming::secureConnectionStart },
60         MapPair { ASCIILiteral("requestStart"), &PerformanceTiming::requestStart },
61         MapPair { ASCIILiteral("responseStart"), &PerformanceTiming::responseStart },
62         MapPair { ASCIILiteral("responseEnd"), &PerformanceTiming::responseEnd },
63         MapPair { ASCIILiteral("domLoading"), &PerformanceTiming::domLoading },
64         MapPair { ASCIILiteral("domInteractive"), &PerformanceTiming::domInteractive },
65         MapPair { ASCIILiteral("domContentLoadedEventStart"), &PerformanceTiming::domContentLoadedEventStart },
66         MapPair { ASCIILiteral("domContentLoadedEventEnd"), &PerformanceTiming::domContentLoadedEventEnd },
67         MapPair { ASCIILiteral("domComplete"), &PerformanceTiming::domComplete },
68         MapPair { ASCIILiteral("loadEventStart"), &PerformanceTiming::loadEventStart },
69         MapPair { ASCIILiteral("loadEventEnd"), &PerformanceTiming::loadEventEnd },
70     } };
71
72     static NeverDestroyed<HashMap<String, NavigationTimingFunction>> map;
73     if (map.get().isEmpty()) {
74         for (auto& pair : pairs)
75             map.get().add(pair.first, pair.second);
76     }
77
78     return map.get().get(markName);
79 }
80
81 } // namespace anonymous
82
83 UserTiming::UserTiming(Performance& performance)
84     : m_performance(performance)
85 {
86 }
87
88 static void clearPerformanceEntries(PerformanceEntryMap& performanceEntryMap, const String& name)
89 {
90     if (name.isNull()) {
91         performanceEntryMap.clear();
92         return;
93     }
94
95     performanceEntryMap.remove(name);
96 }
97
98 ExceptionOr<Ref<PerformanceMark>> UserTiming::mark(const String& markName)
99 {
100     if (restrictedMarkFunction(markName))
101         return Exception { SYNTAX_ERR };
102
103     auto& performanceEntryList = m_marksMap.ensure(markName, [] { return Vector<RefPtr<PerformanceEntry>>(); }).iterator->value;
104     auto entry = PerformanceMark::create(markName, m_performance.now());
105     performanceEntryList.append(entry.copyRef());
106     return WTFMove(entry);
107 }
108
109 void UserTiming::clearMarks(const String& markName)
110 {
111     clearPerformanceEntries(m_marksMap, markName);
112 }
113
114 ExceptionOr<double> UserTiming::findExistingMarkStartTime(const String& markName)
115 {
116     if (m_marksMap.contains(markName))
117         return m_marksMap.get(markName).last()->startTime();
118
119     if (auto function = restrictedMarkFunction(markName)) {
120         if (PerformanceTiming* timing = m_performance.timing()) {
121             double value = static_cast<double>(((*timing).*(function))());
122             if (!value)
123                 return Exception { INVALID_ACCESS_ERR };
124             return value - timing->navigationStart();
125         } else {
126             // FIXME: Support UserTiming in Workers.
127             ASSERT_NOT_REACHED();
128             return Exception { SYNTAX_ERR };
129         }
130     }
131
132     return Exception { SYNTAX_ERR };
133 }
134
135 ExceptionOr<Ref<PerformanceMeasure>> UserTiming::measure(const String& measureName, const String& startMark, const String& endMark)
136 {
137     double startTime = 0.0;
138     double endTime = 0.0;
139
140     if (startMark.isNull())
141         endTime = m_performance.now();
142     else if (endMark.isNull()) {
143         endTime = m_performance.now();
144         auto startMarkResult = findExistingMarkStartTime(startMark);
145         if (startMarkResult.hasException())
146             return startMarkResult.releaseException();
147         startTime = startMarkResult.releaseReturnValue();
148     } else {
149         auto endMarkResult = findExistingMarkStartTime(endMark);
150         if (endMarkResult.hasException())
151             return endMarkResult.releaseException();
152         auto startMarkResult = findExistingMarkStartTime(startMark);
153         if (startMarkResult.hasException())
154             return startMarkResult.releaseException();
155         startTime = startMarkResult.releaseReturnValue();
156         endTime = endMarkResult.releaseReturnValue();
157     }
158
159     auto& performanceEntryList = m_measuresMap.ensure(measureName, [] { return Vector<RefPtr<PerformanceEntry>>(); }).iterator->value;
160     auto entry = PerformanceMeasure::create(measureName, startTime, endTime);
161     performanceEntryList.append(entry.copyRef());
162     return WTFMove(entry);
163 }
164
165 void UserTiming::clearMeasures(const String& measureName)
166 {
167     clearPerformanceEntries(m_measuresMap, measureName);
168 }
169
170 static Vector<RefPtr<PerformanceEntry>> convertToEntrySequence(const PerformanceEntryMap& performanceEntryMap)
171 {
172     Vector<RefPtr<PerformanceEntry>> entries;
173     for (auto& entry : performanceEntryMap.values())
174         entries.appendVector(entry);
175     return entries;
176 }
177
178 static Vector<RefPtr<PerformanceEntry>> getEntrySequenceByName(const PerformanceEntryMap& performanceEntryMap, const String& name)
179 {
180     return performanceEntryMap.get(name);
181 }
182
183 Vector<RefPtr<PerformanceEntry>> UserTiming::getMarks() const
184 {
185     return convertToEntrySequence(m_marksMap);
186 }
187
188 Vector<RefPtr<PerformanceEntry>> UserTiming::getMarks(const String& name) const
189 {
190     return getEntrySequenceByName(m_marksMap, name);
191 }
192
193 Vector<RefPtr<PerformanceEntry>> UserTiming::getMeasures() const
194 {
195     return convertToEntrySequence(m_measuresMap);
196 }
197
198 Vector<RefPtr<PerformanceEntry>> UserTiming::getMeasures(const String& name) const
199 {
200     return getEntrySequenceByName(m_measuresMap, name);
201 }
202
203 } // namespace WebCore
204
205 #endif // ENABLE(WEB_TIMING)