7b9400d9dea6ee8736529666e69796b9fed9439b
[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         double value = static_cast<double>((m_performance.timing().*(function))());
121         if (!value)
122             return Exception { INVALID_ACCESS_ERR };
123         return value - m_performance.timing().navigationStart();
124     }
125
126     return Exception { SYNTAX_ERR };
127 }
128
129 ExceptionOr<Ref<PerformanceMeasure>> UserTiming::measure(const String& measureName, const String& startMark, const String& endMark)
130 {
131     double startTime = 0.0;
132     double endTime = 0.0;
133
134     if (startMark.isNull())
135         endTime = m_performance.now();
136     else if (endMark.isNull()) {
137         endTime = m_performance.now();
138         auto startMarkResult = findExistingMarkStartTime(startMark);
139         if (startMarkResult.hasException())
140             return startMarkResult.releaseException();
141         startTime = startMarkResult.releaseReturnValue();
142     } else {
143         auto endMarkResult = findExistingMarkStartTime(endMark);
144         if (endMarkResult.hasException())
145             return endMarkResult.releaseException();
146         auto startMarkResult = findExistingMarkStartTime(startMark);
147         if (startMarkResult.hasException())
148             return startMarkResult.releaseException();
149         startTime = startMarkResult.releaseReturnValue();
150         endTime = endMarkResult.releaseReturnValue();
151     }
152
153     auto& performanceEntryList = m_measuresMap.ensure(measureName, [] { return Vector<RefPtr<PerformanceEntry>>(); }).iterator->value;
154     auto entry = PerformanceMeasure::create(measureName, startTime, endTime);
155     performanceEntryList.append(entry.copyRef());
156     return WTFMove(entry);
157 }
158
159 void UserTiming::clearMeasures(const String& measureName)
160 {
161     clearPerformanceEntries(m_measuresMap, measureName);
162 }
163
164 static Vector<RefPtr<PerformanceEntry>> convertToEntrySequence(const PerformanceEntryMap& performanceEntryMap)
165 {
166     Vector<RefPtr<PerformanceEntry>> entries;
167     for (auto& entry : performanceEntryMap.values())
168         entries.appendVector(entry);
169     return entries;
170 }
171
172 static Vector<RefPtr<PerformanceEntry>> getEntrySequenceByName(const PerformanceEntryMap& performanceEntryMap, const String& name)
173 {
174     return performanceEntryMap.get(name);
175 }
176
177 Vector<RefPtr<PerformanceEntry>> UserTiming::getMarks() const
178 {
179     return convertToEntrySequence(m_marksMap);
180 }
181
182 Vector<RefPtr<PerformanceEntry>> UserTiming::getMarks(const String& name) const
183 {
184     return getEntrySequenceByName(m_marksMap, name);
185 }
186
187 Vector<RefPtr<PerformanceEntry>> UserTiming::getMeasures() const
188 {
189     return convertToEntrySequence(m_measuresMap);
190 }
191
192 Vector<RefPtr<PerformanceEntry>> UserTiming::getMeasures(const String& name) const
193 {
194     return getEntrySequenceByName(m_measuresMap, name);
195 }
196
197 } // namespace WebCore
198
199 #endif // ENABLE(WEB_TIMING)