Title: [252389] trunk
Revision
252389
Author
[email protected]
Date
2019-11-12 18:07:30 -0800 (Tue, 12 Nov 2019)

Log Message

AudioScheduledSourceNodes leak if they have an attached onended EventTarget
https://bugs.webkit.org/show_bug.cgi?id=204087
Source/WebCore:

<rdar://problem/56772102>

Reviewed by Jer Noble.

Previously, if an AudioNode had any attributes that were event
targets we would leak the node as long as the AudioContext was
alive. This patch makes all the AudioNodes with EventTarget
attributes subclasses of ActiveDOMObject. For,
AudioScheduledSourceNodes we will mark the node as non-active when
either the node becomes eligible for deletion from the graph or
the onended event fires. For, ScriptProcessorNodes we mark the
node as non-active when the node becomes eligible for deletion.

Tests: webaudio/finished-audio-buffer-source-nodes-should-be-collectable.html
       webaudio/audiobuffersource-not-gced-until-ended.html

* Modules/webaudio/AudioBufferSourceNode.cpp:
(WebCore::AudioBufferSourceNode::startLater):
(WebCore::AudioBufferSourceNode::start): Deleted.
* Modules/webaudio/AudioBufferSourceNode.h:
* Modules/webaudio/AudioBufferSourceNode.idl:
* Modules/webaudio/AudioContext.h:
* Modules/webaudio/AudioNode.cpp:
(WebCore::AudioNode::deref):
(WebCore::AudioNode::finishDeref):
* Modules/webaudio/AudioNode.h:
(WebCore::AudioNode::didBecomeMarkedForDeletion):
* Modules/webaudio/AudioNode.idl:
* Modules/webaudio/AudioScheduledSourceNode.cpp:
(WebCore::AudioScheduledSourceNode::AudioScheduledSourceNode):
(WebCore::AudioScheduledSourceNode::startLater):
(WebCore::AudioScheduledSourceNode::stopLater):
(WebCore::AudioScheduledSourceNode::didBecomeMarkedForDeletion):
(WebCore::AudioScheduledSourceNode::finish):
(WebCore::AudioScheduledSourceNode::start): Deleted.
(WebCore::AudioScheduledSourceNode::stop): Deleted.
(WebCore::AudioScheduledSourceNode::addEventListener): Deleted.
(WebCore::AudioScheduledSourceNode::removeEventListener): Deleted.
(WebCore::AudioScheduledSourceNode::removeAllEventListeners): Deleted.
* Modules/webaudio/AudioScheduledSourceNode.h:
* Modules/webaudio/OscillatorNode.h:
* Modules/webaudio/OscillatorNode.idl:
* Modules/webaudio/ScriptProcessorNode.cpp:
(WebCore::ScriptProcessorNode::ScriptProcessorNode):
(WebCore::ScriptProcessorNode::~ScriptProcessorNode):
(WebCore::ScriptProcessorNode::didBecomeMarkedForDeletion):
(WebCore::ScriptProcessorNode::process):
(WebCore::ScriptProcessorNode::addEventListener): Deleted.
(WebCore::ScriptProcessorNode::removeEventListener): Deleted.
(WebCore::ScriptProcessorNode::removeAllEventListeners): Deleted.
* Modules/webaudio/ScriptProcessorNode.h:
* Modules/webaudio/ScriptProcessorNode.idl:
* Sources.txt:
* bindings/js/JSAudioNodeCustom.cpp: Removed.

LayoutTests:

Reviewed by Jer Noble.

* webaudio/audiobuffersource-not-gced-until-ended-expected.txt: Added.
* webaudio/audiobuffersource-not-gced-until-ended.html: Added.
* webaudio/finished-audio-buffer-source-nodes-should-be-collectable-expected.txt: Added.
* webaudio/finished-audio-buffer-source-nodes-should-be-collectable.html: Added.

Modified Paths

Added Paths

Removed Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (252388 => 252389)


--- trunk/LayoutTests/ChangeLog	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/LayoutTests/ChangeLog	2019-11-13 02:07:30 UTC (rev 252389)
@@ -1,3 +1,15 @@
+2019-11-12  Keith Miller  <[email protected]>
+
+        AudioScheduledSourceNodes leak if they have an attached onended EventTarget
+        https://bugs.webkit.org/show_bug.cgi?id=204087
+
+        Reviewed by Jer Noble.
+
+        * webaudio/audiobuffersource-not-gced-until-ended-expected.txt: Added.
+        * webaudio/audiobuffersource-not-gced-until-ended.html: Added.
+        * webaudio/finished-audio-buffer-source-nodes-should-be-collectable-expected.txt: Added.
+        * webaudio/finished-audio-buffer-source-nodes-should-be-collectable.html: Added.
+
 2019-11-12  Wenson Hsieh  <[email protected]>
 
         [iOS] WKWebView does not respect system spellchecking preference

