Diff
Modified: trunk/LayoutTests/ChangeLog (275667 => 275668)
--- trunk/LayoutTests/ChangeLog 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/LayoutTests/ChangeLog 2021-04-08 18:41:23 UTC (rev 275668)
@@ -1,3 +1,17 @@
+2021-04-08 Chris Dumez <[email protected]>
+
+ OfflineAudioContext objects are leaking
+ https://bugs.webkit.org/show_bug.cgi?id=224279
+
+ Reviewed by Darin Adler.
+
+ Add layout test coverage.
+
+ * webaudio/OfflineAudioContext/offlineaudiocontext-leak-after-rendering-expected.txt: Added.
+ * webaudio/OfflineAudioContext/offlineaudiocontext-leak-after-rendering.html: Added.
+ * webaudio/OfflineAudioContext/offlineaudiocontext-leak-expected.txt: Added.
+ * webaudio/OfflineAudioContext/offlineaudiocontext-leak.html: Added.
+
2021-04-08 Andres Gonzalez <[email protected]>
VoiceOver does not echo text insertions and deletions when a contenteditable div has a non editable descendant element with a content editable child
Added: trunk/LayoutTests/webaudio/OfflineAudioContext/offlineaudiocontext-leak-after-rendering-expected.txt (0 => 275668)
--- trunk/LayoutTests/webaudio/OfflineAudioContext/offlineaudiocontext-leak-after-rendering-expected.txt (rev 0)
+++ trunk/LayoutTests/webaudio/OfflineAudioContext/offlineaudiocontext-leak-after-rendering-expected.txt 2021-04-08 18:41:23 UTC (rev 275668)
@@ -0,0 +1,11 @@
+Makes sure that the OfflineAudioContext objects are not leaking after rendering.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS internals.numberOfBaseAudioContexts() >= instancesToCreate is true
+PASS internals.numberOfBaseAudioContexts() < instancesToCreate is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/webaudio/OfflineAudioContext/offlineaudiocontext-leak-after-rendering.html (0 => 275668)
--- trunk/LayoutTests/webaudio/OfflineAudioContext/offlineaudiocontext-leak-after-rendering.html (rev 0)
+++ trunk/LayoutTests/webaudio/OfflineAudioContext/offlineaudiocontext-leak-after-rendering.html 2021-04-08 18:41:23 UTC (rev 275668)
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+description("Makes sure that the OfflineAudioContext objects are not leaking after rendering.");
+jsTestIsAsync = true;
+
+const instancesToCreate = 100;
+
+function test() {
+ let promises = [];
+ for (let i = 0; i < instancesToCreate; i++) {
+ let context = new OfflineAudioContext(2, 1, 44100);
+ promises.push(context.startRendering());
+ }
+ shouldBeTrue("internals.numberOfBaseAudioContexts() >= instancesToCreate");
+ Promise.all(promises).then((values) => {
+ gc();
+ setTimeout(() => {
+ gc();
+ shouldBeTrue("internals.numberOfBaseAudioContexts() < instancesToCreate");
+ finishJSTest();
+ }, 0);
+ });
+}
+
+test();
+</script>
+</body>
+</html>
Added: trunk/LayoutTests/webaudio/OfflineAudioContext/offlineaudiocontext-leak-expected.txt (0 => 275668)
--- trunk/LayoutTests/webaudio/OfflineAudioContext/offlineaudiocontext-leak-expected.txt (rev 0)
+++ trunk/LayoutTests/webaudio/OfflineAudioContext/offlineaudiocontext-leak-expected.txt 2021-04-08 18:41:23 UTC (rev 275668)
@@ -0,0 +1,11 @@
+Makes sure that the OfflineAudioContext objects are not leaking.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS internals.numberOfBaseAudioContexts() >= instancesToCreate is true
+PASS internals.numberOfBaseAudioContexts() < instancesToCreate is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/webaudio/OfflineAudioContext/offlineaudiocontext-leak.html (0 => 275668)
--- trunk/LayoutTests/webaudio/OfflineAudioContext/offlineaudiocontext-leak.html (rev 0)
+++ trunk/LayoutTests/webaudio/OfflineAudioContext/offlineaudiocontext-leak.html 2021-04-08 18:41:23 UTC (rev 275668)
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src=""
+<script>
+description("Makes sure that the OfflineAudioContext objects are not leaking.");
+jsTestIsAsync = true;
+
+const instancesToCreate = 100;
+
+function allocateContexts() {
+ let contexts = [];
+ for (let i = 0; i < instancesToCreate; i++)
+ contexts.push(new OfflineAudioContext(2, 1, 44100));
+ shouldBeTrue("internals.numberOfBaseAudioContexts() >= instancesToCreate");
+}
+
+allocateContexts();
+gc();
+setTimeout(() => {
+ gc();
+ shouldBeTrue("internals.numberOfBaseAudioContexts() < instancesToCreate");
+ finishJSTest();
+}, 0);
+</script>
+</body>
+</html>
Modified: trunk/Source/WTF/ChangeLog (275667 => 275668)
--- trunk/Source/WTF/ChangeLog 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WTF/ChangeLog 2021-04-08 18:41:23 UTC (rev 275668)
@@ -1,3 +1,23 @@
+2021-04-08 Chris Dumez <[email protected]>
+
+ OfflineAudioContext objects are leaking
+ https://bugs.webkit.org/show_bug.cgi?id=224279
+
+ Reviewed by Darin Adler.
+
+ Add flag that can be passed when constructing a WeakPtr to disable threading assertions.
+ This is useful for cases where we know it is safe due to locking but we'd like to use a
+ WeakPtr instead of a raw pointer because it is safer.
+
+ * wtf/WeakPtr.h:
+ (WTF::WeakPtr::get const):
+ (WTF::WeakPtr::operator-> const):
+ (WTF::WeakPtr::operator* const):
+ (WTF::WeakPtr::WeakPtr):
+ (WTF::WeakPtrFactory::createWeakPtr const):
+ (WTF::=):
+ (WTF::makeWeakPtr):
+
2021-04-08 Simon Fraser <[email protected]>
Copy-constructed Vectors should not have excess capacity
Modified: trunk/Source/WTF/wtf/WeakPtr.h (275667 => 275668)
--- trunk/Source/WTF/wtf/WeakPtr.h 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WTF/wtf/WeakPtr.h 2021-04-08 18:41:23 UTC (rev 275668)
@@ -38,6 +38,8 @@
static void decrement() { }
};
+enum class EnableWeakPtrThreadingAssertions : bool { No, Yes };
+
template<typename Counter = EmptyCounter> class WeakPtrImpl : public ThreadSafeRefCounted<WeakPtrImpl<Counter>> {
WTF_MAKE_NONCOPYABLE(WeakPtrImpl);
WTF_MAKE_FAST_ALLOCATED;
@@ -91,7 +93,7 @@
T* get() const
{
// FIXME: Our GC threads currently need to get opaque pointers from WeakPtrs and have to be special-cased.
- ASSERT(!m_impl || Thread::mayBeGCThread() || m_impl->wasConstructedOnMainThread() == isMainThread());
+ ASSERT(!m_impl || !m_shouldEnableAssertions || Thread::mayBeGCThread() || m_impl->wasConstructedOnMainThread() == isMainThread());
return m_impl ? static_cast<T*>(m_impl->template get<T>()) : nullptr;
}
@@ -104,13 +106,13 @@
T* operator->() const
{
- ASSERT(!m_impl || m_impl->wasConstructedOnMainThread() == isMainThread());
+ ASSERT(!m_impl || !m_shouldEnableAssertions || m_impl->wasConstructedOnMainThread() == isMainThread());
return get();
}
T& operator*() const
{
- ASSERT(!m_impl || m_impl->wasConstructedOnMainThread() == isMainThread());
+ ASSERT(!m_impl || !m_shouldEnableAssertions || m_impl->wasConstructedOnMainThread() == isMainThread());
return *get();
}
@@ -121,12 +123,19 @@
template<typename, typename> friend class WeakPtr;
template<typename, typename> friend class WeakPtrFactory;
- explicit WeakPtr(Ref<WeakPtrImpl<Counter>>&& ref)
+ explicit WeakPtr(Ref<WeakPtrImpl<Counter>>&& ref, EnableWeakPtrThreadingAssertions shouldEnableAssertions)
: m_impl(WTFMove(ref))
+#if ASSERT_ENABLED
+ , m_shouldEnableAssertions(shouldEnableAssertions == EnableWeakPtrThreadingAssertions::Yes)
+#endif
{
+ UNUSED_PARAM(shouldEnableAssertions);
}
RefPtr<WeakPtrImpl<Counter>> m_impl;
+#if ASSERT_ENABLED
+ bool m_shouldEnableAssertions { true };
+#endif
};
// Note: you probably want to inherit from CanMakeWeakPtr rather than use this directly.
@@ -159,12 +168,12 @@
m_impl = WeakPtrImpl<Counter>::create(const_cast<T*>(&object));
}
- template<typename U> WeakPtr<U, Counter> createWeakPtr(U& object) const
+ template<typename U> WeakPtr<U, Counter> createWeakPtr(U& object, EnableWeakPtrThreadingAssertions enableWeakPtrThreadingAssertions = EnableWeakPtrThreadingAssertions::Yes) const
{
initializeIfNeeded(object);
ASSERT(&object == m_impl->template get<T>());
- return WeakPtr<U, Counter>(makeRef(*m_impl));
+ return WeakPtr<U, Counter>(makeRef(*m_impl), enableWeakPtrThreadingAssertions);
}
void revokeAll()
@@ -244,16 +253,16 @@
return *this;
}
-template<typename T> inline auto makeWeakPtr(T& object)
+template<typename T> inline auto makeWeakPtr(T& object, EnableWeakPtrThreadingAssertions enableWeakPtrThreadingAssertions = EnableWeakPtrThreadingAssertions::Yes)
{
- return object.weakPtrFactory().template createWeakPtr<T>(object);
+ return object.weakPtrFactory().template createWeakPtr<T>(object, enableWeakPtrThreadingAssertions);
}
-template<typename T> inline auto makeWeakPtr(T* ptr) -> decltype(makeWeakPtr(*ptr))
+template<typename T> inline auto makeWeakPtr(T* ptr, EnableWeakPtrThreadingAssertions enableWeakPtrThreadingAssertions = EnableWeakPtrThreadingAssertions::Yes) -> decltype(makeWeakPtr(*ptr))
{
if (!ptr)
return { };
- return makeWeakPtr(*ptr);
+ return makeWeakPtr(*ptr, enableWeakPtrThreadingAssertions);
}
template<typename T, typename U, typename Counter> inline bool operator==(const WeakPtr<T, Counter>& a, const WeakPtr<U, Counter>& b)
@@ -289,6 +298,7 @@
} // namespace WTF
using WTF::CanMakeWeakPtr;
+using WTF::EnableWeakPtrThreadingAssertions;
using WTF::WeakPtr;
using WTF::WeakPtrFactory;
using WTF::WeakPtrFactoryInitialization;
Modified: trunk/Source/WebCore/ChangeLog (275667 => 275668)
--- trunk/Source/WebCore/ChangeLog 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/ChangeLog 2021-04-08 18:41:23 UTC (rev 275668)
@@ -1,3 +1,178 @@
+2021-04-08 Chris Dumez <[email protected]>
+
+ OfflineAudioContext objects are leaking
+ https://bugs.webkit.org/show_bug.cgi?id=224279
+
+ Reviewed by Darin Adler.
+
+ OfflineAudioContext objects were always leaking due to 2 reference cycles:
+ 1. BaseAudioContext -> m_listener (AudioListener) -> m_positionX (AudioParam) -> m_context (BaseAudioContext)
+ 2. BaseAudioContext -> m_destinationNode (AudioDestinationNode) -> m_context (BaseAudioContext)
+
+ For reference cycle 1, I made AudioSummingJunction (base class of AudioParam) hold a weak pointer to the
+ AudioContext instead of a Ref<>. I don't think there is a good reason for an AudioSummingJunction (AudioParam
+ or AudioNodeInput) to keep its AudioContext alive. AudioNodes already keep their AudioContext alive.
+ AudioNodeInputs and AudioParams are associated to AudioNodes.
+
+ For reference cycle 2, I made AudioDestinationNode not hold a strong pointer to its context but instead a
+ weak pointer. Since keeping an AudioDestinationNode alive should keep its AudioContext alive, I made it so
+ that ref'ing the AudioDestinationNode refs its BaseAudioContext. Also, BaseAudioContext::m_destinationNode
+ is now a UniqueRef<> instead of a RefPtr<> to avoid a cycle.
+
+ Tests: webaudio/OfflineAudioContext/offlineaudiocontext-leak-after-rendering.html
+ webaudio/OfflineAudioContext/offlineaudiocontext-leak.html
+
+ * Modules/webaudio/AudioContext.cpp:
+ (WebCore::AudioContext::baseLatency):
+ (WebCore::AudioContext::getOutputTimestamp):
+ (WebCore::AudioContext::close):
+ (WebCore::AudioContext::destination):
+ (WebCore::AudioContext::destination const):
+ (WebCore::AudioContext::suspendRendering):
+ (WebCore::AudioContext::resumeRendering):
+ (WebCore::AudioContext::startRendering):
+ (WebCore::AudioContext::lazyInitialize):
+ (WebCore::AudioContext::mediaState const):
+ (WebCore::AudioContext::mayResumePlayback):
+ (WebCore::AudioContext::suspendPlayback):
+ (WebCore::AudioContext::pageMutedStateDidChange):
+ * Modules/webaudio/AudioContext.h:
+ - Drop some null checks now that m_destinationNode can no longer be null.
+ - Capture an ActiveDOMObject::PendingActivity when doing async work that ends up resolving a Promise, so that
+ we keep both the object and its wrapper alive for the duration of the async work.
+
+ * Modules/webaudio/AudioDestinationNode.cpp:
+ (WebCore::AudioDestinationNode::ref):
+ (WebCore::AudioDestinationNode::deref):
+ * Modules/webaudio/AudioDestinationNode.h:
+ Have AudioDestinationNode override AudioNode's ref() / deref() to forward the refcounting
+ to its BaseAudioContext, instead of using the AudioNode's internal refCount.
+
+ * Modules/webaudio/AudioListener.cpp:
+ (WebCore::AudioListener::updateValuesIfNeeded):
+ (WebCore::AudioListener::setPosition):
+ (WebCore::AudioListener::setOrientation):
+ Add some null-checks for AudioParam::context() now that AudioParam holds a WeakPtr to its
+ context.
+
+ * Modules/webaudio/AudioNode.cpp:
+ (WebCore::AudioNode::toWeakOrStrongContext):
+ (WebCore::AudioNode::AudioNode):
+ (WebCore::AudioNode::connect):
+ (WebCore::AudioNode::sampleRate const):
+ (WebCore::AudioNode::markNodeForDeletionIfNecessary):
+ (WebCore::AudioNode::contextForBindings):
+ (WebCore::AudioNode::context):
+ (WebCore::AudioNode::context const):
+ * Modules/webaudio/AudioNode.h:
+ - Hold the BaseAudioContext as a WeakPtr instead of a Ref<> if the AudioNode is an
+ AudioDestinationNode. This avoids a reference cycle since the BaseAudioContext owns the
+ AudioDestinationNode. Even though we are using a WeakPtr, it is safe to assume that
+ the context is not null because ref'ing an AudioDestinationNode refs its BaseAudioContext.
+ - Make sure markNodeForDeletionIfNecessary() has no effect for AudioDestinationNode since
+ BaseAudioContext now owns the AudioDestinationNode when we take care of destroying its
+ destination node when destroyed.
+
+ * Modules/webaudio/AudioNodeInput.cpp:
+ (WebCore::AudioNodeInput::connect):
+ (WebCore::AudioNodeInput::disconnect):
+ (WebCore::AudioNodeInput::outputEnabledStateChanged):
+ (WebCore::AudioNodeInput::updateInternalBus):
+ (WebCore::AudioNodeInput::bus):
+ (WebCore::AudioNodeInput::internalSummingBus):
+ (WebCore::AudioNodeInput::sumAllConnections):
+ (WebCore::AudioNodeInput::pull):
+ Add assertions that the context is not null. There were already assertions that we are the graph
+ owner, which means we are holding the BaseAudioContext's lock, which means that the audio context
+ is alive.
+
+ * Modules/webaudio/AudioParam.cpp:
+ (WebCore::AudioParam::value):
+ (WebCore::AudioParam::setValueForBindings):
+ (WebCore::AudioParam::smooth):
+ (WebCore::AudioParam::hasSampleAccurateValues const):
+ (WebCore::AudioParam::calculateSampleAccurateValues):
+ (WebCore::AudioParam::calculateFinalValues):
+ (WebCore::AudioParam::calculateTimelineValues):
+ (WebCore::AudioParam::connect):
+ (WebCore::AudioParam::disconnect):
+ Add null-checks for the AudioContext now that the AudioParam is only holding a WeakPtr to its
+ BaseAudioContext (to avoid a reference cycle).
+
+ * Modules/webaudio/AudioSummingJunction.cpp:
+ (WebCore::AudioSummingJunction::AudioSummingJunction):
+ (WebCore::AudioSummingJunction::~AudioSummingJunction):
+ (WebCore::AudioSummingJunction::markRenderingStateAsDirty):
+ (WebCore::AudioSummingJunction::addOutput):
+ (WebCore::AudioSummingJunction::removeOutput):
+ (WebCore::AudioSummingJunction::updateRenderingState):
+ (WebCore::AudioSummingJunction::outputEnabledStateChanged):
+ * Modules/webaudio/AudioSummingJunction.h:
+ (WebCore::AudioSummingJunction::context):
+ (WebCore::AudioSummingJunction::context const):
+ - Hold a WeakPtr to the BaseAudioContext to avoid a reference cycle.
+ - Deal with the fact that the audio context may be null now that we're holding a WeakPtr
+ to it (except when holding the graph lock)
+
+ * Modules/webaudio/BaseAudioContext.cpp:
+ (WebCore::BaseAudioContext::BaseAudioContext):
+ (WebCore::BaseAudioContext::~BaseAudioContext):
+ (WebCore::BaseAudioContext::numberOfInstances):
+ (WebCore::BaseAudioContext::lazyInitialize):
+ (WebCore::BaseAudioContext::clear):
+ (WebCore::BaseAudioContext::uninitialize):
+ (WebCore::BaseAudioContext::stop):
+ (WebCore::BaseAudioContext::sampleRate const):
+ (WebCore::BaseAudioContext::decodeAudioData):
+ (WebCore::BaseAudioContext::markForDeletion):
+ (WebCore::BaseAudioContext::deleteMarkedNodes):
+ (WebCore::BaseAudioContext::setPendingActivity):
+ (WebCore::BaseAudioContext::workletIsReady):
+ * Modules/webaudio/BaseAudioContext.h:
+ (WebCore::BaseAudioContext::destination):
+ (WebCore::BaseAudioContext::destination const):
+ (WebCore::BaseAudioContext::currentSampleFrame const):
+ (WebCore::BaseAudioContext::currentTime const):
+ - Switch m_destinationNode from RefPtr<> to UniqueRef<> since the AudioContext is now
+ the owner of the destinationNode and since refing the destination node actually refs
+ its BaseAudioContext.
+ - Drop some null checks now that m_destinationNode can no longer be null.
+ - Rename makePendingActivity() to setPendingActivity() to avoid a naming conflict with
+ ActiveDOMObject::makePendingActivity().
+
+ * Modules/webaudio/DefaultAudioDestinationNode.h:
+ * Modules/webaudio/OfflineAudioDestinationNode.h:
+ - Drop create() factory functions and make the constructor public now that the BaseAudioContext owns
+ its destination node via a UniqueRef<>.
+ - Make some member functions public as they are virtual and they now called on the subclass instead of the
+ base class (and these functions are public in the base class).
+
+ * Modules/webaudio/OfflineAudioContext.cpp:
+ (WebCore::OfflineAudioContext::startOfflineRendering):
+ (WebCore::OfflineAudioContext::resumeOfflineRendering):
+ * Modules/webaudio/OfflineAudioContext.h:
+ - Drop some null checks now that m_destinationNode can no longer be null.
+ - Capture an ActiveDOMObject::PendingActivity when doing async work that ends up resolving a Promise, so that
+ we keep both the object and its wrapper alive for the duration of the async work.
+
+ * Modules/webaudio/WebKitAudioContext.cpp:
+ (WebCore::WebKitAudioContext::close):
+ Drop null checks for the destination node now that it can never be null.
+
+ * dom/ShadowRoot.cpp:
+ * rendering/FloatingObjects.cpp:
+ * rendering/RootInlineBox.cpp:
+ Update classes used of size restrictions since the size of a WeakPtr is not longer the same as the size of a
+ pointer when debug assertions are enabled. As long as they are the same size in release builds, there is no
+ memory use concern.
+
+ * testing/Internals.cpp:
+ (WebCore::Internals::numberOfBaseAudioContexts const):
+ * testing/Internals.h:
+ * testing/Internals.idl:
+ Add testing function to check how many BaseAudioContexts are alive, so that we can write layout tests and check
+ for leaks.
+
2021-04-08 Youenn Fablet <[email protected]>
Streams: new test failure for canceling the branches of an errored tee'd stream
Modified: trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/AudioContext.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -152,15 +152,11 @@
{
lazyInitialize();
- auto* destination = this->destination();
- return destination ? static_cast<double>(destination->framesPerBuffer()) / sampleRate() : 0.;
+ return static_cast<double>(destination().framesPerBuffer()) / sampleRate();
}
AudioTimestamp AudioContext::getOutputTimestamp(DOMWindow& window)
{
- if (!destination())
- return { 0, 0 };
-
auto& performance = window.performance();
auto position = outputPosition();
@@ -182,7 +178,7 @@
return;
}
- if (state() == State::Closed || !destinationNode()) {
+ if (state() == State::Closed) {
promise.resolve();
return;
}
@@ -191,17 +187,22 @@
lazyInitialize();
- destinationNode()->close([this, protectedThis = makeRef(*this)] {
+ destination().close([this, activity = makePendingActivity(*this)] {
setState(State::Closed);
uninitialize();
});
}
-DefaultAudioDestinationNode* AudioContext::destination()
+DefaultAudioDestinationNode& AudioContext::destination()
{
- return static_cast<DefaultAudioDestinationNode*>(BaseAudioContext::destination());
+ return static_cast<DefaultAudioDestinationNode&>(BaseAudioContext::destination());
}
+const DefaultAudioDestinationNode& AudioContext::destination() const
+{
+ return static_cast<const DefaultAudioDestinationNode&>(BaseAudioContext::destination());
+}
+
void AudioContext::suspendRendering(DOMPromiseDeferred<void>&& promise)
{
if (isOfflineContext()) {
@@ -209,7 +210,7 @@
return;
}
- if (isStopped() || state() == State::Closed || !destinationNode()) {
+ if (isStopped() || state() == State::Closed) {
promise.reject(Exception { InvalidStateError, "Context is closed"_s });
return;
}
@@ -223,7 +224,7 @@
lazyInitialize();
- destinationNode()->suspend([this, protectedThis = makeRef(*this), promise = WTFMove(promise)](Optional<Exception>&& exception) mutable {
+ destination().suspend([this, activity = makePendingActivity(*this), promise = WTFMove(promise)](Optional<Exception>&& exception) mutable {
if (exception) {
promise.reject(WTFMove(*exception));
return;
@@ -240,7 +241,7 @@
return;
}
- if (isStopped() || state() == State::Closed || !destinationNode()) {
+ if (isStopped() || state() == State::Closed) {
promise.reject(Exception { InvalidStateError, "Context is closed"_s });
return;
}
@@ -254,7 +255,7 @@
lazyInitialize();
- destinationNode()->resume([this, protectedThis = makeRef(*this), promise = WTFMove(promise)](Optional<Exception>&& exception) mutable {
+ destination().resume([this, activity = makePendingActivity(*this), promise = WTFMove(promise)](Optional<Exception>&& exception) mutable {
if (exception) {
promise.reject(WTFMove(*exception));
return;
@@ -293,10 +294,10 @@
if (isStopped() || !willBeginPlayback() || m_wasSuspendedByScript)
return;
- makePendingActivity();
+ setPendingActivity();
lazyInitialize();
- destination()->startRendering([this, protectedThis = makeRef(*this)](Optional<Exception>&& exception) {
+ destination().startRendering([this, protectedThis = makeRef(*this)](Optional<Exception>&& exception) {
if (!exception)
setState(State::Running);
});
@@ -309,7 +310,7 @@
BaseAudioContext::lazyInitialize();
if (isInitialized()) {
- if (destinationNode() && state() != State::Running) {
+ if (state() != State::Running) {
// This starts the audio thread. The destination node's provideInput() method will now be called repeatedly to render audio.
// Each time provideInput() is called, a portion of the audio stream is rendered. Let's call this time period a "render quantum".
// NOTE: for now default AudioContext does not need an explicit startRendering() call from _javascript_.
@@ -346,7 +347,7 @@
MediaProducer::MediaStateFlags AudioContext::mediaState() const
{
- if (!isStopped() && destinationNode() && destinationNode()->isPlayingAudio())
+ if (!isStopped() && destination().isPlayingAudio())
return MediaProducer::IsPlayingAudio;
return MediaProducer::IsNotPlaying;
@@ -354,7 +355,7 @@
void AudioContext::mayResumePlayback(bool shouldResume)
{
- if (!destinationNode() || state() == State::Closed || state() == State::Running)
+ if (state() == State::Closed || !isInitialized() || state() == State::Running)
return;
if (!shouldResume) {
@@ -367,7 +368,7 @@
lazyInitialize();
- destinationNode()->resume([this, protectedThis = makeRef(*this)](Optional<Exception>&& exception) {
+ destination().resume([this, protectedThis = makeRef(*this)](Optional<Exception>&& exception) {
setState(exception ? State::Suspended : State::Running);
});
}
@@ -441,12 +442,12 @@
void AudioContext::suspendPlayback()
{
- if (!destinationNode() || state() == State::Closed)
+ if (state() == State::Closed || !isInitialized())
return;
lazyInitialize();
- destinationNode()->suspend([this, protectedThis = makeRef(*this)](Optional<Exception>&& exception) {
+ destination().suspend([this, protectedThis = makeRef(*this)](Optional<Exception>&& exception) {
if (exception)
return;
@@ -468,8 +469,8 @@
void AudioContext::pageMutedStateDidChange()
{
- if (destinationNode() && document() && document()->page())
- destinationNode()->setMuted(document()->page()->isAudioMuted());
+ if (document() && document()->page())
+ destination().setMuted(document()->page()->isAudioMuted());
}
void AudioContext::mediaCanStart(Document& document)
Modified: trunk/Source/WebCore/Modules/webaudio/AudioContext.h (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/AudioContext.h 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/AudioContext.h 2021-04-08 18:41:23 UTC (rev 275668)
@@ -55,7 +55,8 @@
void close(DOMPromiseDeferred<void>&&);
- DefaultAudioDestinationNode* destination();
+ DefaultAudioDestinationNode& destination();
+ const DefaultAudioDestinationNode& destination() const;
double baseLatency();
AudioTimestamp getOutputTimestamp(DOMWindow&);
Modified: trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -146,6 +146,16 @@
context().isPlayingAudioDidChange();
}
+void AudioDestinationNode::ref()
+{
+ context().ref();
+}
+
+void AudioDestinationNode::deref()
+{
+ context().deref();
+}
+
} // namespace WebCore
#endif // ENABLE(WEB_AUDIO)
Modified: trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.h (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.h 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/AudioDestinationNode.h 2021-04-08 18:41:23 UTC (rev 275668)
@@ -66,6 +66,10 @@
bool isPlayingAudio() const { return m_isEffectivelyPlayingAudio; }
void setMuted(bool muted) { m_muted = muted; }
+ // AudioDestinationNodes are owned by the BaseAudioContext so we forward the refcounting to its BaseAudioContext.
+ void ref() final;
+ void deref() final;
+
protected:
double tailTime() const override { return 0; }
double latencyTime() const override { return 0; }
Modified: trunk/Source/WebCore/Modules/webaudio/AudioListener.cpp (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/AudioListener.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/AudioListener.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -91,7 +91,10 @@
void AudioListener::updateValuesIfNeeded(size_t framesToProcess)
{
- double currentTime = positionX().context().currentTime();
+ if (!positionX().context())
+ return;
+
+ double currentTime = positionX().context()->currentTime();
if (m_lastUpdateTime != currentTime) {
// Time has changed. Update all of the automation values now.
m_lastUpdateTime = currentTime;
@@ -177,8 +180,10 @@
ExceptionOr<void> AudioListener::setPosition(float x, float y, float z)
{
ASSERT(isMainThread());
+ if (!m_positionX->context())
+ return { };
- double now = m_positionX->context().currentTime();
+ double now = m_positionX->context()->currentTime();
auto result = m_positionX->setValueAtTime(x, now);
if (result.hasException())
@@ -201,8 +206,10 @@
ExceptionOr<void> AudioListener::setOrientation(float x, float y, float z, float upX, float upY, float upZ)
{
ASSERT(isMainThread());
+ if (!m_forwardX->context())
+ return { };
- double now = m_forwardX->context().currentTime();
+ double now = m_forwardX->context()->currentTime();
auto result = m_forwardX->setValueAtTime(x, now);
if (result.hasException())
Modified: trunk/Source/WebCore/Modules/webaudio/AudioNode.cpp (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/AudioNode.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNode.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -99,9 +99,17 @@
return values[static_cast<size_t>(enumerationValue)];
}
+auto AudioNode::toWeakOrStrongContext(BaseAudioContext& context, NodeType nodeType) -> WeakOrStrongContext
+{
+ // Destination nodes are owned by the BaseAudioContext so we use WeakPtr to avoid a retain cycle.
+ if (nodeType == AudioNode::NodeTypeDestination)
+ return makeWeakPtr(context, EnableWeakPtrThreadingAssertions::No); // WebAudio code uses locking when accessing the context.
+ return makeRef(context);
+}
+
AudioNode::AudioNode(BaseAudioContext& context, NodeType type)
: m_nodeType(type)
- , m_context(context)
+ , m_context(toWeakOrStrongContext(context, type))
#if !RELEASE_LOG_DISABLED
, m_logger(context.logger())
, m_logIdentifier(context.nextAudioNodeLogIdentifier())
@@ -215,7 +223,7 @@
if (outputIndex >= numberOfOutputs())
return Exception { IndexSizeError, "Output index exceeds number of outputs"_s };
- if (&context() != ¶m.context())
+ if (&context() != param.context())
return Exception { SyntaxError, "Node and AudioParam belong to different audio contexts"_s };
auto* output = this->output(outputIndex);
@@ -367,7 +375,7 @@
float AudioNode::sampleRate() const
{
- return m_context->sampleRate();
+ return context().sampleRate();
}
ExceptionOr<void> AudioNode::setChannelCount(unsigned channelCount)
@@ -625,6 +633,10 @@
if (m_connectionRefCount || m_normalRefCount || m_isMarkedForDeletion)
return;
+ // AudioDestinationNodes are owned by their BaseAudioContext so there is no need to mark them for deletion.
+ if (nodeType() == NodeTypeDestination)
+ return;
+
// All references are gone - we need to go away.
for (auto& output : m_outputs)
output->disconnectAll(); // This will deref() nodes we're connected to.
@@ -661,11 +673,12 @@
context().deleteMarkedNodes();
}
-Variant<RefPtr<BaseAudioContext>, RefPtr<WebKitAudioContext>> AudioNode::contextForBindings() const
+Variant<RefPtr<BaseAudioContext>, RefPtr<WebKitAudioContext>> AudioNode::contextForBindings()
{
- if (m_context->isWebKitAudioContext())
- return makeRefPtr(static_cast<WebKitAudioContext&>(m_context.get()));
- return makeRefPtr(m_context.get());
+ auto& context = this->context();
+ if (context.isWebKitAudioContext())
+ return makeRefPtr(static_cast<WebKitAudioContext&>(context));
+ return makeRefPtr(context);
}
void AudioNode::derefWithLock()
@@ -699,6 +712,24 @@
return { };
}
+BaseAudioContext& AudioNode::context()
+{
+ return WTF::switchOn(m_context, [](Ref<BaseAudioContext>& context) -> BaseAudioContext& {
+ return context.get();
+ }, [](WeakPtr<BaseAudioContext>& context) -> BaseAudioContext& {
+ return *context;
+ });
+}
+
+const BaseAudioContext& AudioNode::context() const
+{
+ return WTF::switchOn(m_context, [](const Ref<BaseAudioContext>& context) -> const BaseAudioContext& {
+ return context.get();
+ }, [](const WeakPtr<BaseAudioContext>& context) -> const BaseAudioContext& {
+ return *context;
+ });
+}
+
#if DEBUG_AUDIONODE_REFERENCES
bool AudioNode::s_isNodeCountInitialized = false;
Modified: trunk/Source/WebCore/Modules/webaudio/AudioNode.h (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/AudioNode.h 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNode.h 2021-04-08 18:41:23 UTC (rev 275668)
@@ -87,16 +87,16 @@
AudioNode(BaseAudioContext&, NodeType);
virtual ~AudioNode();
- BaseAudioContext& context() { return m_context.get(); }
- const BaseAudioContext& context() const { return m_context.get(); }
+ BaseAudioContext& context();
+ const BaseAudioContext& context() const;
- Variant<RefPtr<BaseAudioContext>, RefPtr<WebKitAudioContext>> contextForBindings() const;
+ Variant<RefPtr<BaseAudioContext>, RefPtr<WebKitAudioContext>> contextForBindings();
NodeType nodeType() const { return m_nodeType; }
// Can be called from main thread or context's audio thread.
- void ref();
- void deref();
+ virtual void ref();
+ virtual void deref();
void incrementConnectionCount();
void decrementConnectionCount();
@@ -230,6 +230,9 @@
virtual void updatePullStatus() { }
private:
+ using WeakOrStrongContext = Variant<Ref<BaseAudioContext>, WeakPtr<BaseAudioContext>>;
+ static WeakOrStrongContext toWeakOrStrongContext(BaseAudioContext&, NodeType);
+
// EventTarget
EventTargetInterface eventTargetInterface() const override;
ScriptExecutionContext* scriptExecutionContext() const final;
@@ -236,8 +239,9 @@
volatile bool m_isInitialized { false };
NodeType m_nodeType;
- Ref<BaseAudioContext> m_context;
+ WeakOrStrongContext m_context;
+
Vector<std::unique_ptr<AudioNodeInput>> m_inputs;
Vector<std::unique_ptr<AudioNodeOutput>> m_outputs;
Modified: trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.cpp (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNodeInput.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -47,7 +47,8 @@
void AudioNodeInput::connect(AudioNodeOutput* output)
{
- ASSERT(context().isGraphOwner());
+ ASSERT(context());
+ ASSERT(context()->isGraphOwner());
ASSERT(output && node());
if (!output || !node())
@@ -59,7 +60,8 @@
void AudioNodeInput::disconnect(AudioNodeOutput* output)
{
- ASSERT(context().isGraphOwner());
+ ASSERT(context());
+ ASSERT(context()->isGraphOwner());
ASSERT(output && node());
if (!output || !node())
@@ -76,7 +78,8 @@
void AudioNodeInput::outputEnabledStateChanged(AudioNodeOutput& output)
{
- ASSERT(context().isGraphOwner());
+ ASSERT(context());
+ ASSERT(context()->isGraphOwner());
AudioSummingJunction::outputEnabledStateChanged(output);
// Propagate disabled state to outputs.
@@ -96,7 +99,8 @@
void AudioNodeInput::updateInternalBus()
{
- ASSERT(context().isAudioThread() && context().isGraphOwner());
+ ASSERT(context());
+ ASSERT(context()->isAudioThread() && context()->isGraphOwner());
unsigned numberOfInputChannels = numberOfChannels();
@@ -126,7 +130,8 @@
AudioBus* AudioNodeInput::bus()
{
- ASSERT(context().isAudioThread());
+ ASSERT(context());
+ ASSERT(context()->isAudioThread());
// Handle single connection specially to allow for in-place processing.
if (numberOfRenderingConnections() == 1 && node()->channelCountMode() == ChannelCountMode::Max)
@@ -138,7 +143,8 @@
AudioBus* AudioNodeInput::internalSummingBus()
{
- ASSERT(context().isAudioThread());
+ ASSERT(context());
+ ASSERT(context()->isAudioThread());
return m_internalSummingBus.get();
}
@@ -145,7 +151,8 @@
void AudioNodeInput::sumAllConnections(AudioBus* summingBus, size_t framesToProcess)
{
- ASSERT(context().isAudioThread());
+ ASSERT(context());
+ ASSERT(context()->isAudioThread());
// We shouldn't be calling this method if there's only one connection, since it's less efficient.
ASSERT(numberOfRenderingConnections() > 1 || node()->channelCountMode() != ChannelCountMode::Max);
@@ -171,7 +178,8 @@
AudioBus* AudioNodeInput::pull(AudioBus* inPlaceBus, size_t framesToProcess)
{
- ASSERT(context().isAudioThread());
+ ASSERT(context());
+ ASSERT(context()->isAudioThread());
auto numberOfRenderingConnections = this->numberOfRenderingConnections();
// Handle single connection case.
Modified: trunk/Source/WebCore/Modules/webaudio/AudioParam.cpp (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/AudioParam.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/AudioParam.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -69,8 +69,8 @@
float AudioParam::value()
{
// Update value for timeline.
- if (context().isAudioThread()) {
- auto timelineValue = m_timeline.valueForContextTime(context(), m_value, minValue(), maxValue());
+ if (context() && context()->isAudioThread()) {
+ auto timelineValue = m_timeline.valueForContextTime(*context(), m_value, minValue(), maxValue());
if (timelineValue)
m_value = *timelineValue;
}
@@ -96,7 +96,11 @@
ASSERT(isMainThread());
setValue(value);
- auto result = setValueAtTime(m_value, context().currentTime());
+
+ if (!context())
+ return { };
+
+ auto result = setValueAtTime(m_value, context()->currentTime());
if (result.hasException())
return result.releaseException();
return { };
@@ -118,9 +122,12 @@
bool AudioParam::smooth()
{
+ if (!context())
+ return true;
+
// If values have been explicitly scheduled on the timeline, then use the exact value.
// Smoothing effectively is performed by the timeline.
- auto timelineValue = m_timeline.valueForContextTime(context(), m_value, minValue(), maxValue());
+ auto timelineValue = m_timeline.valueForContextTime(*context(), m_value, minValue(), maxValue());
if (timelineValue)
m_value = *timelineValue;
@@ -145,10 +152,13 @@
ExceptionOr<AudioParam&> AudioParam::setValueAtTime(float value, double startTime)
{
+ if (!context())
+ return *this;
+
if (startTime < 0)
return Exception { RangeError, "startTime must be a positive value"_s };
- startTime = std::max(startTime, context().currentTime());
+ startTime = std::max(startTime, context()->currentTime());
auto result = m_timeline.setValueAtTime(value, Seconds { startTime });
if (result.hasException())
return result.releaseException();
@@ -157,11 +167,14 @@
ExceptionOr<AudioParam&> AudioParam::linearRampToValueAtTime(float value, double endTime)
{
+ if (!context())
+ return *this;
+
if (endTime < 0)
return Exception { RangeError, "endTime must be a positive value"_s };
- endTime = std::max(endTime, context().currentTime());
- auto result = m_timeline.linearRampToValueAtTime(value, Seconds { endTime }, m_value, Seconds { context().currentTime() });
+ endTime = std::max(endTime, context()->currentTime());
+ auto result = m_timeline.linearRampToValueAtTime(value, Seconds { endTime }, m_value, Seconds { context()->currentTime() });
if (result.hasException())
return result.releaseException();
return *this;
@@ -174,8 +187,8 @@
if (endTime < 0)
return Exception { RangeError, "endTime must be a positive value"_s };
- endTime = std::max(endTime, context().currentTime());
- auto result = m_timeline.exponentialRampToValueAtTime(value, Seconds { endTime }, m_value, Seconds { context().currentTime() });
+ endTime = std::max(endTime, context()->currentTime());
+ auto result = m_timeline.exponentialRampToValueAtTime(value, Seconds { endTime }, m_value, Seconds { context()->currentTime() });
if (result.hasException())
return result.releaseException();
return *this;
@@ -183,12 +196,15 @@
ExceptionOr<AudioParam&> AudioParam::setTargetAtTime(float target, double startTime, float timeConstant)
{
+ if (!context())
+ return *this;
+
if (startTime < 0)
return Exception { RangeError, "startTime must be a positive value"_s };
if (timeConstant < 0)
return Exception { RangeError, "timeConstant must be a positive value"_s };
- startTime = std::max(startTime, context().currentTime());
+ startTime = std::max(startTime, context()->currentTime());
auto result = m_timeline.setTargetAtTime(target, Seconds { startTime }, timeConstant);
if (result.hasException())
return result.releaseException();
@@ -197,6 +213,9 @@
ExceptionOr<AudioParam&> AudioParam::setValueCurveAtTime(Vector<float>&& curve, double startTime, double duration)
{
+ if (!context())
+ return *this;
+
if (curve.size() < 2)
return Exception { InvalidStateError, "Array must have a length of at least 2"_s };
if (startTime < 0)
@@ -204,7 +223,7 @@
if (duration <= 0)
return Exception { RangeError, "duration must be a strictly positive value"_s };
- startTime = std::max(startTime, context().currentTime());
+ startTime = std::max(startTime, context()->currentTime());
auto result = m_timeline.setValueCurveAtTime(WTFMove(curve), Seconds { startTime }, Seconds { duration });
if (result.hasException())
return result.releaseException();
@@ -237,7 +256,10 @@
if (numberOfRenderingConnections())
return true;
- return m_timeline.hasValues(context().currentSampleFrame(), context().sampleRate());
+ if (!context())
+ return false;
+
+ return m_timeline.hasValues(context()->currentSampleFrame(), context()->sampleRate());
}
float AudioParam::finalValue()
@@ -249,7 +271,7 @@
void AudioParam::calculateSampleAccurateValues(float* values, unsigned numberOfValues)
{
- bool isSafe = context().isAudioThread() && values && numberOfValues;
+ bool isSafe = context() && context()->isAudioThread() && values && numberOfValues;
ASSERT(isSafe);
if (!isSafe)
return;
@@ -259,7 +281,7 @@
void AudioParam::calculateFinalValues(float* values, unsigned numberOfValues, bool sampleAccurate)
{
- bool isGood = context().isAudioThread() && values && numberOfValues;
+ bool isGood = context() && context()->isAudioThread() && values && numberOfValues;
ASSERT(isGood);
if (!isGood)
return;
@@ -271,7 +293,7 @@
calculateTimelineValues(values, numberOfValues);
} else {
// Calculate control-rate (k-rate) intrinsic value.
- auto timelineValue = m_timeline.valueForContextTime(context(), m_value, minValue(), maxValue());
+ auto timelineValue = m_timeline.valueForContextTime(*context(), m_value, minValue(), maxValue());
if (timelineValue)
m_value = *timelineValue;
@@ -313,10 +335,13 @@
void AudioParam::calculateTimelineValues(float* values, unsigned numberOfValues)
{
+ if (!context())
+ return;
+
// Calculate values for this render quantum.
// Normally numberOfValues will equal AudioUtilities::renderQuantumSize (the render quantum size).
- double sampleRate = context().sampleRate();
- size_t startFrame = context().currentSampleFrame();
+ double sampleRate = context()->sampleRate();
+ size_t startFrame = context()->currentSampleFrame();
size_t endFrame = startFrame + numberOfValues;
// Note we're running control rate at the sample-rate.
@@ -326,7 +351,8 @@
void AudioParam::connect(AudioNodeOutput* output)
{
- ASSERT(context().isGraphOwner());
+ ASSERT(context());
+ ASSERT(context()->isGraphOwner());
ASSERT(output);
if (!output)
@@ -341,7 +367,8 @@
void AudioParam::disconnect(AudioNodeOutput* output)
{
- ASSERT(context().isGraphOwner());
+ ASSERT(context());
+ ASSERT(context()->isGraphOwner());
ASSERT(output);
if (!output)
Modified: trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -35,21 +35,22 @@
namespace WebCore {
AudioSummingJunction::AudioSummingJunction(BaseAudioContext& context)
- : m_context(context)
+ : m_context(makeWeakPtr(context, EnableWeakPtrThreadingAssertions::No)) // WebAudio code uses locking when accessing the context.
{
}
AudioSummingJunction::~AudioSummingJunction()
{
- if (m_renderingStateNeedUpdating)
- context().removeMarkedSummingJunction(this);
+ if (m_renderingStateNeedUpdating && context())
+ context()->removeMarkedSummingJunction(this);
}
void AudioSummingJunction::markRenderingStateAsDirty()
{
- ASSERT(context().isGraphOwner());
+ ASSERT(context());
+ ASSERT(context()->isGraphOwner());
if (!m_renderingStateNeedUpdating && canUpdateState()) {
- context().markSummingJunctionDirty(this);
+ context()->markSummingJunctionDirty(this);
m_renderingStateNeedUpdating = true;
}
}
@@ -56,7 +57,8 @@
bool AudioSummingJunction::addOutput(AudioNodeOutput& output)
{
- ASSERT(context().isGraphOwner());
+ ASSERT(context());
+ ASSERT(context()->isGraphOwner());
if (!m_outputs.add(&output).isNewEntry)
return false;
@@ -74,7 +76,8 @@
bool AudioSummingJunction::removeOutput(AudioNodeOutput& output)
{
- ASSERT(context().isGraphOwner());
+ ASSERT(context());
+ ASSERT(context()->isGraphOwner());
if (!m_outputs.remove(&output))
return false;
@@ -94,7 +97,8 @@
void AudioSummingJunction::updateRenderingState()
{
- ASSERT(context().isAudioThread() && context().isGraphOwner());
+ ASSERT(context());
+ ASSERT(context()->isAudioThread() && context()->isGraphOwner());
if (m_renderingStateNeedUpdating && canUpdateState()) {
// Copy from m_outputs to m_renderingOutputs.
@@ -128,7 +132,8 @@
void AudioSummingJunction::outputEnabledStateChanged(AudioNodeOutput& output)
{
- ASSERT(context().isGraphOwner());
+ ASSERT(context());
+ ASSERT(context()->isGraphOwner());
if (!m_pendingRenderingOutputs) {
// Heap allocations are forbidden on the audio thread for performance reasons so we need to
// explicitly allow the following allocation(s).
Modified: trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.h (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.h 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/AudioSummingJunction.h 2021-04-08 18:41:23 UTC (rev 275668)
@@ -28,6 +28,7 @@
#include <iterator>
#include <wtf/HashSet.h>
#include <wtf/Vector.h>
+#include <wtf/WeakPtr.h>
namespace WebCore {
@@ -42,8 +43,8 @@
virtual ~AudioSummingJunction();
// Can be called from any thread.
- BaseAudioContext& context() { return m_context; }
- const BaseAudioContext& context() const { return m_context; }
+ BaseAudioContext* context() { return m_context.get(); }
+ const BaseAudioContext* context() const { return m_context.get(); }
// This copies m_outputs to m_renderingOutputs. Please see comments for these lists below.
// This must be called when we own the context's graph lock in the audio thread at the very start or end of the render quantum.
@@ -134,7 +135,7 @@
unsigned maximumNumberOfChannels() const;
private:
- Ref<BaseAudioContext> m_context;
+ WeakPtr<BaseAudioContext> m_context;
// m_renderingOutputs is a copy of m_outputs which will never be modified during the graph rendering on the audio thread.
// This is the list which is used by the rendering code.
Modified: trunk/Source/WebCore/Modules/webaudio/BaseAudioContext.cpp (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/BaseAudioContext.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/BaseAudioContext.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -115,6 +115,7 @@
return sampleRate >= 3000 && sampleRate <= 384000;
}
+static unsigned numberOfBaseAudioContexts = 0;
unsigned BaseAudioContext::s_hardwareContextCount = 0;
// Constructor for rendering to the audio hardware.
@@ -125,16 +126,17 @@
, m_logIdentifier(uniqueLogIdentifier())
#endif
, m_worklet(AudioWorklet::create(*this))
+ , m_destinationNode(makeUniqueRef<DefaultAudioDestinationNode>(*this, contextOptions.sampleRate))
, m_listener(isLegacyWebKitAudioContext == IsLegacyWebKitAudioContext::Yes ? Ref<AudioListener>(WebKitAudioListener::create(*this)) : AudioListener::create(*this))
{
+ ++numberOfBaseAudioContexts;
+
// According to spec AudioContext must die only after page navigate.
// Lets mark it as ActiveDOMObject with pending activity and unmark it in clear method.
- makePendingActivity();
+ setPendingActivity();
FFTFrame::initialize();
- m_destinationNode = DefaultAudioDestinationNode::create(*this, contextOptions.sampleRate);
-
// Unlike OfflineAudioContext, AudioContext does not require calling resume() to start rendering.
// Lazy initialization starts rendering so we schedule a task here to make sure lazy initialization
// ends up happening, even if no audio node gets constructed.
@@ -156,21 +158,20 @@
, m_worklet(AudioWorklet::create(*this))
, m_isOfflineContext(true)
, m_renderTarget(WTFMove(renderTarget))
+ , m_destinationNode(makeUniqueRef<OfflineAudioDestinationNode>(*this, numberOfChannels, sampleRate, m_renderTarget.copyRef()))
, m_listener(isLegacyWebKitAudioContext == IsLegacyWebKitAudioContext::Yes ? Ref<AudioListener>(WebKitAudioListener::create(*this)) : AudioListener::create(*this))
{
+ ++numberOfBaseAudioContexts;
FFTFrame::initialize();
-
- // Create a new destination for offline rendering.
- m_destinationNode = OfflineAudioDestinationNode::create(*this, numberOfChannels, sampleRate, m_renderTarget.copyRef());
}
BaseAudioContext::~BaseAudioContext()
{
+ ASSERT(numberOfBaseAudioContexts);
+ --numberOfBaseAudioContexts;
#if DEBUG_AUDIONODE_REFERENCES
fprintf(stderr, "%p: BaseAudioContext::~AudioContext()\n", this);
#endif
- ASSERT(!m_isInitialized);
- ASSERT(m_isStopScheduled);
ASSERT(m_nodesToDelete.isEmpty());
ASSERT(m_referencedSourceNodes.isEmpty());
ASSERT(m_automaticPullNodes.isEmpty());
@@ -180,6 +181,11 @@
// FIXME: Can we assert that m_deferredBreakConnectionList is empty?
}
+unsigned BaseAudioContext::numberOfInstances()
+{
+ return numberOfBaseAudioContexts;
+}
+
void BaseAudioContext::lazyInitialize()
{
if (isStopped())
@@ -193,8 +199,7 @@
if (m_isAudioThreadFinished)
return;
- if (m_destinationNode)
- m_destinationNode->initialize();
+ m_destinationNode->initialize();
m_isInitialized = true;
}
@@ -203,10 +208,6 @@
{
auto protectedThis = makeRef(*this);
- // We have to release our reference to the destination node before the context will ever be deleted since the destination node holds a reference to the context.
- if (m_destinationNode)
- m_destinationNode = nullptr;
-
// Audio thread is dead. Nobody will schedule node deletion action. Let's do it ourselves.
do {
deleteMarkedNodes();
@@ -226,8 +227,7 @@
return;
// This stops the audio thread and all audio rendering.
- if (m_destinationNode)
- m_destinationNode->uninitialize();
+ m_destinationNode->uninitialize();
// Don't allow the context to initialize a second time after it's already been explicitly uninitialized.
m_isAudioThreadFinished = true;
@@ -292,6 +292,7 @@
ALWAYS_LOG(LOGIDENTIFIER);
ASSERT(isMainThread());
+ auto protectedThis = makeRef(*this);
// Usually ScriptExecutionContext calls stop twice.
if (m_isStopScheduled)
@@ -317,7 +318,7 @@
float BaseAudioContext::sampleRate() const
{
- return m_destinationNode ? m_destinationNode->sampleRate() : AudioDestination::hardwareSampleRate();
+ return m_destinationNode->sampleRate();
}
bool BaseAudioContext::wouldTaintOrigin(const URL& url) const
@@ -346,7 +347,7 @@
if (!m_audioDecoder)
m_audioDecoder = makeUnique<AsyncAudioDecoder>();
- m_audioDecoder->decodeAsync(WTFMove(audioData), sampleRate(), [this, activity = ActiveDOMObject::makePendingActivity(*this), successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback), promise = WTFMove(promise)](ExceptionOr<Ref<AudioBuffer>>&& result) mutable {
+ m_audioDecoder->decodeAsync(WTFMove(audioData), sampleRate(), [this, activity = makePendingActivity(*this), successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback), promise = WTFMove(promise)](ExceptionOr<Ref<AudioBuffer>>&& result) mutable {
queueTaskKeepingObjectAlive(*this, TaskSource::InternalAsyncTask, [successCallback = WTFMove(successCallback), errorCallback = WTFMove(errorCallback), promise = WTFMove(promise), result = WTFMove(result)]() mutable {
if (result.hasException()) {
if (promise)
@@ -759,6 +760,7 @@
void BaseAudioContext::markForDeletion(AudioNode& node)
{
ASSERT(isGraphOwner());
+ ASSERT_WITH_MESSAGE(node.nodeType() != AudioNode::NodeTypeDestination, "Destination node is owned by the BaseAudioContext");
if (isAudioThreadFinished())
m_nodesToDelete.append(&node);
@@ -821,6 +823,8 @@
for (unsigned i = 0; i < numberOfOutputs; ++i)
m_dirtyAudioNodeOutputs.remove(node->output(i));
+ ASSERT_WITH_MESSAGE(node->nodeType() != AudioNode::NodeTypeDestination, "Destination node is owned by the BaseAudioContext");
+
// Finally, delete it.
delete node;
}
@@ -1028,10 +1032,10 @@
m_pendingActivity = nullptr;
}
-void BaseAudioContext::makePendingActivity()
+void BaseAudioContext::setPendingActivity()
{
if (!m_pendingActivity)
- m_pendingActivity = ActiveDOMObject::makePendingActivity(*this);
+ m_pendingActivity = makePendingActivity(*this);
}
PeriodicWave& BaseAudioContext::periodicWave(OscillatorType type)
@@ -1087,8 +1091,7 @@
// If we're already rendering when the worklet becomes ready, we need to restart
// rendering in order to switch to the audio worklet thread.
- if (m_destinationNode)
- m_destinationNode->restartRendering();
+ m_destinationNode->restartRendering();
}
#if !RELEASE_LOG_DISABLED
Modified: trunk/Source/WebCore/Modules/webaudio/BaseAudioContext.h (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/BaseAudioContext.h 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/BaseAudioContext.h 2021-04-08 18:41:23 UTC (rev 275668)
@@ -107,6 +107,9 @@
using ThreadSafeRefCounted::ref;
using ThreadSafeRefCounted::deref;
+ // This is used for lifetime testing.
+ WEBCORE_EXPORT static unsigned numberOfInstances();
+
Document* document() const;
bool isInitialized() const;
@@ -113,9 +116,11 @@
bool isOfflineContext() const { return m_isOfflineContext; }
virtual bool isWebKitAudioContext() const { return false; }
- AudioDestinationNode* destination() { return m_destinationNode.get(); }
- size_t currentSampleFrame() const { return m_destinationNode ? m_destinationNode->currentSampleFrame() : 0; }
- double currentTime() const { return m_destinationNode ? m_destinationNode->currentTime() : 0.; }
+ AudioDestinationNode& destination() { return m_destinationNode.get(); }
+ const AudioDestinationNode& destination() const { return m_destinationNode.get(); }
+
+ size_t currentSampleFrame() const { return m_destinationNode->currentSampleFrame(); }
+ double currentTime() const { return m_destinationNode->currentTime(); }
float sampleRate() const;
unsigned long activeSourceCount() const { return static_cast<unsigned long>(m_activeSourceCount); }
@@ -296,12 +301,10 @@
BaseAudioContext(Document&, IsLegacyWebKitAudioContext, unsigned numberOfChannels, float sampleRate, RefPtr<AudioBuffer>&& renderTarget);
void clearPendingActivity();
- void makePendingActivity();
+ void setPendingActivity();
void lockInternal(bool& mustReleaseLock);
- AudioDestinationNode* destinationNode() const { return m_destinationNode.get(); }
-
virtual void uninitialize();
#if !RELEASE_LOG_DISABLED
@@ -383,7 +386,7 @@
Vector<Vector<DOMPromiseDeferred<void>>> m_stateReactions;
RefPtr<AudioBuffer> m_renderTarget;
- RefPtr<AudioDestinationNode> m_destinationNode;
+ UniqueRef<AudioDestinationNode> m_destinationNode;
Ref<AudioListener> m_listener;
unsigned m_connectionCount { 0 };
Modified: trunk/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.h (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.h 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/DefaultAudioDestinationNode.h 2021-04-08 18:41:23 UTC (rev 275668)
@@ -33,10 +33,7 @@
class DefaultAudioDestinationNode final : public AudioDestinationNode {
WTF_MAKE_ISO_ALLOCATED(DefaultAudioDestinationNode);
public:
- static Ref<DefaultAudioDestinationNode> create(BaseAudioContext& context, Optional<float> sampleRate = WTF::nullopt)
- {
- return adoptRef(*new DefaultAudioDestinationNode(context, sampleRate));
- }
+ explicit DefaultAudioDestinationNode(BaseAudioContext&, Optional<float> = WTF::nullopt);
virtual ~DefaultAudioDestinationNode();
@@ -43,10 +40,11 @@
unsigned framesPerBuffer() const;
void startRendering(CompletionHandler<void(Optional<Exception>&&)>&&) final;
+ void resume(CompletionHandler<void(Optional<Exception>&&)>&&) final;
+ void suspend(CompletionHandler<void(Optional<Exception>&&)>&&) final;
+ void close(CompletionHandler<void()>&&) final;
private:
- DefaultAudioDestinationNode(BaseAudioContext&, Optional<float>);
-
void createDestination();
void clearDestination();
void recreateDestination();
@@ -60,10 +58,7 @@
bool requiresTailProcessing() const final { return false; }
void enableInput(const String& inputDeviceId) final;
- void resume(CompletionHandler<void(Optional<Exception>&&)>&&) final;
- void suspend(CompletionHandler<void(Optional<Exception>&&)>&&) final;
void restartRendering() final;
- void close(CompletionHandler<void()>&&) final;
unsigned maxChannelCount() const final;
bool isPlaying() final;
Modified: trunk/Source/WebCore/Modules/webaudio/OfflineAudioContext.cpp (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/OfflineAudioContext.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/OfflineAudioContext.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -109,13 +109,13 @@
lazyInitialize();
- destination()->startRendering([this, promise = WTFMove(promise), pendingActivity = ActiveDOMObject::makePendingActivity(*this)](Optional<Exception>&& exception) mutable {
+ destination().startRendering([this, promise = WTFMove(promise), pendingActivity = makePendingActivity(*this)](Optional<Exception>&& exception) mutable {
if (exception) {
promise->reject(WTFMove(*exception));
return;
}
- makePendingActivity();
+ setPendingActivity();
m_pendingOfflineRenderingPromise = WTFMove(promise);
m_didStartOfflineRendering = true;
setState(State::Running);
@@ -171,13 +171,13 @@
}
ASSERT(state() == AudioContextState::Suspended);
- destination()->startRendering([this, promise = WTFMove(promise), pendingActivity = ActiveDOMObject::makePendingActivity(*this)](Optional<Exception>&& exception) mutable {
+ destination().startRendering([this, promise = WTFMove(promise), pendingActivity = makePendingActivity(*this)](Optional<Exception>&& exception) mutable {
if (exception) {
promise->reject(WTFMove(*exception));
return;
}
- makePendingActivity();
+ setPendingActivity();
setState(State::Running);
promise->resolve();
});
Modified: trunk/Source/WebCore/Modules/webaudio/OfflineAudioContext.h (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/OfflineAudioContext.h 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/OfflineAudioContext.h 2021-04-08 18:41:23 UTC (rev 275668)
@@ -47,7 +47,8 @@
unsigned length() const { return m_length; }
bool shouldSuspend() final;
- OfflineAudioDestinationNode* destination() { return static_cast<OfflineAudioDestinationNode*>(BaseAudioContext::destination()); }
+ OfflineAudioDestinationNode& destination() { return static_cast<OfflineAudioDestinationNode&>(BaseAudioContext::destination()); }
+ const OfflineAudioDestinationNode& destination() const { return static_cast<const OfflineAudioDestinationNode&>(BaseAudioContext::destination()); }
// mustReleaseLock is set to true if we acquired the lock in this method call and caller must unlock(), false if it was previously acquired.
void offlineLock(bool& mustReleaseLock);
Modified: trunk/Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.h (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.h 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/OfflineAudioDestinationNode.h 2021-04-08 18:41:23 UTC (rev 275668)
@@ -37,10 +37,7 @@
class OfflineAudioDestinationNode final : public AudioDestinationNode {
WTF_MAKE_ISO_ALLOCATED(OfflineAudioDestinationNode);
public:
- static Ref<OfflineAudioDestinationNode> create(BaseAudioContext& context, unsigned numberOfChannels, float sampleRate, RefPtr<AudioBuffer>&& renderTarget)
- {
- return adoptRef(*new OfflineAudioDestinationNode(context, numberOfChannels, sampleRate, WTFMove(renderTarget)));
- }
+ OfflineAudioDestinationNode(BaseAudioContext&, unsigned numberOfChannels, float sampleRate, RefPtr<AudioBuffer>&& renderTarget);
virtual ~OfflineAudioDestinationNode();
@@ -53,8 +50,6 @@
void startRendering(CompletionHandler<void(Optional<Exception>&&)>&&) final;
private:
- OfflineAudioDestinationNode(BaseAudioContext&, unsigned numberOfChannels, float sampleRate, RefPtr<AudioBuffer>&& renderTarget);
-
enum class OfflineRenderResult { Failure, Suspended, Complete };
OfflineRenderResult offlineRender();
void notifyOfflineRenderingSuspended();
Modified: trunk/Source/WebCore/Modules/webaudio/WebKitAudioContext.cpp (275667 => 275668)
--- trunk/Source/WebCore/Modules/webaudio/WebKitAudioContext.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/Modules/webaudio/WebKitAudioContext.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -200,7 +200,7 @@
return;
}
- if (state() == State::Closed || !destinationNode()) {
+ if (state() == State::Closed) {
promise.resolve();
return;
}
@@ -209,7 +209,7 @@
lazyInitialize();
- destinationNode()->close([this, protectedThis = makeRef(*this)] {
+ destination().close([this, protectedThis = makeRef(*this)] {
setState(State::Closed);
uninitialize();
});
Modified: trunk/Source/WebCore/dom/ShadowRoot.cpp (275667 => 275668)
--- trunk/Source/WebCore/dom/ShadowRoot.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/dom/ShadowRoot.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -54,12 +54,15 @@
uint8_t mode;
void* styleScope;
void* styleSheetList;
- void* host;
+ WeakPtr<Element> host;
void* slotAssignment;
Optional<HashMap<AtomString, AtomString>> partMappings;
};
COMPILE_ASSERT(sizeof(ShadowRoot) == sizeof(SameSizeAsShadowRoot), shadowroot_should_stay_small);
+#if !ASSERT_ENABLED
+COMPILE_ASSERT(sizeof(WeakPtr<Element>) == sizeof(void*), WeakPtr_should_be_same_size_as_raw_pointer);
+#endif
ShadowRoot::ShadowRoot(Document& document, ShadowRootMode type, DelegatesFocus delegatesFocus)
: DocumentFragment(document, CreateShadowRoot)
Modified: trunk/Source/WebCore/rendering/FloatingObjects.cpp (275667 => 275668)
--- trunk/Source/WebCore/rendering/FloatingObjects.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/rendering/FloatingObjects.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -34,7 +34,8 @@
namespace WebCore {
struct SameSizeAsFloatingObject {
- void* pointers[2];
+ WeakPtr<RenderBox> renderer;
+ WeakPtr<RootInlineBox> originatingLine;
LayoutRect rect;
int paginationStrut;
LayoutSize size;
@@ -42,6 +43,10 @@
};
COMPILE_ASSERT(sizeof(FloatingObject) == sizeof(SameSizeAsFloatingObject), FloatingObject_should_stay_small);
+#if !ASSERT_ENABLED
+COMPILE_ASSERT(sizeof(WeakPtr<RenderBox>) == sizeof(void*), WeakPtr_should_be_same_size_as_raw_pointer);
+COMPILE_ASSERT(sizeof(WeakPtr<RootInlineBox>) == sizeof(void*), WeakPtr_should_be_same_size_as_raw_pointer);
+#endif
FloatingObject::FloatingObject(RenderBox& renderer)
: m_renderer(makeWeakPtr(renderer))
Modified: trunk/Source/WebCore/rendering/RootInlineBox.cpp (275667 => 275668)
--- trunk/Source/WebCore/rendering/RootInlineBox.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/rendering/RootInlineBox.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -47,10 +47,14 @@
struct SameSizeAsRootInlineBox : public InlineFlowBox, public CanMakeWeakPtr<RootInlineBox> {
unsigned variables[7];
- void* pointers[3];
+ WeakPtr<RenderObject> lineBreakObj;
+ void* pointers[2];
};
COMPILE_ASSERT(sizeof(RootInlineBox) == sizeof(SameSizeAsRootInlineBox), RootInlineBox_should_stay_small);
+#if !ASSERT_ENABLED
+COMPILE_ASSERT(sizeof(WeakPtr<RenderObject>) == sizeof(void*), WeakPtr_should_be_same_size_as_raw_pointer);
+#endif
typedef WTF::HashMap<const RootInlineBox*, std::unique_ptr<EllipsisBox>> EllipsisBoxMap;
static EllipsisBoxMap* gEllipsisBoxMap;
Modified: trunk/Source/WebCore/testing/Internals.cpp (275667 => 275668)
--- trunk/Source/WebCore/testing/Internals.cpp 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/testing/Internals.cpp 2021-04-08 18:41:23 UTC (rev 275668)
@@ -2656,6 +2656,15 @@
return document.referencingNodeCount();
}
+unsigned Internals::numberOfBaseAudioContexts() const
+{
+#if ENABLE(WEB_AUDIO)
+ return BaseAudioContext::numberOfInstances();
+#else
+ return 0;
+#endif
+}
+
#if ENABLE(INTERSECTION_OBSERVER)
unsigned Internals::numberOfIntersectionObservers(const Document& document) const
{
Modified: trunk/Source/WebCore/testing/Internals.h (275667 => 275668)
--- trunk/Source/WebCore/testing/Internals.h 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/testing/Internals.h 2021-04-08 18:41:23 UTC (rev 275668)
@@ -455,6 +455,7 @@
unsigned numberOfLiveNodes() const;
unsigned numberOfLiveDocuments() const;
unsigned referencingNodeCount(const Document&) const;
+ unsigned numberOfBaseAudioContexts() const;
#if ENABLE(INTERSECTION_OBSERVER)
unsigned numberOfIntersectionObservers(const Document&) const;
Modified: trunk/Source/WebCore/testing/Internals.idl (275667 => 275668)
--- trunk/Source/WebCore/testing/Internals.idl 2021-04-08 18:37:33 UTC (rev 275667)
+++ trunk/Source/WebCore/testing/Internals.idl 2021-04-08 18:41:23 UTC (rev 275668)
@@ -537,6 +537,7 @@
unsigned long numberOfLiveNodes();
unsigned long numberOfLiveDocuments();
+ unsigned long numberOfBaseAudioContexts();
unsigned long referencingNodeCount(Document document);
[Conditional=INTERSECTION_OBSERVER] unsigned long numberOfIntersectionObservers(Document document);
[Conditional = RESIZE_OBSERVER] unsigned long numberOfResizeObservers(Document document);