1 | /* |
2 | * Copyright (C) 2010 Google Inc. All rights reserved. |
3 | * Copyright (C) 2012 Intel Inc. All rights reserved. |
4 | * Copyright (C) 2016 Apple 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 "Performance.h" |
35 | |
36 | #include "CustomHeaderFields.h" |
37 | #include "Document.h" |
38 | #include "DocumentLoader.h" |
39 | #include "Event.h" |
40 | #include "EventNames.h" |
41 | #include "Frame.h" |
42 | #include "PerformanceEntry.h" |
43 | #include "PerformanceNavigation.h" |
44 | #include "PerformanceObserver.h" |
45 | #include "PerformanceResourceTiming.h" |
46 | #include "PerformanceTiming.h" |
47 | #include "PerformanceUserTiming.h" |
48 | #include "ResourceResponse.h" |
49 | #include "ScriptExecutionContext.h" |
50 | #include <wtf/IsoMallocInlines.h> |
51 | |
52 | namespace WebCore { |
53 | |
54 | WTF_MAKE_ISO_ALLOCATED_IMPL(Performance); |
55 | |
56 | Performance::Performance(ScriptExecutionContext* context, MonotonicTime timeOrigin) |
57 | : ContextDestructionObserver(context) |
58 | , m_resourceTimingBufferFullTimer(*this, &Performance::resourceTimingBufferFullTimerFired) |
59 | , m_timeOrigin(timeOrigin) |
60 | , m_performanceTimelineTaskQueue(context) |
61 | { |
62 | ASSERT(m_timeOrigin); |
63 | ASSERT(context || m_performanceTimelineTaskQueue.isClosed()); |
64 | } |
65 | |
66 | Performance::~Performance() = default; |
67 | |
68 | void Performance::contextDestroyed() |
69 | { |
70 | m_performanceTimelineTaskQueue.close(); |
71 | m_resourceTimingBufferFullTimer.stop(); |
72 | ContextDestructionObserver::contextDestroyed(); |
73 | } |
74 | |
75 | DOMHighResTimeStamp Performance::now() const |
76 | { |
77 | Seconds now = MonotonicTime::now() - m_timeOrigin; |
78 | return reduceTimeResolution(now).milliseconds(); |
79 | } |
80 | |
81 | Seconds Performance::reduceTimeResolution(Seconds seconds) |
82 | { |
83 | double resolution = (1000_us).seconds(); |
84 | double reduced = std::floor(seconds.seconds() / resolution) * resolution; |
85 | return Seconds(reduced); |
86 | } |
87 | |
88 | DOMHighResTimeStamp Performance::relativeTimeFromTimeOriginInReducedResolution(MonotonicTime timestamp) const |
89 | { |
90 | Seconds seconds = timestamp - m_timeOrigin; |
91 | return reduceTimeResolution(seconds).milliseconds(); |
92 | } |
93 | |
94 | PerformanceNavigation* Performance::navigation() |
95 | { |
96 | if (!is<Document>(scriptExecutionContext())) |
97 | return nullptr; |
98 | |
99 | ASSERT(isMainThread()); |
100 | if (!m_navigation) |
101 | m_navigation = PerformanceNavigation::create(downcast<Document>(*scriptExecutionContext()).domWindow()); |
102 | return m_navigation.get(); |
103 | } |
104 | |
105 | PerformanceTiming* Performance::timing() |
106 | { |
107 | if (!is<Document>(scriptExecutionContext())) |
108 | return nullptr; |
109 | |
110 | ASSERT(isMainThread()); |
111 | if (!m_timing) |
112 | m_timing = PerformanceTiming::create(downcast<Document>(*scriptExecutionContext()).domWindow()); |
113 | return m_timing.get(); |
114 | } |
115 | |
116 | Vector<RefPtr<PerformanceEntry>> Performance::getEntries() const |
117 | { |
118 | Vector<RefPtr<PerformanceEntry>> entries; |
119 | |
120 | entries.appendVector(m_resourceTimingBuffer); |
121 | |
122 | if (m_userTiming) { |
123 | entries.appendVector(m_userTiming->getMarks()); |
124 | entries.appendVector(m_userTiming->getMeasures()); |
125 | } |
126 | |
127 | std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan); |
128 | return entries; |
129 | } |
130 | |
131 | Vector<RefPtr<PerformanceEntry>> Performance::getEntriesByType(const String& entryType) const |
132 | { |
133 | Vector<RefPtr<PerformanceEntry>> entries; |
134 | |
135 | if (equalLettersIgnoringASCIICase(entryType, "resource" )) |
136 | entries.appendVector(m_resourceTimingBuffer); |
137 | |
138 | if (m_userTiming) { |
139 | if (equalLettersIgnoringASCIICase(entryType, "mark" )) |
140 | entries.appendVector(m_userTiming->getMarks()); |
141 | else if (equalLettersIgnoringASCIICase(entryType, "measure" )) |
142 | entries.appendVector(m_userTiming->getMeasures()); |
143 | } |
144 | |
145 | std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan); |
146 | return entries; |
147 | } |
148 | |
149 | Vector<RefPtr<PerformanceEntry>> Performance::getEntriesByName(const String& name, const String& entryType) const |
150 | { |
151 | Vector<RefPtr<PerformanceEntry>> entries; |
152 | |
153 | if (entryType.isNull() || equalLettersIgnoringASCIICase(entryType, "resource" )) { |
154 | for (auto& resource : m_resourceTimingBuffer) { |
155 | if (resource->name() == name) |
156 | entries.append(resource); |
157 | } |
158 | } |
159 | |
160 | if (m_userTiming) { |
161 | if (entryType.isNull() || equalLettersIgnoringASCIICase(entryType, "mark" )) |
162 | entries.appendVector(m_userTiming->getMarks(name)); |
163 | if (entryType.isNull() || equalLettersIgnoringASCIICase(entryType, "measure" )) |
164 | entries.appendVector(m_userTiming->getMeasures(name)); |
165 | } |
166 | |
167 | std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan); |
168 | return entries; |
169 | } |
170 | |
171 | void Performance::clearResourceTimings() |
172 | { |
173 | m_resourceTimingBuffer.clear(); |
174 | m_resourceTimingBufferFullFlag = false; |
175 | } |
176 | |
177 | void Performance::setResourceTimingBufferSize(unsigned size) |
178 | { |
179 | m_resourceTimingBufferSize = size; |
180 | m_resourceTimingBufferFullFlag = false; |
181 | } |
182 | |
183 | void Performance::addResourceTiming(ResourceTiming&& resourceTiming) |
184 | { |
185 | ASSERT(scriptExecutionContext()); |
186 | |
187 | auto entry = PerformanceResourceTiming::create(m_timeOrigin, WTFMove(resourceTiming)); |
188 | |
189 | if (m_waitingForBackupBufferToBeProcessed) { |
190 | m_backupResourceTimingBuffer.append(WTFMove(entry)); |
191 | return; |
192 | } |
193 | |
194 | if (m_resourceTimingBufferFullFlag) { |
195 | // We fired resourcetimingbufferfull event but the author script didn't clear the buffer. |
196 | // Notify performance observers but don't add it to the buffer. |
197 | queueEntry(entry.get()); |
198 | return; |
199 | } |
200 | |
201 | if (isResourceTimingBufferFull()) { |
202 | ASSERT(!m_resourceTimingBufferFullTimer.isActive()); |
203 | m_backupResourceTimingBuffer.append(WTFMove(entry)); |
204 | m_waitingForBackupBufferToBeProcessed = true; |
205 | m_resourceTimingBufferFullTimer.startOneShot(0_s); |
206 | return; |
207 | } |
208 | |
209 | queueEntry(entry.get()); |
210 | m_resourceTimingBuffer.append(WTFMove(entry)); |
211 | } |
212 | |
213 | bool Performance::isResourceTimingBufferFull() const |
214 | { |
215 | return m_resourceTimingBuffer.size() >= m_resourceTimingBufferSize; |
216 | } |
217 | |
218 | void Performance::resourceTimingBufferFullTimerFired() |
219 | { |
220 | ASSERT(scriptExecutionContext()); |
221 | |
222 | while (!m_backupResourceTimingBuffer.isEmpty()) { |
223 | auto beforeCount = m_backupResourceTimingBuffer.size(); |
224 | |
225 | auto backupBuffer = WTFMove(m_backupResourceTimingBuffer); |
226 | ASSERT(m_backupResourceTimingBuffer.isEmpty()); |
227 | |
228 | if (isResourceTimingBufferFull()) { |
229 | m_resourceTimingBufferFullFlag = true; |
230 | dispatchEvent(Event::create(eventNames().resourcetimingbufferfullEvent, Event::CanBubble::No, Event::IsCancelable::No)); |
231 | } |
232 | |
233 | if (m_resourceTimingBufferFullFlag) { |
234 | for (auto& entry : backupBuffer) |
235 | queueEntry(*entry); |
236 | // Dispatching resourcetimingbufferfull event may have inserted more entries. |
237 | for (auto& entry : m_backupResourceTimingBuffer) |
238 | queueEntry(*entry); |
239 | m_backupResourceTimingBuffer.clear(); |
240 | break; |
241 | } |
242 | |
243 | // More entries may have added while dispatching resourcetimingbufferfull event. |
244 | backupBuffer.appendVector(m_backupResourceTimingBuffer); |
245 | m_backupResourceTimingBuffer.clear(); |
246 | |
247 | for (auto& entry : backupBuffer) { |
248 | if (!isResourceTimingBufferFull()) { |
249 | m_resourceTimingBuffer.append(entry.copyRef()); |
250 | queueEntry(*entry); |
251 | } else |
252 | m_backupResourceTimingBuffer.append(entry.copyRef()); |
253 | } |
254 | |
255 | auto afterCount = m_backupResourceTimingBuffer.size(); |
256 | |
257 | if (beforeCount <= afterCount) { |
258 | m_backupResourceTimingBuffer.clear(); |
259 | break; |
260 | } |
261 | } |
262 | m_waitingForBackupBufferToBeProcessed = false; |
263 | } |
264 | |
265 | ExceptionOr<void> Performance::mark(const String& markName) |
266 | { |
267 | if (!m_userTiming) |
268 | m_userTiming = std::make_unique<UserTiming>(*this); |
269 | |
270 | auto result = m_userTiming->mark(markName); |
271 | if (result.hasException()) |
272 | return result.releaseException(); |
273 | |
274 | queueEntry(result.releaseReturnValue()); |
275 | |
276 | return { }; |
277 | } |
278 | |
279 | void Performance::clearMarks(const String& markName) |
280 | { |
281 | if (!m_userTiming) |
282 | m_userTiming = std::make_unique<UserTiming>(*this); |
283 | m_userTiming->clearMarks(markName); |
284 | } |
285 | |
286 | ExceptionOr<void> Performance::measure(const String& measureName, const String& startMark, const String& endMark) |
287 | { |
288 | if (!m_userTiming) |
289 | m_userTiming = std::make_unique<UserTiming>(*this); |
290 | |
291 | auto result = m_userTiming->measure(measureName, startMark, endMark); |
292 | if (result.hasException()) |
293 | return result.releaseException(); |
294 | |
295 | queueEntry(result.releaseReturnValue()); |
296 | |
297 | return { }; |
298 | } |
299 | |
300 | void Performance::clearMeasures(const String& measureName) |
301 | { |
302 | if (!m_userTiming) |
303 | m_userTiming = std::make_unique<UserTiming>(*this); |
304 | m_userTiming->clearMeasures(measureName); |
305 | } |
306 | |
307 | void Performance::removeAllObservers() |
308 | { |
309 | for (auto& observer : m_observers) |
310 | observer->disassociate(); |
311 | m_observers.clear(); |
312 | } |
313 | |
314 | void Performance::registerPerformanceObserver(PerformanceObserver& observer) |
315 | { |
316 | m_observers.add(&observer); |
317 | } |
318 | |
319 | void Performance::unregisterPerformanceObserver(PerformanceObserver& observer) |
320 | { |
321 | m_observers.remove(&observer); |
322 | } |
323 | |
324 | void Performance::queueEntry(PerformanceEntry& entry) |
325 | { |
326 | bool shouldScheduleTask = false; |
327 | for (auto& observer : m_observers) { |
328 | if (observer->typeFilter().contains(entry.type())) { |
329 | observer->queueEntry(entry); |
330 | shouldScheduleTask = true; |
331 | } |
332 | } |
333 | |
334 | if (!shouldScheduleTask) |
335 | return; |
336 | |
337 | if (m_performanceTimelineTaskQueue.hasPendingTasks()) |
338 | return; |
339 | |
340 | m_performanceTimelineTaskQueue.enqueueTask([this] () { |
341 | for (auto& observer : copyToVector(m_observers)) |
342 | observer->deliver(); |
343 | }); |
344 | } |
345 | |
346 | } // namespace WebCore |
347 | |