Added: trunk/LayoutTests/webaudio/audiobuffersource-not-gced-until-ended-expected.txt (0 => 252389)


--- trunk/LayoutTests/webaudio/audiobuffersource-not-gced-until-ended-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webaudio/audiobuffersource-not-gced-until-ended-expected.txt	2019-11-13 02:07:30 UTC (rev 252389)
@@ -0,0 +1,6 @@
+Tests that AudioBufferSourceNode calls its onended EventListener, even after a GC.
+PASS source.onended called.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/webaudio/audiobuffersource-not-gced-until-ended.html (0 => 252389)


--- trunk/LayoutTests/webaudio/audiobuffersource-not-gced-until-ended.html	                        (rev 0)
+++ trunk/LayoutTests/webaudio/audiobuffersource-not-gced-until-ended.html	2019-11-13 02:07:30 UTC (rev 252389)
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<head>
+    <script src=""
+    <script src=""
+    <script src=""
+    <script>
+        var context;
+
+        function runTest() 
+        {
+            jsTestIsAsync = true;
+
+            var sampleRate = 44100.0;
+            var numberOfFrames = 32;
+            context = new webkitOfflineAudioContext(1, numberOfFrames, sampleRate);
+            var source = context.createBufferSource();
+            source.buffer = createTestBuffer(context, numberOfFrames);
+            source.connect(context.destination);
+            source._onended_ = function() 
+            {
+                testPassed("source.onended called.");
+                finishJSTest();
+            }
+            source.start(0);
+            context.startRendering();
+            // trigger a GC in the next micro task to try and collect source.
+            Promise.resolve().then(gc);
+        }
+    </script>
+
+</head>
+<body _onload_="runTest()">
+    <div>Tests that AudioBufferSourceNode calls its onended EventListener, even after a GC.</div>
+    <div id="console"></div>
+</body>

Added: trunk/LayoutTests/webaudio/finished-audio-buffer-source-nodes-should-be-collectable-expected.txt (0 => 252389)


--- trunk/LayoutTests/webaudio/finished-audio-buffer-source-nodes-should-be-collectable-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/webaudio/finished-audio-buffer-source-nodes-should-be-collectable-expected.txt	2019-11-13 02:07:30 UTC (rev 252389)
@@ -0,0 +1,12 @@
+Tests that AudioBufferSourceNodes are collectable after they trigger their onended event.
+PASS source.onended called.
+PASS source.onended called.
+PASS source.onended called.
+PASS source.onended called.
+PASS source.onended called.
+PASS source.onended called.
+PASS AudioBufferSourceNode was collected after calling onended.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/webaudio/finished-audio-buffer-source-nodes-should-be-collectable.html (0 => 252389)


--- trunk/LayoutTests/webaudio/finished-audio-buffer-source-nodes-should-be-collectable.html	                        (rev 0)
+++ trunk/LayoutTests/webaudio/finished-audio-buffer-source-nodes-should-be-collectable.html	2019-11-13 02:07:30 UTC (rev 252389)
@@ -0,0 +1,66 @@
+<!DOCTYPE html><!-- webkit-test-runner [ jscOptions=--useWeakRefs=true ] -->
+<head>
+    <script src=""
+    <script src=""
+    <script src=""
+    <script>
+        var context;
+        var source;
+        var promises = [];
+        var refs = [];
+        const sampleRate = 44100.0;
+        const numberOfFrames = 32;
+
+        function create(mergerNode, index)
+        {
+            jsTestIsAsync = true;
+
+            source = context.createBufferSource();
+            source.buffer = createTestBuffer(context, numberOfFrames);
+            source.connect(mergerNode, 0, index);
+
+            let resolve;
+            let promise = new Promise((r) => resolve = r);
+            promises.push(promise);
+            refs.push(new WeakRef(source));
+            
+            source._onended_ = function() 
+            {
+                testPassed("source.onended called.");
+                gc();
+                resolve();
+            }
+            source.start(0);
+        }
+      
+        function runTest() 
+        {
+            context = new webkitOfflineAudioContext(1, numberOfFrames, sampleRate);
+            // FIXME: Passing more than 6 seems to fail. 6 is the default value so I'm guessing there's a bug there...
+            const count = 6;
+            let merger = context.createChannelMerger(count);
+            for (let i = 0; i < count; ++i)
+                create(merger, i);
+            merger.connect(context.destination);
+            context.startRendering();
+            
+            Promise.all(promises).then(() => {
+                for (ref of refs) {
+                    if (!ref.deref()) {
+                        testPassed("AudioBufferSourceNode was collected after calling onended.");
+                        finishJSTest();
+                        return;
+                    }
+                }
+
+                testFailed("No AudioBufferSourceNode was collected.");
+                finishJSTest();
+            });
+        }
+    </script>
+
+</head>
+<body _onload_="runTest()">
+    <div>Tests that AudioBufferSourceNodes are collectable after they trigger their onended event.</div>
+    <div id="console"></div>
+</body>

