Title: [275668] trunk
Revision
275668
Author
[email protected]
Date
2021-04-08 11:41:23 -0700 (Thu, 08 Apr 2021)

Log Message

OfflineAudioContext objects are leaking
https://bugs.webkit.org/show_bug.cgi?id=224279

Reviewed by Darin Adler.

Source/WebCore:

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.

Source/WTF:

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):

LayoutTests:

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.

Modified Paths

Added Paths

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() != &param.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);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to