Bug 1068270 - Add connectNode and disconnect methods to AudioNodeActor. r=vp
--- a/browser/devtools/webaudioeditor/test/browser.ini
+++ b/browser/devtools/webaudioeditor/test/browser.ini
@@ -17,16 +17,17 @@ support-files =
[browser_audionode-actor-get-param-flags.js]
[browser_audionode-actor-get-params-01.js]
[browser_audionode-actor-get-params-02.js]
[browser_audionode-actor-get-set-param.js]
[browser_audionode-actor-get-type.js]
[browser_audionode-actor-is-source.js]
[browser_audionode-actor-bypass.js]
+[browser_audionode-actor-connectnode-disconnect.js]
[browser_webaudio-actor-simple.js]
[browser_webaudio-actor-destroy-node.js]
[browser_webaudio-actor-connect-param.js]
[browser_wa_destroy-node-01.js]
[browser_wa_first-run.js]
[browser_wa_reset-01.js]
new file mode 100644
--- /dev/null
+++ b/browser/devtools/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
@@ -0,0 +1,44 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Tests that AudioNodeActor#connectNode() and AudioNodeActor#disconnect() work.
+ * Uses the editor front as the actors do not retain connect state.
+ */
+
+function spawnTest() {
+ let { target, panel } = yield initWebAudioEditor(SIMPLE_CONTEXT_URL);
+ let { panelWin } = panel;
+ let { gFront, $, $$, EVENTS, gAudioNodes } = panelWin;
+
+ reload(target);
+
+ let [actors] = yield Promise.all([
+ get3(gFront, "create-node"),
+ waitForGraphRendered(panelWin, 3, 2)
+ ]);
+
+ let [dest, osc, gain] = actors;
+
+ info("Disconnecting oscillator...");
+ osc.disconnect();
+ yield Promise.all([
+ waitForGraphRendered(panelWin, 3, 1),
+ once(gAudioNodes, "disconnect")
+ ]);
+ ok(true, "Oscillator disconnected, event emitted.");
+
+
+ info("Reconnecting oscillator...");
+ osc.connectNode(gain);
+ yield Promise.all([
+ waitForGraphRendered(panelWin, 3, 2),
+ once(gAudioNodes, "connect")
+ ]);
+ ok(true, "Oscillator reconnected.");
+
+
+ yield teardown(panel);
+ finish();
+}
+
--- a/toolkit/devtools/server/actors/webaudio.js
+++ b/toolkit/devtools/server/actors/webaudio.js
@@ -10,17 +10,17 @@ const Services = require("Services");
const { Promise: promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
const events = require("sdk/event/core");
const { on: systemOn, off: systemOff } = require("sdk/system/events");
const protocol = require("devtools/server/protocol");
const { CallWatcherActor, CallWatcherFront } = require("devtools/server/actors/call-watcher");
const { ThreadActor } = require("devtools/server/actors/script");
const { on, once, off, emit } = events;
-const { method, Arg, Option, RetVal } = protocol;
+const { types, method, Arg, Option, RetVal } = protocol;
const AUDIO_GLOBALS = [
"AudioContext", "AudioNode"
];
const NODE_CREATION_METHODS = [
"createBufferSource", "createMediaElementSource", "createMediaStreamSource",
"createMediaStreamDestination", "createScriptProcessor", "createAnalyser",
@@ -104,16 +104,17 @@ const NODE_PROPERTIES = {
"stream": { "MediaStream": true }
}
};
/**
* An Audio Node actor allowing communication to a specific audio node in the
* Audio Context graph.
*/
+types.addActorType("audionode");
let AudioNodeActor = exports.AudioNodeActor = protocol.ActorClass({
typeName: "audionode",
/**
* Create the Audio Node actor.
*
* @param DebuggerServerConnection conn
* The server connection.
@@ -284,17 +285,70 @@ let AudioNodeActor = exports.AudioNodeAc
* corresponding to a property name and current value of the audio node.
*/
getParams: method(function (param) {
let props = Object.keys(NODE_PROPERTIES[this.type]);
return props.map(prop =>
({ param: prop, value: this.getParam(prop), flags: this.getParamFlags(prop) }));
}, {
response: { params: RetVal("json") }
+ }),
+
+ /**
+ * Connects this audionode to another via `node.connect(dest)`.
+ */
+ connectNode: method(function (destActor, output, input) {
+ let srcNode = this.node.get();
+ let destNode = destActor.node.get();
+
+ if (srcNode === null || destNode === null) {
+ return CollectedAudioNodeError();
+ }
+
+ try {
+ // Connect via the unwrapped node, so we can call the
+ // patched method that fires the webaudio actor's `connect-node` event.
+ // Connect directly to the wrapped `destNode`, otherwise
+ // the patched method thinks this is a new node and won't be
+ // able to find it in `_nativeToActorID`.
+ XPCNativeWrapper.unwrap(srcNode).connect(destNode, output, input);
+ } catch (e) {
+ return constructError(e);
+ }
+ }, {
+ request: {
+ destActor: Arg(0, "audionode"),
+ output: Arg(1, "nullable:number"),
+ input: Arg(2, "nullable:number")
+ },
+ response: { error: RetVal("nullable:json") }
+ }),
+
+ /**
+ * Disconnects this audionode from all connections via `node.disconnect()`.
+ */
+ disconnect: method(function (destActor, output) {
+ let node = this.node.get();
+
+ if (node === null) {
+ return CollectedAudioNodeError();
+ }
+
+ try {
+ // Disconnect via the unwrapped node, so we can call the
+ // patched method that fires the webaudio actor's `disconnect` event.
+ XPCNativeWrapper.unwrap(node).disconnect(output);
+ } catch (e) {
+ return constructError(e);
+ }
+ }, {
+ request: { output: Arg(0, "nullable:number") },
+ response: { error: RetVal("nullable:json") }
})
+
});
/**
* The corresponding Front object for the AudioNodeActor.
*/
let AudioNodeFront = protocol.FrontClass(AudioNodeActor, {
initialize: function (client, form) {
protocol.Front.prototype.initialize.call(this, client, form);
@@ -548,16 +602,17 @@ let WebAudioActor = exports.WebAudioActo
},
/**
* Called when one audio node is connected to another.
*/
_onConnectNode: function (source, dest) {
let sourceActor = this._getActorByNativeID(source.id);
let destActor = this._getActorByNativeID(dest.id);
+
emit(this, "connect-node", {
source: sourceActor,
dest: destActor
});
},
/**
* Called when an audio node is connected to an audio param.