Modified: trunk/Source/WebCore/ChangeLog (252388 => 252389)


--- trunk/Source/WebCore/ChangeLog	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/ChangeLog	2019-11-13 02:07:30 UTC (rev 252389)
@@ -1,3 +1,62 @@
+2019-11-12  Keith Miller  <[email protected]>
+
+        AudioScheduledSourceNodes leak if they have an attached onended EventTarget
+        https://bugs.webkit.org/show_bug.cgi?id=204087
+        <rdar://problem/56772102>
+
+        Reviewed by Jer Noble.
+
+        Previously, if an AudioNode had any attributes that were event
+        targets we would leak the node as long as the AudioContext was
+        alive. This patch makes all the AudioNodes with EventTarget
+        attributes subclasses of ActiveDOMObject. For,
+        AudioScheduledSourceNodes we will mark the node as non-active when
+        either the node becomes eligible for deletion from the graph or
+        the onended event fires. For, ScriptProcessorNodes we mark the
+        node as non-active when the node becomes eligible for deletion.
+
+        Tests: webaudio/finished-audio-buffer-source-nodes-should-be-collectable.html
+               webaudio/audiobuffersource-not-gced-until-ended.html
+
+        * Modules/webaudio/AudioBufferSourceNode.cpp:
+        (WebCore::AudioBufferSourceNode::startLater):
+        (WebCore::AudioBufferSourceNode::start): Deleted.
+        * Modules/webaudio/AudioBufferSourceNode.h:
+        * Modules/webaudio/AudioBufferSourceNode.idl:
+        * Modules/webaudio/AudioContext.h:
+        * Modules/webaudio/AudioNode.cpp:
+        (WebCore::AudioNode::deref):
+        (WebCore::AudioNode::finishDeref):
+        * Modules/webaudio/AudioNode.h:
+        (WebCore::AudioNode::didBecomeMarkedForDeletion):
+        * Modules/webaudio/AudioNode.idl:
+        * Modules/webaudio/AudioScheduledSourceNode.cpp:
+        (WebCore::AudioScheduledSourceNode::AudioScheduledSourceNode):
+        (WebCore::AudioScheduledSourceNode::startLater):
+        (WebCore::AudioScheduledSourceNode::stopLater):
+        (WebCore::AudioScheduledSourceNode::didBecomeMarkedForDeletion):
+        (WebCore::AudioScheduledSourceNode::finish):
+        (WebCore::AudioScheduledSourceNode::start): Deleted.
+        (WebCore::AudioScheduledSourceNode::stop): Deleted.
+        (WebCore::AudioScheduledSourceNode::addEventListener): Deleted.
+        (WebCore::AudioScheduledSourceNode::removeEventListener): Deleted.
+        (WebCore::AudioScheduledSourceNode::removeAllEventListeners): Deleted.
+        * Modules/webaudio/AudioScheduledSourceNode.h:
+        * Modules/webaudio/OscillatorNode.h:
+        * Modules/webaudio/OscillatorNode.idl:
+        * Modules/webaudio/ScriptProcessorNode.cpp:
+        (WebCore::ScriptProcessorNode::ScriptProcessorNode):
+        (WebCore::ScriptProcessorNode::~ScriptProcessorNode):
+        (WebCore::ScriptProcessorNode::didBecomeMarkedForDeletion):
+        (WebCore::ScriptProcessorNode::process):
+        (WebCore::ScriptProcessorNode::addEventListener): Deleted.
+        (WebCore::ScriptProcessorNode::removeEventListener): Deleted.
+        (WebCore::ScriptProcessorNode::removeAllEventListeners): Deleted.
+        * Modules/webaudio/ScriptProcessorNode.h:
+        * Modules/webaudio/ScriptProcessorNode.idl:
+        * Sources.txt:
+        * bindings/js/JSAudioNodeCustom.cpp: Removed.
+
 2019-11-12  Zalan Bujtas  <[email protected]>
 
         [LFC] InlineFormattingState::addInlineRun should take a unique_ptr<Display::Run>

