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_timeOrigin(timeOrigin)
  56     , m_performanceTimelineTaskQueue(context)
  57 {
  58     ASSERT(m_timeOrigin);
  59 }
  60 
  61 Performance::~Performance()
  62 {
  63 }
  64 
  65 void Performance::contextDestroyed()
  66 {
  67     m_performanceTimelineTaskQueue.close();
  68 
  69     ContextDestructionObserver::contextDestroyed();
  70 }
  71 
  72 double Performance::now() const
  73 {
  74     Seconds now = MonotonicTime::now() - m_timeOrigin;
  75     return reduceTimeResolution(now).milliseconds();
  76 }
  77 
  78 Seconds Performance::reduceTimeResolution(Seconds seconds)
  79 {
  80     double resolution = (100_us).seconds();
  81     double reduced = std::floor(seconds.seconds() / resolution) * resolution;
  82     return Seconds(reduced);
  83 }
  84 
  85 PerformanceNavigation* Performance::navigation()
  86 {
  87     if (!is<Document>(scriptExecutionContext()))
  88         return nullptr;
  89 
  90     ASSERT(isMainThread());
  91     if (!m_navigation)
  92         m_navigation = PerformanceNavigation::create(downcast<Document>(*scriptExecutionContext()).frame());
  93     return m_navigation.get();
  94 }
  95 
  96 PerformanceTiming* Performance::timing()
  97 {
  98     if (!is<Document>(scriptExecutionContext()))
  99         return nullptr;
 100 
 101     ASSERT(isMainThread());
 102     if (!m_timing)
 103         m_timing = PerformanceTiming::create(downcast<Document>(*scriptExecutionContext()).frame());
 104     return m_timing.get();
 105 }
 106 
 107 Vector<RefPtr<PerformanceEntry>> Performance::getEntries() const
 108 {
 109     Vector<RefPtr<PerformanceEntry>> entries;
 110 
 111     entries.appendVector(m_resourceTimingBuffer);
 112 
 113     if (m_userTiming) {
 114         entries.appendVector(m_userTiming->getMarks());
 115         entries.appendVector(m_userTiming->getMeasures());
 116     }
 117 
 118     std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan);
 119     return entries;
 120 }
 121 
 122 Vector<RefPtr<PerformanceEntry>> Performance::getEntriesByType(const String& entryType) const
 123 {
 124     Vector<RefPtr<PerformanceEntry>> entries;
 125 
 126     if (equalLettersIgnoringASCIICase(entryType, "resource"))
 127         entries.appendVector(m_resourceTimingBuffer);
 128 
 129     if (m_userTiming) {
 130         if (equalLettersIgnoringASCIICase(entryType, "mark"))
 131             entries.appendVector(m_userTiming->getMarks());
 132         else if (equalLettersIgnoringASCIICase(entryType, "measure"))
 133             entries.appendVector(m_userTiming->getMeasures());
 134     }
 135 
 136     std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan);
 137     return entries;
 138 }
 139 
 140 Vector<RefPtr<PerformanceEntry>> Performance::getEntriesByName(const String& name, const String& entryType) const
 141 {
 142     Vector<RefPtr<PerformanceEntry>> entries;
 143 
 144     if (entryType.isNull() || equalLettersIgnoringASCIICase(entryType, "resource")) {
 145         for (auto& resource : m_resourceTimingBuffer) {
 146             if (resource->name() == name)
 147                 entries.append(resource);
 148         }
 149     }
 150 
 151     if (m_userTiming) {
 152         if (entryType.isNull() || equalLettersIgnoringASCIICase(entryType, "mark"))
 153             entries.appendVector(m_userTiming->getMarks(name));
 154         if (entryType.isNull() || equalLettersIgnoringASCIICase(entryType, "measure"))
 155             entries.appendVector(m_userTiming->getMeasures(name));
 156     }
 157 
 158     std::sort(entries.begin(), entries.end(), PerformanceEntry::startTimeCompareLessThan);
 159     return entries;
 160 }
 161 
 162 void Performance::clearResourceTimings()
 163 {
 164     m_resourceTimingBuffer.clear();
 165 }
 166 
 167 void Performance::setResourceTimingBufferSize(unsigned size)
 168 {
 169     m_resourceTimingBufferSize = size;
 170 }
 171 
 172 void Performance::addResourceTiming(ResourceTiming&& resourceTiming)
 173 {
 174     RefPtr<PerformanceResourceTiming> entry = PerformanceResourceTiming::create(m_timeOrigin, WTFMove(resourceTiming));
 175 
 176     queueEntry(*entry);
 177 
 178     if (isResourceTimingBufferFull())
 179         return;
 180 
 181     m_resourceTimingBuffer.append(entry);
 182 
 183     if (isResourceTimingBufferFull())
 184         dispatchEvent(Event::create(eventNames().resourcetimingbufferfullEvent, true, false));
 185 }
 186 
 187 bool Performance::isResourceTimingBufferFull() const
 188 {
 189     return m_resourceTimingBuffer.size() >= m_resourceTimingBufferSize;
 190 }
 191 
 192 ExceptionOr<void> Performance::mark(const String& markName)
 193 {
 194     if (!m_userTiming)
 195         m_userTiming = std::make_unique<UserTiming>(*this);
 196 
 197     auto result = m_userTiming->mark(markName);
 198     if (result.hasException())
 199         return result.releaseException();
 200 
 201     queueEntry(result.releaseReturnValue());
 202 
 203     return { };
 204 }
 205 
 206 void Performance::clearMarks(const String& markName)
 207 {
 208     if (!m_userTiming)
 209         m_userTiming = std::make_unique<UserTiming>(*this);
 210     m_userTiming->clearMarks(markName);
 211 }
 212 
 213 ExceptionOr<void> Performance::measure(const String& measureName, const String& startMark, const String& endMark)
 214 {
 215     if (!m_userTiming)
 216         m_userTiming = std::make_unique<UserTiming>(*this);
 217 
 218     auto result = m_userTiming->measure(measureName, startMark, endMark);
 219     if (result.hasException())
 220         return result.releaseException();
 221 
 222     queueEntry(result.releaseReturnValue());
 223 
 224     return { };
 225 }
 226 
 227 void Performance::clearMeasures(const String& measureName)
 228 {
 229     if (!m_userTiming)
 230         m_userTiming = std::make_unique<UserTiming>(*this);
 231     m_userTiming->clearMeasures(measureName);
 232 }
 233 
 234 void Performance::removeAllObservers()
 235 {
 236     for (auto& observer : m_observers)
 237         observer->disassociate();
 238     m_observers.clear();
 239 }
 240 
 241 void Performance::registerPerformanceObserver(PerformanceObserver& observer)
 242 {
 243     m_observers.add(&observer);
 244 }
 245 
 246 void Performance::unregisterPerformanceObserver(PerformanceObserver& observer)
 247 {
 248     m_observers.remove(&observer);
 249 }
 250 
 251 void Performance::queueEntry(PerformanceEntry& entry)
 252 {
 253     bool shouldScheduleTask = false;
 254     for (auto& observer : m_observers) {
 255         if (observer->typeFilter().contains(entry.type())) {
 256             observer->queueEntry(entry);
 257             shouldScheduleTask = true;
 258         }
 259     }
 260 
 261     if (!shouldScheduleTask)
 262         return;
 263 
 264     if (m_performanceTimelineTaskQueue.hasPendingTasks())
 265         return;
 266 
 267     m_performanceTimelineTaskQueue.enqueueTask([this] () {
 268         Vector<RefPtr<PerformanceObserver>> observers;
 269         copyToVector(m_observers, observers);
 270         for (auto& observer : observers)
 271             observer->deliver();
 272     });
 273 }
 274 
 275 } // namespace WebCore