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