Chromium Code Reviews| Index: Source/core/timing/PerformanceObserver.cpp |
| diff --git a/Source/core/timing/PerformanceObserver.cpp b/Source/core/timing/PerformanceObserver.cpp |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..9f03a254e75c39e8bb2bd809de531d59518972ce |
| --- /dev/null |
| +++ b/Source/core/timing/PerformanceObserver.cpp |
| @@ -0,0 +1,216 @@ |
| +/* |
| + * Copyright (C) 2015 Google Inc. All rights reserved. |
| + * |
| + * Redistribution and use in source and binary forms, with or without |
| + * modification, are permitted provided that the following conditions are |
| + * met: |
| + * |
| + * * Redistributions of source code must retain the above copyright |
| + * notice, this list of conditions and the following disclaimer. |
| + * * Redistributions in binary form must reproduce the above |
| + * copyright notice, this list of conditions and the following disclaimer |
| + * in the documentation and/or other materials provided with the |
| + * distribution. |
| + * * Neither the name of Google Inc. nor the names of its |
| + * contributors may be used to endorse or promote products derived from |
| + * this software without specific prior written permission. |
|
esprehn
2015/07/18 22:24:16
short copyright
MikeB
2015/07/20 23:06:50
Done.
|
| + * |
| + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| + */ |
| + |
| +#include "config.h" |
| +#include "core/timing/PerformanceObserver.h" |
| + |
| +#include "bindings/core/v8/ExceptionState.h" |
| +#include "core/dom/ExceptionCode.h" |
| +#include "core/dom/Microtask.h" |
| +#include "core/inspector/InspectorInstrumentation.h" |
| +#include "core/timing/PerformanceBase.h" |
| +#include "core/timing/PerformanceEntry.h" |
| +#include "core/timing/PerformanceObserverCallback.h" |
| +#include "core/timing/PerformanceObserverEntryList.h" |
| +#include "core/timing/PerformanceObserverInit.h" |
| +#include "core/timing/PerformanceObserverRegistration.h" |
| +#include "wtf/MainThread.h" |
| +#include <algorithm> |
| + |
| +namespace blink { |
| + |
| +static unsigned s_observerPriority = 0; |
| + |
| +struct PerformanceObserver::ObserverLessThan { |
| + bool operator()(const Member<PerformanceObserver>& lhs, const Member<PerformanceObserver>& rhs) |
| + { |
|
esprehn
2015/07/18 22:24:16
If you just use a ListHashSet you don't need this
MikeB
2015/07/20 23:06:50
Done.
|
| + return lhs->m_priority < rhs->m_priority; |
| + } |
| +}; |
| + |
| +PerformanceObserver* PerformanceObserver::create(PerformanceBase* performance, PerformanceObserverCallback* callback) |
| +{ |
| + ASSERT(isMainThread()); |
| + return new PerformanceObserver(performance, callback); |
| +} |
| + |
| +PerformanceObserver::PerformanceObserver(PerformanceBase* performance, PerformanceObserverCallback* callback) |
| + : m_callback(callback) |
| + , m_performance(performance) |
| + , m_priority(s_observerPriority++) |
|
esprehn
2015/07/18 22:24:16
or this
MikeB
2015/07/20 23:06:50
Done.
|
| +{ |
| +} |
| + |
| +PerformanceObserver::~PerformanceObserver() |
| +{ |
| + if (!m_performanceEntries.isEmpty()) |
| + InspectorInstrumentation::didClearAllPerformanceObservations(m_callback->executionContext(), this); |
| +} |
| + |
| +void PerformanceObserver::observe(const PerformanceObserverInit& observerInit, ExceptionState& exceptionState) |
| +{ |
| + PerformanceBase* performance = m_performance.get(); |
| + if (!performance) { |
| + exceptionState.throwTypeError("Window may be destroyed? Performance target is invalid."); |
| + return; |
| + } |
| + |
| + HashSet<AtomicString> entryType; |
| + if (observerInit.hasEntryType() && observerInit.entryType().size()) { |
| + const Vector<String>& sequence = observerInit.entryType(); |
| + for (unsigned i = 0; i < sequence.size(); ++i) |
| + entryType.add(AtomicString(sequence[i])); |
| + } else { |
| + exceptionState.throwTypeError("A Performance Observer MUST have a non-empty typeFilter attribute."); |
| + return; |
| + } |
| + for (auto registration : m_registrations) { |
| + if (registration->performance() == performance) { |
| + registration->resetObservation(entryType); |
| + return; |
| + } |
| + } |
| + PerformanceObserverRegistration::create(*this, performance, entryType); |
| +} |
| + |
| +void PerformanceObserver::disconnect() |
| +{ |
| + m_performanceEntries.clear(); |
| + InspectorInstrumentation::didClearAllPerformanceObservations(m_callback->executionContext(), this); |
| + // registration->unregister() will call m_registrations.remove() and would |
| + // invalidate the iterator being used here if we don't make a copy. |
| + PerformanceObserverRegistrationSet copyOfRegistrations(m_registrations); |
| + for (auto registration : copyOfRegistrations) { |
| + registration->unregister(); |
| + } |
| +} |
| + |
| +void PerformanceObserver::observationStarted(PerformanceObserverRegistration* registration) |
| +{ |
| + ASSERT(!m_registrations.contains(registration)); |
| + m_registrations.add(registration); |
| +} |
| + |
| +void PerformanceObserver::observationEnded(PerformanceObserverRegistration* registration) |
| +{ |
| + ASSERT(m_registrations.contains(registration)); |
| + m_registrations.remove(m_registrations.find(registration)); |
| +} |
| + |
| +static PerformanceObserverSet& activePerformanceObservers() |
| +{ |
| + DEFINE_STATIC_LOCAL(Persistent<PerformanceObserverSet>, activeObservers, (new PerformanceObserverSet())); |
| + return *activeObservers; |
| +} |
| + |
| +static PerformanceObserverSet& suspendedPerformanceObservers() |
| +{ |
| + DEFINE_STATIC_LOCAL(Persistent<PerformanceObserverSet>, suspendedObservers, (new PerformanceObserverSet())); |
| + return *suspendedObservers; |
| +} |
| + |
| +static void activateObserver(PerformanceObserver* observer) |
| +{ |
| + if (activePerformanceObservers().isEmpty()) |
| + Microtask::enqueueMicrotask(WTF::bind(&PerformanceObserver::deliverObservations)); |
| + |
| + activePerformanceObservers().add(observer); |
| +} |
| + |
| +void PerformanceObserver::enqueuePerformanceEntry(PerformanceEntry* entry) |
| +{ |
| + ASSERT(isMainThread()); |
| + m_performanceEntries.append(entry); |
| + activateObserver(this); |
| + InspectorInstrumentation::didEnqueuePerformanceObserverEntries(m_callback->executionContext(), this); |
| +} |
| + |
| +bool PerformanceObserver::shouldBeSuspended() const |
| +{ |
| + return m_callback->executionContext() && m_callback->executionContext()->activeDOMObjectsAreSuspended(); |
| +} |
| + |
| +void PerformanceObserver::deliver() |
| +{ |
| + ASSERT(!shouldBeSuspended()); |
| + |
| + if (m_performanceEntries.isEmpty()) |
| + return; |
| + |
| + // TODO(mpb) |
| + PerformanceEntryVector performanceEntries; |
| + performanceEntries.swap(m_performanceEntries); |
| + Member<PerformanceObserverEntryList> entryList(new PerformanceObserverEntryList(performanceEntries)); |
| + |
| + InspectorInstrumentation::willDeliverPerformanceObservations(m_callback->executionContext(), this); |
| + m_callback->handleEvent(entryList, this); |
| + InspectorInstrumentation::didDeliverPerformanceObservations(m_callback->executionContext()); |
| +} |
| + |
| +void PerformanceObserver::resumeSuspendedObservers() |
| +{ |
| + ASSERT(isMainThread()); |
| + if (suspendedPerformanceObservers().isEmpty()) |
| + return; |
| + |
| + PerformanceObserverVector suspended; |
| + copyToVector(suspendedPerformanceObservers(), suspended); |
| + for (size_t i = 0; i < suspended.size(); ++i) { |
| + if (!suspended[i]->shouldBeSuspended()) { |
| + suspendedPerformanceObservers().remove(suspended[i]); |
| + activateObserver(suspended[i]); |
| + } |
| + } |
| +} |
| + |
| +void PerformanceObserver::deliverObservations() |
| +{ |
| + ASSERT(isMainThread()); |
| + PerformanceObserverVector observers; |
| + copyToVector(activePerformanceObservers(), observers); |
| + activePerformanceObservers().clear(); |
| + std::sort(observers.begin(), observers.end(), ObserverLessThan()); |
| + for (size_t i = 0; i < observers.size(); ++i) { |
| + if (observers[i]->shouldBeSuspended()) |
| + suspendedPerformanceObservers().add(observers[i]); |
| + else |
| + observers[i]->deliver(); |
| + } |
| +} |
| + |
| +DEFINE_TRACE(PerformanceObserver) |
| +{ |
| + visitor->trace(m_callback); |
| + visitor->trace(m_performance); |
| + visitor->trace(m_performanceEntries); |
| + visitor->trace(m_registrations); |
| +} |
| + |
| +} // namespace blink |