Modified: trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.cpp	2019-11-13 02:07:30 UTC (rev 252389)
@@ -441,7 +441,7 @@
     return output(0)->numberOfChannels();
 }
 
-ExceptionOr<void> AudioBufferSourceNode::start(double when, double grainOffset, Optional<double> optionalGrainDuration)
+ExceptionOr<void> AudioBufferSourceNode::startLater(double when, double grainOffset, Optional<double> optionalGrainDuration)
 {
     double grainDuration = 0;
     if (optionalGrainDuration)

Modified: trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.h (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.h	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.h	2019-11-13 02:07:30 UTC (rev 252389)
@@ -57,7 +57,7 @@
     unsigned numberOfChannels();
 
     // Play-state
-    ExceptionOr<void> start(double when, double grainOffset, Optional<double> grainDuration);
+    ExceptionOr<void> startLater(double when, double grainOffset, Optional<double> grainDuration);
 
     // Note: the attribute was originally exposed as .looping, but to be more consistent in naming with <audio>
     // and with how it's described in the specification, the proper attribute name is .loop
@@ -88,6 +88,8 @@
     // AudioScheduledSourceNode
     void finish() final;
 
+    const char* activeDOMObjectName() const override { return "AudioBufferSourceNode"; }
+
 private:
     AudioBufferSourceNode(AudioContext&, float sampleRate);
 

Modified: trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.idl (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.idl	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/AudioBufferSourceNode.idl	2019-11-13 02:07:30 UTC (rev 252389)
@@ -26,6 +26,7 @@
 [
     Conditional=WEB_AUDIO,
     JSGenerateToJSObject,
+    ActiveDOMObject,
 ] interface AudioBufferSourceNode : AudioNode {
     attribute AudioBuffer? buffer;
 
@@ -44,8 +45,8 @@
     attribute unrestricted double loopStart;
     attribute unrestricted double loopEnd;
 
-    [MayThrowException] void start(optional unrestricted double when = 0, optional unrestricted double grainOffset = 0, optional unrestricted double grainDuration);
-    [MayThrowException] void stop(optional unrestricted double when = 0);
+    [MayThrowException, ImplementedAs=startLater] void start(optional unrestricted double when = 0, optional unrestricted double grainOffset = 0, optional unrestricted double grainDuration);
+    [MayThrowException, ImplementedAs=stopLater] void stop(optional unrestricted double when = 0);
 
     attribute EventHandler onended;
 };

Modified: trunk/Source/WebCore/Modules/webaudio/AudioContext.h (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/AudioContext.h	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/AudioContext.h	2019-11-13 02:07:30 UTC (rev 252389)
@@ -291,6 +291,9 @@
     const SecurityOrigin* origin() const;
     void addConsoleMessage(MessageSource, MessageLevel, const String& message);
 
+    // EventTarget
+    ScriptExecutionContext* scriptExecutionContext() const final;
+
 protected:
     explicit AudioContext(Document&);
     AudioContext(Document&, AudioBuffer* renderTarget);
@@ -320,7 +323,6 @@
     void mediaCanStart(Document&) override;
 
     // EventTarget
-    ScriptExecutionContext* scriptExecutionContext() const final;
     void dispatchEvent(Event&) final;
 
     // MediaProducer

Modified: trunk/Source/WebCore/Modules/webaudio/AudioNode.cpp (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/AudioNode.cpp	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNode.cpp	2019-11-13 02:07:30 UTC (rev 252389)
@@ -509,7 +509,7 @@
         context().lock(mustReleaseLock);
         hasLock = true;
     }
-    
+
     if (hasLock) {
         // This is where the real deref work happens.
         finishDeref(refType);
@@ -561,6 +561,7 @@
                 // Mark for deletion at end of each render quantum or when context shuts down.
                 context().markForDeletion(*this);
                 m_isMarkedForDeletion = true;
+                didBecomeMarkedForDeletion();
             }
         } else if (refType == RefTypeConnection)
             disableOutputsIfNecessary();

Modified: trunk/Source/WebCore/Modules/webaudio/AudioNode.h (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/AudioNode.h	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNode.h	2019-11-13 02:07:30 UTC (rev 252389)
@@ -105,6 +105,7 @@
 
     // Can be called from main thread or context's audio thread.  It must be called while the context's graph lock is held.
     void finishDeref(RefType refType);
+    virtual void didBecomeMarkedForDeletion() { }
 
     // The AudioNodeInput(s) (if any) will already have their input data available when process() is called.
     // Subclasses will take this input data and put the results in the AudioBus(s) of its AudioNodeOutput(s) (if any).

Modified: trunk/Source/WebCore/Modules/webaudio/AudioNode.idl (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/AudioNode.idl	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/AudioNode.idl	2019-11-13 02:07:30 UTC (rev 252389)
@@ -24,7 +24,6 @@
 
 [
     Conditional=WEB_AUDIO,
-    CustomIsReachable,
 ] interface AudioNode : EventTarget {
     readonly attribute AudioContext context;
     readonly attribute unsigned long numberOfInputs;

Modified: trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.cpp	2019-11-13 02:07:30 UTC (rev 252389)
@@ -37,6 +37,7 @@
 #include <algorithm>
 #include <wtf/IsoMallocInlines.h>
 #include <wtf/MathExtras.h>
+#include <wtf/Scope.h>
 
 #if PLATFORM(IOS_FAMILY)
 #include "ScriptController.h"
@@ -50,8 +51,11 @@
 
 AudioScheduledSourceNode::AudioScheduledSourceNode(AudioContext& context, float sampleRate)
     : AudioNode(context, sampleRate)
+    , ActiveDOMObject(context.scriptExecutionContext())
     , m_endTime(UnknownTime)
 {
+    suspendIfNeeded();
+    m_pendingActivity = makePendingActivity(*this);
 }
 
 void AudioScheduledSourceNode::updateSchedulingInfo(size_t quantumFrameSize, AudioBus& outputBus, size_t& quantumFrameOffset, size_t& nonSilentFramesToProcess)
@@ -132,7 +136,7 @@
     }
 }
 
-ExceptionOr<void> AudioScheduledSourceNode::start(double when)
+ExceptionOr<void> AudioScheduledSourceNode::startLater(double when)
 {
     ASSERT(isMainThread());
     ALWAYS_LOG(LOGIDENTIFIER, when);
@@ -150,7 +154,7 @@
     return { };
 }
 
-ExceptionOr<void> AudioScheduledSourceNode::stop(double when)
+ExceptionOr<void> AudioScheduledSourceNode::stopLater(double when)
 {
     ASSERT(isMainThread());
     ALWAYS_LOG(LOGIDENTIFIER, when);
@@ -165,19 +169,27 @@
     return { };
 }
 
+void AudioScheduledSourceNode::didBecomeMarkedForDeletion()
+{
+    ASSERT(context().isGraphOwner());
+    m_pendingActivity = nullptr;
+    ASSERT(!hasPendingActivity());
+}
+
 void AudioScheduledSourceNode::finish()
 {
-    if (m_playbackState != FINISHED_STATE) {
-        // Let the context dereference this AudioNode.
-        context().notifyNodeFinishedProcessing(this);
-        m_playbackState = FINISHED_STATE;
-        context().decrementActiveSourceCount();
-    }
+    ASSERT(!hasFinished());
+    // Let the context dereference this AudioNode.
+    context().notifyNodeFinishedProcessing(this);
+    m_playbackState = FINISHED_STATE;
+    context().decrementActiveSourceCount();
 
-    if (!m_hasEndedListener)
-        return;
-
     context().postTask([this, protectedThis = makeRef(*this)] {
+        auto release = makeScopeExit([&] () {
+            AudioContext::AutoLocker locker(context());
+            m_pendingActivity = nullptr;
+            ASSERT(!hasPendingActivity());
+        });
         if (context().isStopped())
             return;
         this->dispatchEvent(Event::create(eventNames().endedEvent, Event::CanBubble::No, Event::IsCancelable::No));
@@ -184,28 +196,6 @@
     });
 }
 
-bool AudioScheduledSourceNode::addEventListener(const AtomString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
-{
-    bool success = AudioNode::addEventListener(eventType, WTFMove(listener), options);
-    if (success && eventType == eventNames().endedEvent)
-        m_hasEndedListener = hasEventListeners(eventNames().endedEvent);
-    return success;
-}
-
-bool AudioScheduledSourceNode::removeEventListener(const AtomString& eventType, EventListener& listener, const ListenerOptions& options)
-{
-    bool success = AudioNode::removeEventListener(eventType, listener, options);
-    if (success && eventType == eventNames().endedEvent)
-        m_hasEndedListener = hasEventListeners(eventNames().endedEvent);
-    return success;
-}
-
-void AudioScheduledSourceNode::removeAllEventListeners()
-{
-    m_hasEndedListener = false;
-    AudioNode::removeAllEventListeners();
-}
-
 } // namespace WebCore
 
 #endif // ENABLE(WEB_AUDIO)

Modified: trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.h (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.h	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/AudioScheduledSourceNode.h	2019-11-13 02:07:30 UTC (rev 252389)
@@ -28,11 +28,12 @@
 
 #pragma once
 
+#include "ActiveDOMObject.h"
 #include "AudioNode.h"
 
 namespace WebCore {
 
-class AudioScheduledSourceNode : public AudioNode {
+class AudioScheduledSourceNode : public AudioNode, public ActiveDOMObject {
     WTF_MAKE_ISO_ALLOCATED(AudioScheduledSourceNode);
 public:
     // These are the possible states an AudioScheduledSourceNode can be in:
@@ -54,9 +55,11 @@
     
     AudioScheduledSourceNode(AudioContext&, float sampleRate);
 
-    ExceptionOr<void> start(double when);
-    ExceptionOr<void> stop(double when);
+    ExceptionOr<void> startLater(double when);
+    ExceptionOr<void> stopLater(double when);
 
+    void didBecomeMarkedForDeletion() override;
+
     unsigned short playbackState() const { return static_cast<unsigned short>(m_playbackState); }
     bool isPlayingOrScheduled() const { return m_playbackState == PLAYING_STATE || m_playbackState == SCHEDULED_STATE; }
     bool hasFinished() const { return m_playbackState == FINISHED_STATE; }
@@ -75,6 +78,7 @@
 
     PlaybackState m_playbackState { UNSCHEDULED_STATE };
 
+    RefPtr<PendingActivity<AudioScheduledSourceNode>> m_pendingActivity;
     // m_startTime is the time to start playing based on the context's timeline (0 or a time less than the context's current time means "now").
     double m_startTime { 0 }; // in seconds
 
@@ -83,14 +87,7 @@
     // has been reached.
     double m_endTime; // in seconds
 
-    bool m_hasEndedListener { false };
-
     static const double UnknownTime;
-
-private:
-    bool addEventListener(const AtomString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) override;
-    bool removeEventListener(const AtomString& eventType, EventListener&, const ListenerOptions&) override;
-    void removeAllEventListeners() override;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/Modules/webaudio/OscillatorNode.h (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/OscillatorNode.h	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/OscillatorNode.h	2019-11-13 02:07:30 UTC (rev 252389)
@@ -49,6 +49,8 @@
 
     virtual ~OscillatorNode();
 
+    const char* activeDOMObjectName() const override { return "OscillatorNode"; }
+
     Type type() const { return m_type; }
     ExceptionOr<void> setType(Type);
 

Modified: trunk/Source/WebCore/Modules/webaudio/OscillatorNode.idl (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/OscillatorNode.idl	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/OscillatorNode.idl	2019-11-13 02:07:30 UTC (rev 252389)
@@ -34,6 +34,7 @@
 [
     Conditional=WEB_AUDIO,
     JSGenerateToJSObject,
+    ActiveDOMObject,
 ] interface OscillatorNode : AudioNode {
     attribute OscillatorType type;
 
@@ -48,8 +49,8 @@
     readonly attribute AudioParam frequency; // in Hertz
     readonly attribute AudioParam detune; // in Cents
 
-    [MayThrowException] void start(optional unrestricted double when = 0);
-    [MayThrowException] void stop(optional unrestricted double when = 0);
+    [MayThrowException, ImplementedAs=startLater] void start(optional unrestricted double when = 0);
+    [MayThrowException, ImplementedAs=stopLater] void stop(optional unrestricted double when = 0);
 
     void setPeriodicWave(PeriodicWave? wave); // FIXME: The parameter should not be nullable.
 

Modified: trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.cpp	2019-11-13 02:07:30 UTC (rev 252389)
@@ -51,6 +51,7 @@
 
 ScriptProcessorNode::ScriptProcessorNode(AudioContext& context, float sampleRate, size_t bufferSize, unsigned numberOfInputChannels, unsigned numberOfOutputChannels)
     : AudioNode(context, sampleRate)
+    , ActiveDOMObject(context.scriptExecutionContext())
     , m_doubleBufferIndex(0)
     , m_doubleBufferIndexForEvent(0)
     , m_bufferSize(bufferSize)
@@ -59,7 +60,6 @@
     , m_numberOfInputChannels(numberOfInputChannels)
     , m_numberOfOutputChannels(numberOfOutputChannels)
     , m_internalInputBus(AudioBus::create(numberOfInputChannels, AudioNode::ProcessingSizeInFrames, false))
-    , m_hasAudioProcessListener(false)
 {
     // Regardless of the allowed buffer sizes, we still need to process at the granularity of the AudioNode.
     if (m_bufferSize < AudioNode::ProcessingSizeInFrames)
@@ -72,10 +72,13 @@
     addOutput(makeUnique<AudioNodeOutput>(this, numberOfOutputChannels));
 
     initialize();
+    suspendIfNeeded();
+    m_pendingActivity = makePendingActivity(*this);
 }
 
 ScriptProcessorNode::~ScriptProcessorNode()
 {
+    ASSERT(!hasPendingActivity());
     uninitialize();
 }
 
@@ -110,6 +113,13 @@
     AudioNode::uninitialize();
 }
 
+void ScriptProcessorNode::didBecomeMarkedForDeletion()
+{
+    ASSERT(context().isGraphOwner());
+    m_pendingActivity = nullptr;
+    ASSERT(!hasPendingActivity());
+}
+
 void ScriptProcessorNode::process(size_t framesToProcess)
 {
     // Discussion about inputs and outputs:
@@ -118,10 +128,6 @@
     // This node is the producer for inputBuffer and the consumer for outputBuffer.
     // The _javascript_ code is the consumer of inputBuffer and the producer for outputBuffer.
 
-    // Check if audioprocess listener is set.
-    if (!m_hasAudioProcessListener)
-        return;
-
     // Get input and output busses.
     AudioBus* inputBus = this->input(0)->bus();
     AudioBus* outputBus = this->output(0)->bus();
@@ -192,9 +198,6 @@
             m_isRequestOutstanding = true;
 
             callOnMainThread([this] {
-                if (!m_hasAudioProcessListener)
-                    return;
-
                 fireProcessEvent();
 
                 // De-reference to match the ref() call in process().
@@ -256,28 +259,6 @@
     return std::numeric_limits<double>::infinity();
 }
 
-bool ScriptProcessorNode::addEventListener(const AtomString& eventType, Ref<EventListener>&& listener, const AddEventListenerOptions& options)
-{
-    bool success = AudioNode::addEventListener(eventType, WTFMove(listener), options);
-    if (success && eventType == eventNames().audioprocessEvent)
-        m_hasAudioProcessListener = hasEventListeners(eventNames().audioprocessEvent);
-    return success;
-}
-
-bool ScriptProcessorNode::removeEventListener(const AtomString& eventType, EventListener& listener, const ListenerOptions& options)
-{
-    bool success = AudioNode::removeEventListener(eventType, listener, options);
-    if (success && eventType == eventNames().audioprocessEvent)
-        m_hasAudioProcessListener = hasEventListeners(eventNames().audioprocessEvent);
-    return success;
-}
-
-void ScriptProcessorNode::removeAllEventListeners()
-{
-    m_hasAudioProcessListener = false;
-    AudioNode::removeAllEventListeners();
-}
-
 } // namespace WebCore
 
 #endif // ENABLE(WEB_AUDIO)

Modified: trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.h (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.h	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.h	2019-11-13 02:07:30 UTC (rev 252389)
@@ -24,6 +24,7 @@
 
 #pragma once
 
+#include "ActiveDOMObject.h"
 #include "AudioBus.h"
 #include "AudioNode.h"
 #include "EventListener.h"
@@ -44,7 +45,7 @@
 // The "onaudioprocess" attribute is an event listener which will get called periodically with an AudioProcessingEvent which has
 // AudioBuffers for each input and output.
 
-class ScriptProcessorNode final : public AudioNode {
+class ScriptProcessorNode final : public AudioNode, public ActiveDOMObject {
     WTF_MAKE_ISO_ALLOCATED(ScriptProcessorNode);
 public:
     // bufferSize must be one of the following values: 256, 512, 1024, 2048, 4096, 8192, 16384.
@@ -55,11 +56,14 @@
 
     virtual ~ScriptProcessorNode();
 
+    const char* activeDOMObjectName() const override { return "ScriptProcessorNode"; }
+
     // AudioNode
     void process(size_t framesToProcess) override;
     void reset() override;
     void initialize() override;
     void uninitialize() override;
+    void didBecomeMarkedForDeletion() override;
 
     size_t bufferSize() const { return m_bufferSize; }
 
@@ -71,10 +75,6 @@
 
     void fireProcessEvent();
 
-    bool addEventListener(const AtomString& eventType, Ref<EventListener>&&, const AddEventListenerOptions&) override;
-    bool removeEventListener(const AtomString& eventType, EventListener&, const ListenerOptions&) override;
-    void removeAllEventListeners() override;
-
     // Double buffering
     unsigned doubleBufferIndex() const { return m_doubleBufferIndex; }
     void swapBuffers() { m_doubleBufferIndex = 1 - m_doubleBufferIndex; }
@@ -91,7 +91,7 @@
     unsigned m_numberOfOutputChannels;
 
     RefPtr<AudioBus> m_internalInputBus;
-    bool m_hasAudioProcessListener;
+    RefPtr<PendingActivity<ScriptProcessorNode>> m_pendingActivity;
 };
 
 } // namespace WebCore

Modified: trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.idl (252388 => 252389)


--- trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.idl	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Modules/webaudio/ScriptProcessorNode.idl	2019-11-13 02:07:30 UTC (rev 252389)
@@ -26,7 +26,8 @@
 [
     Conditional=WEB_AUDIO,
     JSGenerateToJSObject,
-    JSGenerateToNativeObject
+    JSGenerateToNativeObject,
+    ActiveDOMObject,
 ] interface ScriptProcessorNode : AudioNode {
     // Rendering callback
     attribute EventHandler onaudioprocess;

Modified: trunk/Source/WebCore/Sources.txt (252388 => 252389)


--- trunk/Source/WebCore/Sources.txt	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/Sources.txt	2019-11-13 02:07:30 UTC (rev 252389)
@@ -458,7 +458,6 @@
 animation/KeyframeEffect.cpp
 animation/WebAnimation.cpp
 
-bindings/js/JSAudioNodeCustom.cpp
 bindings/js/CachedModuleScriptLoader.cpp
 bindings/js/CachedScriptFetcher.cpp
 bindings/js/CallTracer.cpp

Deleted: trunk/Source/WebCore/bindings/js/JSAudioNodeCustom.cpp (252388 => 252389)


--- trunk/Source/WebCore/bindings/js/JSAudioNodeCustom.cpp	2019-11-13 01:48:00 UTC (rev 252388)
+++ trunk/Source/WebCore/bindings/js/JSAudioNodeCustom.cpp	2019-11-13 02:07:30 UTC (rev 252389)
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2019 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
- * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
-
-#include "config.h"
-
-#if ENABLE(WEB_AUDIO)
-
-#include "AudioContext.h"
-#include "JSAudioNode.h"
-
-namespace WebCore {
-using namespace JSC;
-
-bool JSAudioNodeOwner::isReachableFromOpaqueRoots(JSC::Handle<JSC::Unknown> handle, void*, SlotVisitor& visitor, const char** reason)
-{
-    auto& node = jsCast<JSAudioNode*>(handle.slot()->asCell())->wrapped();
-
-    if (node.isFiringEventListeners()) {
-        if (UNLIKELY(reason))
-            *reason = "AudioNode is firing event listeners";
-        return true;
-    }
-
-    if (!node.context().hasPendingActivity() || node.context().isClosed())
-        return false;
-
-    if (node.hasEventListeners()) {
-        if (UNLIKELY(reason))
-            *reason = "AudioNode has event listeners";
-        return true;
-    }
-
-    return visitor.containsOpaqueRoot(&node);
-}
-
-} // namespace WebCore
-
-#endif
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to