Bug 1015783 - Add a devtools API for Web Audio; r=padenot,smaug

See bug 980506 for an extensive discussion about this.  This patch adds
three APIs to AudioNode in order for us to be able to build awesome
devtools on top of it.

* Weak reference API.
  This patch allows one to hold a weak reference to all AudioNode's
  using Components.utils.getWeakReference().  That way, the devtool's
  inspection code would not change the lifetime of AudioNodes.
* AudioNode.id
  This is a chrome-only unique and monotonically incrementing ID for
  AudioNode objects.  It is supposed to be used in order for the
  devtools to be able to identify a node without having to keep it
  alive.
* webaudio-node-demise
  This is an observer notification that is called every time an
  AudioNode gets destroyed inside Gecko.  The ID of the corresponding
  node is passed to this notification.
This commit is contained in:
Ehsan Akhgari 2014-06-03 18:28:18 -04:00
parent c7bda4ec38
commit 4f46b92922
9 changed files with 100 additions and 5 deletions

View File

@ -227,7 +227,6 @@ NS_IMPL_CYCLE_COLLECTION_INHERITED(AudioDestinationNode, AudioNode,
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioDestinationNode)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
NS_INTERFACE_MAP_ENTRY(nsIAudioChannelAgentCallback)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END_INHERITING(AudioNode)
NS_IMPL_ADDREF_INHERITED(AudioDestinationNode, AudioNode)

View File

@ -12,7 +12,6 @@
#include "nsIDOMEventListener.h"
#include "nsIAudioChannelAgent.h"
#include "AudioChannelCommon.h"
#include "nsWeakReference.h"
namespace mozilla {
namespace dom {
@ -22,7 +21,6 @@ class AudioContext;
class AudioDestinationNode : public AudioNode
, public nsIDOMEventListener
, public nsIAudioChannelAgentCallback
, public nsSupportsWeakReference
, public MainThreadMediaStreamListener
{
public:

View File

@ -14,6 +14,7 @@ namespace mozilla {
namespace dom {
static const uint32_t INVALID_PORT = 0xffffffff;
static uint32_t gId = 0;
NS_IMPL_CYCLE_COLLECTION_CLASS(AudioNode)
@ -49,6 +50,7 @@ AudioNode::Release()
}
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(AudioNode)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
AudioNode::AudioNode(AudioContext* aContext,
@ -60,6 +62,10 @@ AudioNode::AudioNode(AudioContext* aContext,
, mChannelCount(aChannelCount)
, mChannelCountMode(aChannelCountMode)
, mChannelInterpretation(aChannelInterpretation)
, mId(gId++)
#ifdef DEBUG
, mDemiseNotified(false)
#endif
{
MOZ_ASSERT(aContext);
DOMEventTargetHelper::BindToOwner(aContext->GetParentObject());
@ -72,6 +78,10 @@ AudioNode::~AudioNode()
MOZ_ASSERT(mInputNodes.IsEmpty());
MOZ_ASSERT(mOutputNodes.IsEmpty());
MOZ_ASSERT(mOutputParams.IsEmpty());
#ifdef DEBUG
MOZ_ASSERT(mDemiseNotified,
"The webaudio-node-demise notification must have been sent");
#endif
if (mContext) {
mContext->UpdateNodeCount(-1);
}
@ -385,6 +395,16 @@ AudioNode::DestroyMediaStream()
mStream->Destroy();
mStream = nullptr;
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
nsAutoString id;
id.AppendPrintf("%u", mId);
obs->NotifyObservers(nullptr, "webaudio-node-demise", id.get());
}
#ifdef DEBUG
mDemiseNotified = true;
#endif
}
}

View File

@ -16,6 +16,7 @@
#include "MediaStreamGraph.h"
#include "WebAudioUtils.h"
#include "mozilla/MemoryReporting.h"
#include "nsWeakReference.h"
namespace mozilla {
@ -82,7 +83,8 @@ private:
* still alive, and will still be alive when it receives a message from the
* engine.
*/
class AudioNode : public DOMEventTargetHelper
class AudioNode : public DOMEventTargetHelper,
public nsSupportsWeakReference
{
protected:
// You can only use refcounting to delete this object
@ -133,6 +135,8 @@ public:
virtual uint16_t NumberOfInputs() const { return 1; }
virtual uint16_t NumberOfOutputs() const { return 1; }
uint32_t Id() const { return mId; }
uint32_t ChannelCount() const { return mChannelCount; }
virtual void SetChannelCount(uint32_t aChannelCount, ErrorResult& aRv)
{
@ -266,6 +270,12 @@ private:
uint32_t mChannelCount;
ChannelCountMode mChannelCountMode;
ChannelInterpretation mChannelInterpretation;
const uint32_t mId;
#ifdef DEBUG
// In debug builds, check to make sure that the node demise notification has
// been properly sent before the node is destroyed.
bool mDemiseNotified;
#endif
};
}

View File

@ -42,7 +42,7 @@ MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(AudioContext* aContext,
ProcessedMediaStream* outputStream = static_cast<ProcessedMediaStream*>(mStream.get());
mInputPort = outputStream->AllocateInputPort(aMediaStream->GetStream(),
MediaInputPort::FLAG_BLOCK_INPUT);
mInputStream->AddConsumerToKeepAlive(this);
mInputStream->AddConsumerToKeepAlive(static_cast<nsIDOMEventTarget*>(this));
PrincipalChanged(mInputStream); // trigger enabling/disabling of the connector
mInputStream->AddPrincipalChangeObserver(this);

View File

@ -0,0 +1,3 @@
[DEFAULT]
[test_AudioNodeDevtoolsAPI.html]

View File

@ -8,3 +8,7 @@ MOCHITEST_MANIFESTS += [
'blink/mochitest.ini',
'mochitest.ini',
]
MOCHITEST_CHROME_MANIFESTS += [
'chrome.ini'
]

View File

@ -0,0 +1,55 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test the devtool AudioNode API</title>
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
Components.utils.import('resource://gre/modules/Services.jsm');
SimpleTest.waitForExplicitFinish();
var ac = new AudioContext();
var ids;
var weak;
(function() {
var src1 = ac.createBufferSource();
var src2 = ac.createBufferSource();
ok(src2.id > src1.id, "The ID should be monotonic");
ok(src1.id > ac.destination.id, "The ID of the destination node should be the lowest");
ids = [src1.id, src2.id];
weak = Components.utils.getWeakReference(src1);
is(weak.get(), src1, "The node should support a weak reference");
})();
function observer(subject, topic, data) {
var id = parseInt(data);
var index = ids.indexOf(id);
if (index != -1) {
info("Dropping id " + id + " at index " + index);
ids.splice(index, 1);
if (ids.length == 0) {
SimpleTest.executeSoon(function() {
is(weak.get(), null, "The weak reference must be dropped now");
Services.obs.removeObserver(observer, "webaudio-node-demise");
SimpleTest.finish();
});
}
}
}
Services.obs.addObserver(observer, "webaudio-node-demise", false);
forceCC();
forceCC();
function forceCC() {
SpecialPowers.DOMWindowUtils.cycleCollect();
}
</script>
</pre>
</body>
</html>

View File

@ -43,3 +43,9 @@ interface AudioNode : EventTarget {
};
// Mozilla extension
partial interface AudioNode {
[ChromeOnly]
readonly attribute unsigned long id;
};