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