Bug 1068270 - Add connectNode and disconnect methods to AudioNodeActor. r=vp
authorJordan Santell <jsantell@gmail.com>
Wed, 15 Oct 2014 08:10:00 +0200
changeset 210597 ceac5e4be9f2f827040f34f35b76de189c9aa3e8
parent 210596 683d12c284ff968611f7a1d7609330ae5970cb80
child 210598 20a0ffce7beefbe501ece8a1ecd0321e1b7eb6a2
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersvp
bugs1068270
milestone36.0a1
Bug 1068270 - Add connectNode and disconnect methods to AudioNodeActor. r=vp
browser/devtools/webaudioeditor/test/browser.ini
browser/devtools/webaudioeditor/test/browser_audionode-actor-connectnode-disconnect.js
toolkit/devtools/server/actors/webaudio.js
--- 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.