Throw error if connections are made between different contexts
The WebAudio spec says that if a node is connected/disconnected to
another node or AudioParam that belongs to a different context, then
an InvalidStateError must be thrown.[1][2] Add a check for this and throw
the required error.
This also requires a fix to audit.js which wasn't properly catching
DOM exceptions of the wrong type.
Finally, ctor-channelsplitter.html needed to be updated to specify the
error type because of the change in audit.js.
Also adds new WPT test, different-contexts.html, to more thoroughly test
connect/disconnects to different contexts.
[1] https://webaudio.github.io/web-audio-api/#dom-audionode-connect-destinationnode-output-input-destinationnode
[2] https://webaudio.github.io/web-audio-api/#dom-audionode-disconnect-destinationnode-destinationnode
Bug: 1206927
Change-Id: I320425268d1fd243e347dd36c79172c69dcc9733
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2965479
Reviewed-by: Hongchan Choi <hongchan@chromium.org>
Commit-Queue: Raymond Toy <rtoy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#895851}
diff --git a/webaudio/resources/audit.js b/webaudio/resources/audit.js
index 896b9d5..0acd340 100644
--- a/webaudio/resources/audit.js
+++ b/webaudio/resources/audit.js
@@ -320,11 +320,16 @@
didThrowCorrectly = true;
passDetail = '${actual} threw ' + error.name + errorMessage + '.';
} else if (this._expected === DOMException &&
- (this._expectedDescription === undefined ||
- this._expectedDescription === error.name)) {
- // Handles DOMException with the associated name.
- didThrowCorrectly = true;
- passDetail = '${actual} threw ${expected}' + errorMessage + '.';
+ this._expectedDescription !== undefined) {
+ // Handles DOMException with an expected exception name.
+ if (this._expectedDescription === error.name) {
+ didThrowCorrectly = true;
+ passDetail = '${actual} threw ${expected}' + errorMessage + '.';
+ } else {
+ didThrowCorrectly = false;
+ failDetail =
+ '${actual} threw "' + error.name + '" instead of ${expected}.';
+ }
} else if (this._expected == error.constructor) {
// Handler other error types.
didThrowCorrectly = true;
diff --git a/webaudio/the-audio-api/the-audiocontext-interface/audiocontextoptions.html b/webaudio/the-audio-api/the-audiocontext-interface/audiocontextoptions.html
index 0bf2cfb..136abed 100644
--- a/webaudio/the-audio-api/the-audiocontext-interface/audiocontextoptions.html
+++ b/webaudio/the-audio-api/the-audiocontext-interface/audiocontextoptions.html
@@ -170,7 +170,7 @@
context = new AudioContext({sampleRate: 1})
},
'context = new AudioContext({sampleRate: 1})')
- .throw(DOMException);
+ .throw(DOMException, 'NotSupportedError');
// A sampleRate of 1,000,000 is unlikely to be supported on any
// browser, test that this rate is also rejected.
@@ -179,21 +179,21 @@
context = new AudioContext({sampleRate: 1000000})
},
'context = new AudioContext({sampleRate: 1000000})')
- .throw(DOMException);
+ .throw(DOMException, 'NotSupportedError');
// A negative sample rate should not be accepted
should(
() => {
context = new AudioContext({sampleRate: -1})
},
'context = new AudioContext({sampleRate: -1})')
- .throw(DOMException);
+ .throw(DOMException, 'NotSupportedError');
// A null sample rate should not be accepted
should(
() => {
context = new AudioContext({sampleRate: 0})
},
'context = new AudioContext({sampleRate: 0})')
- .throw(DOMException);
+ .throw(DOMException, 'NotSupportedError');
should(
() => {
diff --git a/webaudio/the-audio-api/the-audionode-interface/different-contexts.html b/webaudio/the-audio-api/the-audionode-interface/different-contexts.html
new file mode 100644
index 0000000..f763d34
--- /dev/null
+++ b/webaudio/the-audio-api/the-audionode-interface/different-contexts.html
@@ -0,0 +1,101 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>
+ Connections and disconnections with different contexts
+ </title>
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script src="/webaudio/resources/audit-util.js"></script>
+ <script src="/webaudio/resources/audit.js"></script>
+ </head>
+ <body>
+ <script>
+ let audit = Audit.createTaskRunner();
+
+ // Different contexts to be used for testing.
+ let c1;
+ let c2;
+
+ audit.define(
+ {label: 'setup', description: 'Contexts for testing'},
+ (task, should) => {
+ should(() => {c1 = new AudioContext()}, 'c1 = new AudioContext()')
+ .notThrow();
+ should(() => {c2 = new AudioContext()}, 'c2 = new AudioContext()')
+ .notThrow();
+ task.done();
+ });
+
+ audit.define(
+ {label: 'Test 1', description: 'Connect nodes between contexts'},
+ (task, should) => {
+ let g1;
+ let g2;
+ should(
+ () => {g1 = new GainNode(c1)}, 'Test 1: g1 = new GainNode(c1)')
+ .notThrow();
+ should(
+ () => {g2 = new GainNode(c2)}, 'Test 1: g2 = new GainNode(c2)')
+ .notThrow();
+ should(() => {g2.connect(g1)}, 'Test 1: g2.connect(g1)')
+ .throw(DOMException, 'InvalidAccessError');
+ task.done();
+ });
+
+ audit.define(
+ {label: 'Test 2', description: 'Connect AudioParam between contexts'},
+ (task, should) => {
+ let g1;
+ let g2;
+ should(
+ () => {g1 = new GainNode(c1)}, 'Test 2: g1 = new GainNode(c1)')
+ .notThrow();
+ should(
+ () => {g2 = new GainNode(c2)}, 'Test 2: g2 = new GainNode(c2)')
+ .notThrow();
+ should(() => {g2.connect(g1.gain)}, 'Test 2: g2.connect(g1.gain)')
+ .throw(DOMException, 'InvalidAccessError');
+ task.done();
+ });
+
+ audit.define(
+ {label: 'Test 3', description: 'Disconnect nodes between contexts'},
+ (task, should) => {
+ let g1;
+ let g2;
+ should(
+ () => {g1 = new GainNode(c1)}, 'Test 3: g1 = new GainNode(c1)')
+ .notThrow();
+ should(
+ () => {g2 = new GainNode(c2)}, 'Test 3: g2 = new GainNode(c2)')
+ .notThrow();
+ should(() => {g2.disconnect(g1)}, 'Test 3: g2.disconnect(g1)')
+ .throw(DOMException, 'InvalidAccessError');
+ task.done();
+ });
+
+ audit.define(
+ {
+ label: 'Test 4',
+ description: 'Disconnect AudioParam between contexts'
+ },
+ (task, should) => {
+ let g1;
+ let g2;
+ should(
+ () => {g1 = new GainNode(c1)}, 'Test 4: g1 = new GainNode(c1)')
+ .notThrow();
+ should(
+ () => {g2 = new GainNode(c2)}, 'Test 4: g2 = new GainNode(c2)')
+ .notThrow();
+ should(
+ () => {g2.disconnect(g1.gain)}, 'Test 4: g2.connect(g1.gain)')
+ .throw(DOMException, 'InvalidAccessError');
+ task.done();
+ });
+
+ audit.run();
+ </script>
+ </body>
+</html>
diff --git a/webaudio/the-audio-api/the-channelsplitternode-interface/ctor-channelsplitter.html b/webaudio/the-audio-api/the-channelsplitternode-interface/ctor-channelsplitter.html
index 9cbb46b..b7165ba 100644
--- a/webaudio/the-audio-api/the-channelsplitternode-interface/ctor-channelsplitter.html
+++ b/webaudio/the-audio-api/the-channelsplitternode-interface/ctor-channelsplitter.html
@@ -49,6 +49,7 @@
channelCountMode: {
value: 'explicit',
isFixed: true,
+ exceptionType: 'InvalidStateError'
},
channelInterpretation: {
value: 'discrete',