WebRTC: Update RTCPeerConnection overloaded legacy operations to return a Promise
authoradam.bergkvist@ericsson.com <adam.bergkvist@ericsson.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 27 May 2016 18:35:49 +0000 (18:35 +0000)
committeradam.bergkvist@ericsson.com <adam.bergkvist@ericsson.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 27 May 2016 18:35:49 +0000 (18:35 +0000)
https://bugs.webkit.org/show_bug.cgi?id=158114

Reviewed by Eric Carlson.

Source/WebCore:

Update overloaded operations so that the legacy callback versions also return a promise
and never throw [1].

[1] https://w3c.github.io/webrtc-pc/archives/20160513/webrtc.html#legacy-interface-extensions

Updated existing tests.
- fast/mediastream/RTCPeerConnection-overloaded-operations-params.html
- fast/mediastream/RTCPeerConnection-overloaded-operations.html

* Modules/mediastream/RTCPeerConnection.idl:
Updated legacy signatures (just for documentation purposes)
* Modules/mediastream/RTCPeerConnection.js:
Implements the promise overload and the legacy callbacks overload (using the promise version)
as specified in [1] (above).
(createOffer):
(createAnswer):
(setLocalDescription):
(setRemoteDescription):
(addIceCandidate):
(getStats):
* Modules/mediastream/RTCPeerConnectionInternals.js:
Added helper functions objectAndCallbacksOverload and callbacksAndDictionaryOverload that
process an argument list and determine which overloaded version to use.
(callbacksAndDictionaryOverload):
(setLocalOrRemoteDescription): Deleted.
(extractCallbackArg): Deleted.

LayoutTests:

Updated existing tests (see below).

* fast/mediastream/RTCPeerConnection-overloaded-operations-expected.txt:
* fast/mediastream/RTCPeerConnection-overloaded-operations-params-expected.txt:
* fast/mediastream/RTCPeerConnection-overloaded-operations-params.html:
Test various combinations of good and bad arguments and verify that no errors are thrown.
* fast/mediastream/RTCPeerConnection-overloaded-operations.html:
Test that all overloaded versions return a promise.
* fast/mediastream/resources/promise-utils.js: Added.
Shared utils to make it easier to test async promise APIs.
(ensurePromise):
(promiseShouldReject):
(promiseShouldNotRejectWithTypeError.):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@201455 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/fast/mediastream/RTCPeerConnection-overloaded-operations-expected.txt
LayoutTests/fast/mediastream/RTCPeerConnection-overloaded-operations-params-expected.txt
LayoutTests/fast/mediastream/RTCPeerConnection-overloaded-operations-params.html
LayoutTests/fast/mediastream/RTCPeerConnection-overloaded-operations.html
LayoutTests/fast/mediastream/resources/promise-utils.js [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/mediastream/RTCPeerConnection.idl
Source/WebCore/Modules/mediastream/RTCPeerConnection.js
Source/WebCore/Modules/mediastream/RTCPeerConnectionInternals.js

index a8c3ad6..9d883af 100644 (file)
@@ -1,3 +1,24 @@
+2016-05-27  Adam Bergkvist  <adam.bergkvist@ericsson.com>
+
+        WebRTC: Update RTCPeerConnection overloaded legacy operations to return a Promise
+        https://bugs.webkit.org/show_bug.cgi?id=158114
+
+        Reviewed by Eric Carlson.
+
+        Updated existing tests (see below).
+
+        * fast/mediastream/RTCPeerConnection-overloaded-operations-expected.txt:
+        * fast/mediastream/RTCPeerConnection-overloaded-operations-params-expected.txt:
+        * fast/mediastream/RTCPeerConnection-overloaded-operations-params.html:
+        Test various combinations of good and bad arguments and verify that no errors are thrown.
+        * fast/mediastream/RTCPeerConnection-overloaded-operations.html:
+        Test that all overloaded versions return a promise.
+        * fast/mediastream/resources/promise-utils.js: Added.
+        Shared utils to make it easier to test async promise APIs.
+        (ensurePromise):
+        (promiseShouldReject):
+        (promiseShouldNotRejectWithTypeError.):
+
 2016-05-27  Antoine Quint  <graouts@apple.com>
 
         Video play glyph not visible if initially invisible when contained in a "-webkit-overflow-scrolling: touch" container
index 7000ad3..7cec339 100644 (file)
@@ -9,36 +9,36 @@ PASS result is an instance of Promise
 PASS result = pc.createOffer({}) did not throw exception.
 PASS result is an instance of Promise
 PASS result = pc.createOffer(emptyFunc, emptyFunc) did not throw exception.
-PASS result is undefined.
+PASS result is an instance of Promise
 PASS result = pc.createOffer(emptyFunc, emptyFunc, {}) did not throw exception.
-PASS result is undefined.
+PASS result is an instance of Promise
 PASS result = pc.createAnswer() did not throw exception.
 PASS result is an instance of Promise
 PASS result = pc.createAnswer({}) did not throw exception.
 PASS result is an instance of Promise
 PASS result = pc.createAnswer(emptyFunc, emptyFunc) did not throw exception.
-PASS result is undefined.
+PASS result is an instance of Promise
 PASS result = pc.createAnswer(emptyFunc, emptyFunc, {}) did not throw exception.
-PASS result is undefined.
+PASS result is an instance of Promise
 PASS desc = new RTCSessionDescription({ type: 'offer', sdp: 'x' }); did not throw exception.
 PASS result = pc.setLocalDescription(desc) did not throw exception.
 PASS result is an instance of Promise
 PASS result = pc.setLocalDescription(desc, emptyFunc, emptyFunc) did not throw exception.
-PASS result is undefined.
+PASS result is an instance of Promise
 PASS result = pc.setRemoteDescription(desc) did not throw exception.
 PASS result is an instance of Promise
 PASS result = pc.setRemoteDescription(desc, emptyFunc, emptyFunc) did not throw exception.
-PASS result is undefined.
+PASS result is an instance of Promise
 PASS result = pc.addIceCandidate(candidate) did not throw exception.
 PASS result is an instance of Promise
 PASS result = pc.addIceCandidate(candidate, emptyFunc, emptyFunc) did not throw exception.
-PASS result is undefined.
+PASS result is an instance of Promise
 PASS result = pc.getStats() did not throw exception.
 PASS result is an instance of Promise
 PASS result = pc.getStats(null) did not throw exception.
 PASS result is an instance of Promise
 PASS result = pc.getStats(null, emptyFunc, emptyFunc) did not throw exception.
-PASS result is undefined.
+PASS result is an instance of Promise
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 515e11e..bc745ba 100644 (file)
@@ -4,120 +4,142 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 PASS pc = new webkitRTCPeerConnection({iceServers:[{urls:'stun:foo.com'}]}); did not throw exception.
-PASS pc.createOffer() did not throw exception.
-PASS pc.createOffer(emptyFunc, emptyFunc) did not throw exception.
-PASS pc.createOffer(null) did not throw exception.
-PASS pc.createOffer(emptyFunc, emptyFunc, null) did not throw exception.
-PASS pc.createOffer(undefined) did not throw exception.
-PASS pc.createOffer(emptyFunc, emptyFunc, undefined) did not throw exception.
-PASS pc.createOffer({}) did not throw exception.
-PASS pc.createOffer(emptyFunc, emptyFunc, {}) did not throw exception.
-
+*** Test createOffer
+PASS promise pc.createOffer() did not reject with TypeError.
+PASS promise pc.createOffer(emptyFunc, emptyFunc) did not reject with TypeError.
+PASS promise pc.createOffer(null) did not reject with TypeError.
+PASS promise pc.createOffer(undefined) did not reject with TypeError.
+PASS promise pc.createOffer({}) did not reject with TypeError.
+PASS promise pc.createOffer(emptyFunc, emptyFunc, null) did not reject with TypeError.
+PASS promise pc.createOffer(emptyFunc, emptyFunc, undefined) did not reject with TypeError.
+PASS promise pc.createOffer(emptyFunc, emptyFunc, {}) did not reject with TypeError.
 *** Options object must be last
-PASS pc.createOffer({}, emptyFunc, emptyFunc) threw exception TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createOffer must be a Function.
+PASS promise pc.createOffer({}, emptyFunc, emptyFunc) rejected with TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createOffer must be a function
 *** Callbacks are not nullable
-PASS pc.createOffer(null, emptyFunc) threw exception TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createOffer must be a Function.
-PASS pc.createOffer(emptyFunc, null) threw exception TypeError: Argument 2 ('errorCallback') to RTCPeerConnection.createOffer must be a Function.
-PASS pc.createOffer(null, null) threw exception TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createOffer must be a Function.
-
-PASS pc.createAnswer() did not throw exception.
-PASS pc.createAnswer(emptyFunc, emptyFunc) did not throw exception.
-PASS pc.createAnswer(null) did not throw exception.
-PASS pc.createAnswer(emptyFunc, emptyFunc, null) did not throw exception.
-PASS pc.createAnswer(undefined) did not throw exception.
-PASS pc.createAnswer(emptyFunc, emptyFunc, undefined) did not throw exception.
-PASS pc.createAnswer({}) did not throw exception.
-PASS pc.createAnswer(emptyFunc, emptyFunc, {}) did not throw exception.
-
+PASS promise pc.createOffer(emptyFunc, null) rejected with TypeError: Argument 2 ('errorCallback') to RTCPeerConnection.createOffer must be a function
+PASS promise pc.createOffer(null, emptyFunc) rejected with TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createOffer must be a function
+PASS promise pc.createOffer(null, null) rejected with TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createOffer must be a function
+*** Bad input
+PASS promise pc.createOffer({}, emptyFunc) rejected with TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createOffer must be a function
+PASS promise pc.createOffer(emptyFunc, {}) rejected with TypeError: Argument 2 ('errorCallback') to RTCPeerConnection.createOffer must be a function
+PASS promise pc.createOffer(1) rejected with TypeError: Argument 1 ('options') to RTCPeerConnection.createOffer must be a Dictionary
+PASS promise pc.createOffer(emptyFunc, emptyFunc, 1) rejected with TypeError: Argument 3 ('options') to RTCPeerConnection.createOffer must be a Dictionary
+
+*** Test createAnswer
+PASS promise pc.createAnswer() did not reject with TypeError.
+PASS promise pc.createAnswer(emptyFunc, emptyFunc) did not reject with TypeError.
+PASS promise pc.createAnswer(null) did not reject with TypeError.
+PASS promise pc.createAnswer(undefined) did not reject with TypeError.
+PASS promise pc.createAnswer({}) did not reject with TypeError.
+PASS promise pc.createAnswer(emptyFunc, emptyFunc, null) did not reject with TypeError.
+PASS promise pc.createAnswer(emptyFunc, emptyFunc, undefined) did not reject with TypeError.
+PASS promise pc.createAnswer(emptyFunc, emptyFunc, {}) did not reject with TypeError.
 *** Options object must be last
-PASS pc.createAnswer({}, emptyFunc, emptyFunc) threw exception TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createAnswer must be a Function.
+PASS promise pc.createAnswer({}, emptyFunc, emptyFunc) rejected with TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createAnswer must be a function
 *** Callbacks are not nullable
-PASS pc.createAnswer(null, emptyFunc) threw exception TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createAnswer must be a Function.
-PASS pc.createAnswer(emptyFunc, null) threw exception TypeError: Argument 2 ('errorCallback') to RTCPeerConnection.createAnswer must be a Function.
-PASS pc.createAnswer(null, null) threw exception TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createAnswer must be a Function.
-
-PASS desc = new RTCSessionDescription({type:'offer', sdp:'x'}); did not throw exception.
-PASS pc.setLocalDescription(desc) did not throw exception.
-PASS pc.setLocalDescription(desc, emptyFunc, emptyFunc) did not throw exception.
-
+PASS promise pc.createAnswer(emptyFunc, null) rejected with TypeError: Argument 2 ('errorCallback') to RTCPeerConnection.createAnswer must be a function
+PASS promise pc.createAnswer(null, emptyFunc) rejected with TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createAnswer must be a function
+PASS promise pc.createAnswer(null, null) rejected with TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createAnswer must be a function
+*** Bad input
+PASS promise pc.createAnswer({}, emptyFunc) rejected with TypeError: Argument 1 ('successCallback') to RTCPeerConnection.createAnswer must be a function
+PASS promise pc.createAnswer(emptyFunc, {}) rejected with TypeError: Argument 2 ('errorCallback') to RTCPeerConnection.createAnswer must be a function
+PASS promise pc.createAnswer(1) rejected with TypeError: Argument 1 ('options') to RTCPeerConnection.createAnswer must be a Dictionary
+PASS promise pc.createAnswer(emptyFunc, emptyFunc, 1) rejected with TypeError: Argument 3 ('options') to RTCPeerConnection.createAnswer must be a Dictionary
+
+*** Test setLocalDescription
+PASS promise pc.setLocalDescription(desc) did not reject with TypeError.
+PASS promise pc.setLocalDescription(desc, emptyFunc, emptyFunc) did not reject with TypeError.
 *** desc is not optional
-PASS pc.setLocalDescription() threw exception TypeError: Not enough arguments.
+PASS promise pc.setLocalDescription() rejected with TypeError: Not enough arguments
 *** desc is not nullable
-PASS pc.setLocalDescription(null) threw exception TypeError: Argument 1 ('description') to RTCPeerConnection.setLocalDescription must be an instance of RTCSessionDescription.
-PASS pc.setLocalDescription(null, emptyFunc, emptyFunc) threw exception TypeError: Argument 1 ('description') to RTCPeerConnection.setLocalDescription must be an instance of RTCSessionDescription.
+PASS promise pc.setLocalDescription(null) rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setLocalDescription must be an instance of RTCSessionDescription
+PASS promise pc.setLocalDescription(null, emptyFunc, emptyFunc) rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setLocalDescription must be an instance of RTCSessionDescription
+PASS promise pc.setLocalDescription(1) rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setLocalDescription must be an instance of RTCSessionDescription
 *** Error callback is mandatory
-PASS pc.setLocalDescription(desc, emptyFunc) threw exception TypeError: Not enough arguments.
-PASS pc.setLocalDescription(desc, null) threw exception TypeError: Not enough arguments.
+PASS promise pc.setLocalDescription(desc, emptyFunc) rejected with TypeError: Not enough arguments
 *** Callbacks are not nullable
-PASS pc.setLocalDescription(desc, emptyFunc, null) threw exception TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.setLocalDescription must be a Function.
-PASS pc.setLocalDescription(desc, null, emptyFunc) threw exception TypeError: Argument 2 ('successCallback') to RTCPeerConnection.setLocalDescription must be a Function.
-PASS pc.setLocalDescription(desc, null, null) threw exception TypeError: Argument 2 ('successCallback') to RTCPeerConnection.setLocalDescription must be a Function.
+PASS promise pc.setLocalDescription(desc, emptyFunc, null) rejected with TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.setLocalDescription must be a function
+PASS promise pc.setLocalDescription(desc, null, emptyFunc) rejected with TypeError: Argument 2 ('successCallback') to RTCPeerConnection.setLocalDescription must be a function
+PASS promise pc.setLocalDescription(desc, null, null) rejected with TypeError: Argument 2 ('successCallback') to RTCPeerConnection.setLocalDescription must be a function
 *** Bad input as desc
-PASS pc.setLocalDescription('foo') threw exception TypeError: Argument 1 ('description') to RTCPeerConnection.setLocalDescription must be an instance of RTCSessionDescription.
-PASS pc.setLocalDescription('foo', emptyFunc, emptyFunc) threw exception TypeError: Argument 1 ('description') to RTCPeerConnection.setLocalDescription must be an instance of RTCSessionDescription.
-PASS pc.setLocalDescription(1) threw exception TypeError: Argument 1 ('description') to RTCPeerConnection.setLocalDescription must be an instance of RTCSessionDescription.
-PASS pc.setLocalDescription(1, emptyFunc, emptyFunc) threw exception TypeError: Argument 1 ('description') to RTCPeerConnection.setLocalDescription must be an instance of RTCSessionDescription.
-
-PASS pc.setRemoteDescription(desc) did not throw exception.
-PASS pc.setRemoteDescription(desc, emptyFunc, emptyFunc) did not throw exception.
-
+PASS promise pc.setLocalDescription('foo') rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setLocalDescription must be an instance of RTCSessionDescription
+PASS promise pc.setLocalDescription('foo', emptyFunc, emptyFunc) rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setLocalDescription must be an instance of RTCSessionDescription
+PASS promise pc.setLocalDescription(1) rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setLocalDescription must be an instance of RTCSessionDescription
+PASS promise pc.setLocalDescription(1, emptyFunc, emptyFunc) rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setLocalDescription must be an instance of RTCSessionDescription
+*** Bad input for callback arguments
+PASS promise pc.setLocalDescription(desc, {}, emptyFunc) rejected with TypeError: Argument 2 ('successCallback') to RTCPeerConnection.setLocalDescription must be a function
+PASS promise pc.setLocalDescription(desc, emptyFunc, {}) rejected with TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.setLocalDescription must be a function
+
+*** Test setRemoteDescription
+PASS promise pc.setRemoteDescription(desc) did not reject with TypeError.
+PASS promise pc.setRemoteDescription(desc, emptyFunc, emptyFunc) did not reject with TypeError.
 *** desc is not optional
-PASS pc.setRemoteDescription() threw exception TypeError: Not enough arguments.
+PASS promise pc.setRemoteDescription() rejected with TypeError: Not enough arguments
 *** desc is not nullable
-PASS pc.setRemoteDescription(null) threw exception TypeError: Argument 1 ('description') to RTCPeerConnection.setRemoteDescription must be an instance of RTCSessionDescription.
-PASS pc.setRemoteDescription(null, emptyFunc, emptyFunc) threw exception TypeError: Argument 1 ('description') to RTCPeerConnection.setRemoteDescription must be an instance of RTCSessionDescription.
+PASS promise pc.setRemoteDescription(null) rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setRemoteDescription must be an instance of RTCSessionDescription
+PASS promise pc.setRemoteDescription(null, emptyFunc, emptyFunc) rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setRemoteDescription must be an instance of RTCSessionDescription
+PASS promise pc.setRemoteDescription(1) rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setRemoteDescription must be an instance of RTCSessionDescription
 *** Error callback is mandatory
-PASS pc.setRemoteDescription(desc, emptyFunc) threw exception TypeError: Not enough arguments.
-PASS pc.setRemoteDescription(desc, null) threw exception TypeError: Not enough arguments.
+PASS promise pc.setRemoteDescription(desc, emptyFunc) rejected with TypeError: Not enough arguments
 *** Callbacks are not nullable
-PASS pc.setRemoteDescription(desc, emptyFunc, null) threw exception TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.setRemoteDescription must be a Function.
-PASS pc.setRemoteDescription(desc, null, emptyFunc) threw exception TypeError: Argument 2 ('successCallback') to RTCPeerConnection.setRemoteDescription must be a Function.
-PASS pc.setRemoteDescription(desc, null, null) threw exception TypeError: Argument 2 ('successCallback') to RTCPeerConnection.setRemoteDescription must be a Function.
+PASS promise pc.setRemoteDescription(desc, emptyFunc, null) rejected with TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.setRemoteDescription must be a function
+PASS promise pc.setRemoteDescription(desc, null, emptyFunc) rejected with TypeError: Argument 2 ('successCallback') to RTCPeerConnection.setRemoteDescription must be a function
+PASS promise pc.setRemoteDescription(desc, null, null) rejected with TypeError: Argument 2 ('successCallback') to RTCPeerConnection.setRemoteDescription must be a function
 *** Bad input as desc
-PASS pc.setRemoteDescription('foo') threw exception TypeError: Argument 1 ('description') to RTCPeerConnection.setRemoteDescription must be an instance of RTCSessionDescription.
-PASS pc.setRemoteDescription('foo', emptyFunc, emptyFunc) threw exception TypeError: Argument 1 ('description') to RTCPeerConnection.setRemoteDescription must be an instance of RTCSessionDescription.
-PASS pc.setRemoteDescription(1) threw exception TypeError: Argument 1 ('description') to RTCPeerConnection.setRemoteDescription must be an instance of RTCSessionDescription.
-PASS pc.setRemoteDescription(1, emptyFunc, emptyFunc) threw exception TypeError: Argument 1 ('description') to RTCPeerConnection.setRemoteDescription must be an instance of RTCSessionDescription.
-
-PASS pc.addIceCandidate(candidate) did not throw exception.
-PASS pc.addIceCandidate(candidate, emptyFunc, emptyFunc) did not throw exception.
-
+PASS promise pc.setRemoteDescription('foo') rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setRemoteDescription must be an instance of RTCSessionDescription
+PASS promise pc.setRemoteDescription('foo', emptyFunc, emptyFunc) rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setRemoteDescription must be an instance of RTCSessionDescription
+PASS promise pc.setRemoteDescription(1) rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setRemoteDescription must be an instance of RTCSessionDescription
+PASS promise pc.setRemoteDescription(1, emptyFunc, emptyFunc) rejected with TypeError: Argument 1 ('description') to RTCPeerConnection.setRemoteDescription must be an instance of RTCSessionDescription
+*** Bad input for callback arguments
+PASS promise pc.setRemoteDescription(desc, {}, emptyFunc) rejected with TypeError: Argument 2 ('successCallback') to RTCPeerConnection.setRemoteDescription must be a function
+PASS promise pc.setRemoteDescription(desc, emptyFunc, {}) rejected with TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.setRemoteDescription must be a function
+
+*** Test addIceCandidate
+PASS promise pc.addIceCandidate(candidate) did not reject with TypeError.
+PASS promise pc.addIceCandidate(candidate, emptyFunc, emptyFunc) did not reject with TypeError.
 *** candidate is not optional
-PASS pc.addIceCandidate() threw exception TypeError: Not enough arguments.
+PASS promise pc.addIceCandidate() rejected with TypeError: Not enough arguments
 *** candidate is not nullable
-PASS pc.addIceCandidate(null) threw exception TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate.
-PASS pc.addIceCandidate(null, emptyFunc, emptyFunc) threw exception TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate.
+PASS promise pc.addIceCandidate(null) rejected with TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate
+PASS promise pc.addIceCandidate(null, emptyFunc, emptyFunc) rejected with TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate
+PASS promise pc.addIceCandidate(1) rejected with TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate
 *** Error callback is mandatory
-PASS pc.addIceCandidate(candidate, emptyFunc) threw exception TypeError: Not enough arguments.
-PASS pc.addIceCandidate(candidate, null) threw exception TypeError: Not enough arguments.
+PASS promise pc.addIceCandidate(candidate, emptyFunc) rejected with TypeError: Not enough arguments
 *** Callbacks are not nullable
-PASS pc.addIceCandidate(candidate, emptyFunc, null) threw exception TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.addIceCandidate must be a Function.
-PASS pc.addIceCandidate(candidate, null, emptyFunc) threw exception TypeError: Argument 2 ('successCallback') to RTCPeerConnection.addIceCandidate must be a Function.
-PASS pc.addIceCandidate(candidate, null, null) threw exception TypeError: Argument 2 ('successCallback') to RTCPeerConnection.addIceCandidate must be a Function.
+PASS promise pc.addIceCandidate(candidate, emptyFunc, null) rejected with TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.addIceCandidate must be a function
+PASS promise pc.addIceCandidate(candidate, null, emptyFunc) rejected with TypeError: Argument 2 ('successCallback') to RTCPeerConnection.addIceCandidate must be a function
+PASS promise pc.addIceCandidate(candidate, null, null) rejected with TypeError: Argument 2 ('successCallback') to RTCPeerConnection.addIceCandidate must be a function
 *** Bad input as candidate
-PASS pc.addIceCandidate('foo') threw exception TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate.
-PASS pc.addIceCandidate('foo', emptyFunc, emptyFunc) threw exception TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate.
-PASS pc.addIceCandidate(1) threw exception TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate.
-PASS pc.addIceCandidate(1, emptyFunc, emptyFunc) threw exception TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate.
-
-PASS pc.getStats() did not throw exception.
-PASS pc.getStats(null) did not throw exception.
-PASS pc.getStats(track) did not throw exception.
-PASS pc.getStats(null, emptyFunc, emptyFunc) did not throw exception.
-PASS pc.getStats(track, emptyFunc, emptyFunc) did not throw exception.
-
+PASS promise pc.addIceCandidate('foo') rejected with TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate
+PASS promise pc.addIceCandidate('foo', emptyFunc, emptyFunc) rejected with TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate
+PASS promise pc.addIceCandidate(1) rejected with TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate
+PASS promise pc.addIceCandidate(1, emptyFunc, emptyFunc) rejected with TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate
+*** Bad input for callback arguments
+PASS promise pc.addIceCandidate(candidate, {}, emptyFunc) rejected with TypeError: Argument 2 ('successCallback') to RTCPeerConnection.addIceCandidate must be a function
+PASS promise pc.addIceCandidate(candidate, emptyFunc, {}) rejected with TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.addIceCandidate must be a function
+
+*** Test getStats
+PASS promise pc.getStats() did not reject with TypeError.
+PASS promise pc.getStats(null) did not reject with TypeError.
+PASS promise pc.getStats(selector) did not reject with TypeError.
+PASS promise pc.getStats(null, emptyFunc, emptyFunc) did not reject with TypeError.
+PASS promise pc.getStats(selector, emptyFunc, emptyFunc) did not reject with TypeError.
+PASS promise pc.getStats(1) rejected with TypeError: Argument 1 ('selector') to RTCPeerConnection.getStats must be an instance of MediaStreamTrack
 *** Error callback is mandatory
-PASS pc.getStats(track, emptyFunc) threw exception TypeError: Not enough arguments.
-PASS pc.getStats(track, null) threw exception TypeError: Not enough arguments.
+PASS promise pc.getStats(selector, emptyFunc) rejected with TypeError: Not enough arguments
 *** Callbacks are not nullable
-PASS pc.getStats(track, emptyFunc, null) threw exception TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.getStats must be a Function.
-PASS pc.getStats(track, null, emptyFunc) threw exception TypeError: Argument 2 ('successCallback') to RTCPeerConnection.getStats must be a Function.
-PASS pc.getStats(track, null, null) threw exception TypeError: Argument 2 ('successCallback') to RTCPeerConnection.getStats must be a Function.
-*** Bad input as track
-PASS pc.getStats('foo') threw exception TypeError: Argument 1 ('selector') to RTCPeerConnection.getStats must be an instance of MediaStreamTrack.
-PASS pc.getStats('foo', emptyFunc, emptyFunc) threw exception TypeError: Argument 1 ('selector') to RTCPeerConnection.getStats must be an instance of MediaStreamTrack.
-PASS pc.getStats(1) threw exception TypeError: Argument 1 ('selector') to RTCPeerConnection.getStats must be an instance of MediaStreamTrack.
-PASS pc.getStats(1, emptyFunc, emptyFunc) threw exception TypeError: Argument 1 ('selector') to RTCPeerConnection.getStats must be an instance of MediaStreamTrack.
+PASS promise pc.getStats(selector, emptyFunc, null) rejected with TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.getStats must be a function
+PASS promise pc.getStats(selector, null, emptyFunc) rejected with TypeError: Argument 2 ('successCallback') to RTCPeerConnection.getStats must be a function
+PASS promise pc.getStats(selector, null, null) rejected with TypeError: Argument 2 ('successCallback') to RTCPeerConnection.getStats must be a function
+*** Bad input as selector
+PASS promise pc.getStats('foo') rejected with TypeError: Argument 1 ('selector') to RTCPeerConnection.getStats must be an instance of MediaStreamTrack
+PASS promise pc.getStats('foo', emptyFunc, emptyFunc) rejected with TypeError: Argument 1 ('selector') to RTCPeerConnection.getStats must be an instance of MediaStreamTrack
+PASS promise pc.getStats(1) rejected with TypeError: Argument 1 ('selector') to RTCPeerConnection.getStats must be an instance of MediaStreamTrack
+PASS promise pc.getStats(1, emptyFunc, emptyFunc) rejected with TypeError: Argument 1 ('selector') to RTCPeerConnection.getStats must be an instance of MediaStreamTrack
+*** Bad input for callback arguments
+PASS promise pc.getStats(selector, {}, emptyFunc) rejected with TypeError: Argument 2 ('successCallback') to RTCPeerConnection.getStats must be a function
+PASS promise pc.getStats(selector, emptyFunc, {}) rejected with TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.getStats must be a function
+
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 826a93e..55e426c 100644 (file)
 <html>
     <head>
         <script src="../../resources/js-test-pre.js"></script>
-        <script src="./resources/getUserMedia-helper.js"></script>
+        <script src="./resources/promise-utils.js"></script>
     </head>
     <body>
         <script>
             var pc;
-            var result;
             var desc;
             var candidate;
-            var track;
+            var selector;
+            var reason;
+            var detailedErrors;
 
             description("Test various arguments to RTCPeerConnection overloaded functions");
 
             shouldNotThrow("pc = new webkitRTCPeerConnection({iceServers:[{urls:'stun:foo.com'}]});");
 
-            // Test createOffer/Answer()
-            testCreateOfferOrAnswer("createOffer");
-            testCreateOfferOrAnswer("createAnswer");
-
             function testCreateOfferOrAnswer(functionName) {
+                return new Promise(function (resolve) {
+                    debug(`*** Test ${functionName}`);
 
-                shouldNotThrow("pc." + functionName + "()");
-                shouldNotThrow("pc." + functionName + "(emptyFunc, emptyFunc)");
-
-                ["null", "undefined", "{}"].forEach(function (arg) {
-                    shouldNotThrow("pc." + functionName + "(" + arg + ")");
-                    shouldNotThrow("pc." + functionName + "(emptyFunc, emptyFunc, " + arg + ")");
+                    promiseShouldNotRejectWithTypeError(`pc.${functionName}()`)
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError(`pc.${functionName}(emptyFunc, emptyFunc)`);
+                    })
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError(`pc.${functionName}(null)`);
+                    })
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError(`pc.${functionName}(undefined)`);
+                    })
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError(`pc.${functionName}({})`);
+                    })
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError(`pc.${functionName}(emptyFunc, emptyFunc, null)`);
+                    })
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError(`pc.${functionName}(emptyFunc, emptyFunc, undefined)`);
+                    })
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError(`pc.${functionName}(emptyFunc, emptyFunc, {})`);
+                    })
+                    .then(function () {
+                        debug("*** Options object must be last")
+                        reason = `TypeError: Argument 1 ('successCallback') to RTCPeerConnection.${functionName} must be a function`;
+                        return promiseShouldReject(`pc.${functionName}({}, emptyFunc, emptyFunc)`, "reason");
+                    })
+                    .then(function () {
+                        debug("*** Callbacks are not nullable");
+                        reason = `TypeError: Argument 2 ('errorCallback') to RTCPeerConnection.${functionName} must be a function`;
+                        return promiseShouldReject(`pc.${functionName}(emptyFunc, null)`, "reason");
+                    })
+                    .then(function () {
+                        reason = `TypeError: Argument 1 ('successCallback') to RTCPeerConnection.${functionName} must be a function`;
+                        return promiseShouldReject(`pc.${functionName}(null, emptyFunc)`, "reason");
+                    })
+                    .then(function () {
+                        reason = `TypeError: Argument 1 ('successCallback') to RTCPeerConnection.${functionName} must be a function`;
+                        return promiseShouldReject(`pc.${functionName}(null, null)`, "reason");
+                    })
+                    .then(function () {
+                        debug("*** Bad input");
+                        reason = `TypeError: Argument 1 ('successCallback') to RTCPeerConnection.${functionName} must be a function`;
+                        return promiseShouldReject(`pc.${functionName}({}, emptyFunc)`, "reason");
+                    })
+                    .then(function () {
+                        reason = `TypeError: Argument 2 ('errorCallback') to RTCPeerConnection.${functionName} must be a function`;
+                        return promiseShouldReject(`pc.${functionName}(emptyFunc, {})`, "reason");
+                    })
+                    .then(function () {
+                        reason = `TypeError: Argument 1 ('options') to RTCPeerConnection.${functionName} must be a Dictionary`;
+                        return promiseShouldReject(`pc.${functionName}(1)`, "reason");
+                    })
+                    .then(function () {
+                        reason = `TypeError: Argument 3 ('options') to RTCPeerConnection.${functionName} must be a Dictionary`;
+                        return promiseShouldReject(`pc.${functionName}(emptyFunc, emptyFunc, 1)`, "reason");
+                    })
+                    .then(function () {
+                        debug("");
+                        resolve();
+                    })
+                    .catch(function (error) {
+                        testFailed(`Unexpected error in ${functionName} promise chain: ${error}`);
+                        resolve();
+                    });
                 });
-                debug("");
+            }
+
+            function testSetLocalOrRemoteDescription(functionName) {
+                return new Promise(function (resolve) {
+                    debug(`*** Test ${functionName}`);
 
+                    desc = new RTCSessionDescription({type:'offer', sdp:'x'});
 
-                debug("*** Options object must be last")
-                shouldThrow("pc." + functionName + "({}, emptyFunc, emptyFunc)");
-                debug("*** Callbacks are not nullable");
-                shouldThrow("pc." + functionName + "(null, emptyFunc)");
-                shouldThrow("pc." + functionName + "(emptyFunc, null)");
-                shouldThrow("pc." + functionName + "(null, null)");
-                debug("");
+                    promiseShouldNotRejectWithTypeError(`pc.${functionName}(desc)`)
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError(`pc.${functionName}(desc, emptyFunc, emptyFunc)`);
+                    })
+                    .then(function () {
+                        return badInputFull(functionName, "desc", {
+                            "arg1": `TypeError: Argument 1 ('description') to RTCPeerConnection.${functionName} must be an instance of RTCSessionDescription`,
+                            "arg2": `TypeError: Argument 2 ('successCallback') to RTCPeerConnection.${functionName} must be a function`,
+                            "arg3": `TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.${functionName} must be a function`
+                        });
+                    })
+                    .then(function () {
+                        debug("");
+                        resolve();
+                    })
+                    .catch(function (error) {
+                        testFailed(`Unexpected error in ${functionName} promise chain: ${error}`);
+                        resolve();
+                    });
+                });
             }
 
-            // Test setLocal/RemoteDescription()
-            shouldNotThrow("desc = new RTCSessionDescription({type:'offer', sdp:'x'});");
+            // badInputFull eventually runs badInput
+            function badInputFull(functionName, firstArg, detailedErrorsArg) {
+                detailedErrors = detailedErrorsArg;
 
-            testSetLocalOrRemoteDescription("setLocalDescription")
-            testSetLocalOrRemoteDescription("setRemoteDescription")
+                return new Promise(function (resolve) {
+                    debug(`*** ${firstArg} is not optional`);
+                    promiseShouldReject(`pc.${functionName}()`, "'TypeError: Not enough arguments'")
+                    .then(function () {
+                        debug(`*** ${firstArg} is not nullable`);
+                        return promiseShouldReject(`pc.${functionName}(null)`, "detailedErrors.arg1");
+                    })
+                    .then(function () {
+                        return promiseShouldReject(`pc.${functionName}(null, emptyFunc, emptyFunc)`, "detailedErrors.arg1");
+                    })
+                    .then(function () {
+                        return badInput(functionName, firstArg, detailedErrorsArg);
+                    })
+                    .then(function () {
+                        resolve();
+                    })
+                    .catch(function (error) {
+                        testFailed(`Unexpected error in promise chain: ${error}`);
+                        resolve();
+                    });
+                });
+            }
 
-            function testSetLocalOrRemoteDescription(functionName) {
-                shouldNotThrow("pc." + functionName + "(desc)");
-                shouldNotThrow("pc." + functionName + "(desc, emptyFunc, emptyFunc)");
-                debug("");
+            function badInput(functionName, firstArg, detailedErrorsArg) {
+                detailedErrors = detailedErrorsArg;
 
-                badInputFull(functionName, "desc");
-                debug("");
+                return new Promise(function (resolve) {
+                    promiseShouldReject(`pc.${functionName}(1)`, "detailedErrors.arg1")
+                    .then(function () {
+                        debug("*** Error callback is mandatory")
+                        return promiseShouldReject(`pc.${functionName}(${firstArg}, emptyFunc)`, "'TypeError: Not enough arguments'");
+                    })
+                    .then(function () {
+                        debug("*** Callbacks are not nullable");
+                        return promiseShouldReject(`pc.${functionName}(${firstArg}, emptyFunc, null)`, "detailedErrors.arg3");
+                    })
+                    .then(function () {
+                        return promiseShouldReject(`pc.${functionName}(${firstArg}, null, emptyFunc)`, "detailedErrors.arg2");
+                    })
+                    .then(function () {
+                        return promiseShouldReject(`pc.${functionName}(${firstArg}, null, null)`, "detailedErrors.arg2");
+                    })
+                    .then(function () {
+                        debug(`*** Bad input as ${firstArg}`);
+                        return promiseShouldReject(`pc.${functionName}('foo')`, "detailedErrors.arg1");
+                    })
+                    .then(function () {
+                        return promiseShouldReject(`pc.${functionName}('foo', emptyFunc, emptyFunc)`, "detailedErrors.arg1");
+                    })
+                    .then(function () {
+                        return promiseShouldReject(`pc.${functionName}(1)`, "detailedErrors.arg1");
+                    })
+                    .then(function () {
+                        return promiseShouldReject(`pc.${functionName}(1, emptyFunc, emptyFunc)`, "detailedErrors.arg1");
+                    })
+                    .then(function () {
+                        debug("*** Bad input for callback arguments");
+                        return promiseShouldReject(`pc.${functionName}(${firstArg}, {}, emptyFunc)`, "detailedErrors.arg2");
+                    })
+                    .then(function () {
+                        return promiseShouldReject(`pc.${functionName}(${firstArg}, emptyFunc, {})`, "detailedErrors.arg3");
+                    })
+                    .then(function () {
+                        resolve();
+                    })
+                    .catch(function (error) {
+                        testFailed(`Unexpected error in promise chain: ${error}`);
+                        resolve();
+                    });
+                });
             }
 
-            function badInputFull(functionName, firstArg) {
-                debug("*** " + firstArg + " is not optional");
-                shouldThrow("pc." + functionName + "()");
-                debug("*** " + firstArg + " is not nullable");
-                shouldThrow("pc." + functionName + "(null)");
-                shouldThrow("pc." + functionName + "(null, emptyFunc, emptyFunc)");
-                badInput(functionName, firstArg);
-            }
+            function testAddIceCandidate() {
+                return new Promise(function (resolve) {
+                    debug("*** Test addIceCandidate");
+
+                    candidate = new RTCIceCandidate({ "candidate": "foo", "sdpMid": "bar" });
 
-            function badInput(functionName, firstArg) {
-                debug("*** Error callback is mandatory")
-                shouldThrow("pc." + functionName + "(" + firstArg + ", emptyFunc)");
-                shouldThrow("pc." + functionName + "(" + firstArg + ", null)");
-                debug("*** Callbacks are not nullable");
-                shouldThrow("pc." + functionName + "(" + firstArg + ", emptyFunc, null)");
-                shouldThrow("pc." + functionName + "(" + firstArg + ", null, emptyFunc)");
-                shouldThrow("pc." + functionName + "(" + firstArg + ", null, null)");
-                debug("*** Bad input as " + firstArg);
-                shouldThrow("pc." + functionName + "('foo')");
-                shouldThrow("pc." + functionName + "('foo', emptyFunc, emptyFunc)");
-                shouldThrow("pc." + functionName + "(1)");
-                shouldThrow("pc." + functionName + "(1, emptyFunc, emptyFunc)");
+                    promiseShouldNotRejectWithTypeError("pc.addIceCandidate(candidate)")
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError("pc.addIceCandidate(candidate, emptyFunc, emptyFunc)");
+                    })
+                    .then(function () {
+                        return badInputFull("addIceCandidate", "candidate", {
+                            "arg1": "TypeError: Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate",
+                            "arg2": "TypeError: Argument 2 ('successCallback') to RTCPeerConnection.addIceCandidate must be a function",
+                            "arg3": "TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.addIceCandidate must be a function"
+                        });
+                    })
+                    .then(function () {
+                        debug("");
+                        resolve();
+                    })
+                    .catch(function (error) {
+                        testFailed(`Unexpected error in testAddIceCandidate promise chain: ${error}`);
+                        resolve();
+                    });
+                });
             }
 
-            // Test addIceCandidate()
-            candidate = new RTCIceCandidate({ "candidate": "foo", "sdpMid": "bar" });
+            function testGetStats() {
+                return new Promise(function (resolve) {
+                    debug("*** Test getStats");
 
-            shouldNotThrow("pc.addIceCandidate(candidate)");
-            shouldNotThrow("pc.addIceCandidate(candidate, emptyFunc, emptyFunc)");
-            debug("");
+                    navigator.mediaDevices.getUserMedia({ "video": true })
+                    .then(function (stream) {
+                        selector = stream.getTracks()[0];
 
-            badInputFull("addIceCandidate", "candidate");
-            debug("");
+                        return Promise.resolve();
+                    })
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError("pc.getStats()");
+                    })
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError("pc.getStats(null)");
+                    })
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError("pc.getStats(selector)");
+                    })
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError("pc.getStats(null, emptyFunc, emptyFunc)");
+                    })
+                    .then(function () {
+                        return promiseShouldNotRejectWithTypeError("pc.getStats(selector, emptyFunc, emptyFunc)");
+                    })
+                    .then(function () {
+                        return badInput("getStats", "selector", {
+                            "arg1": "TypeError: Argument 1 ('selector') to RTCPeerConnection.getStats must be an instance of MediaStreamTrack",
+                            "arg2": "TypeError: Argument 2 ('successCallback') to RTCPeerConnection.getStats must be a function",
+                            "arg3": "TypeError: Argument 3 ('errorCallback') to RTCPeerConnection.getStats must be a function"
+                        });
+                    })
+                    .then(function () {
+                        debug("");
+                        resolve();
+                    })
+                    .catch(function (error) {
+                        testFailed(`Unexpected error in testGetStats promise chain: ${error}`);
+                        resolve();
+                    });
+                })
+            }
 
-            // Test getStats()
-            getUserMedia("allow", { "video": true }, function (stream) {
-                track = stream.getTracks()[0];
+            function emptyFunc() { }
 
-                shouldNotThrow("pc.getStats()");
-                shouldNotThrow("pc.getStats(null)");
-                shouldNotThrow("pc.getStats(track)");
-                shouldNotThrow("pc.getStats(null, emptyFunc, emptyFunc)");
-                shouldNotThrow("pc.getStats(track, emptyFunc, emptyFunc)");
-                debug("");
+            if (window.testRunner)
+                testRunner.setUserMediaPermission(true);
+            else {
+                debug("This test can not be run without the testRunner");
+                finishJSTest();
+            }
 
-                badInput("getStats", "track");
+            testCreateOfferOrAnswer("createOffer")
+            .then(function () {
+                return testCreateOfferOrAnswer("createAnswer");
+            })
+            .then(function () {
+                return testSetLocalOrRemoteDescription("setLocalDescription");
+            })
+            .then(function () {
+                return testSetLocalOrRemoteDescription("setRemoteDescription");
+            })
+            .then(function () {
+                return testAddIceCandidate();
+            })
+            .then(function () {
+                return testGetStats();
+            })
+            .then(function () {
+                finishJSTest();
+            })
+            .catch(function () {
                 finishJSTest();
             });
 
-            function emptyFunc() { }
-
             window.jsTestIsAsync = true;
             window.successfullyParsed = true;
 
index 9aa4e4e..4890357 100644 (file)
                 shouldBeType("result", "Promise");
 
                 shouldNotThrow("result = pc." + functionName + "(emptyFunc, emptyFunc)");
-                shouldBeUndefined("result");
+                shouldBeType("result", "Promise");
 
                 shouldNotThrow("result = pc." + functionName + "(emptyFunc, emptyFunc, {})");
-                shouldBeUndefined("result");
+                shouldBeType("result", "Promise");
             }
 
             // Test setLocal/RemoteDescription()
@@ -43,7 +43,7 @@
                 shouldBeType("result", "Promise");
 
                 shouldNotThrow("result = pc." + functionName + "(desc, emptyFunc, emptyFunc)");
-                shouldBeUndefined("result");
+                shouldBeType("result", "Promise");
             }
 
             // Test addIceCandidate()
@@ -53,7 +53,7 @@
             shouldBeType("result", "Promise");
 
             shouldNotThrow("result = pc.addIceCandidate(candidate, emptyFunc, emptyFunc)");
-            shouldBeUndefined("result");
+            shouldBeType("result", "Promise");
 
             // Test getStats()
             shouldNotThrow("result = pc.getStats()");
@@ -63,7 +63,7 @@
             shouldBeType("result", "Promise");
 
             shouldNotThrow("result = pc.getStats(null, emptyFunc, emptyFunc)");
-            shouldBeUndefined("result");
+            shouldBeType("result", "Promise");
 
             function emptyFunc() { }
 
diff --git a/LayoutTests/fast/mediastream/resources/promise-utils.js b/LayoutTests/fast/mediastream/resources/promise-utils.js
new file mode 100644 (file)
index 0000000..20e078d
--- /dev/null
@@ -0,0 +1,72 @@
+function ensurePromise(expr) {
+    var p;
+    try {
+        p = eval(expr);
+    } catch (e) {
+        testFailed("evaluating " + expr + " threw exception " + e);
+        return null;
+    }
+
+    if (!(p instanceof Promise)) {
+        testFailed(expr + " does not evaluate to a promise.");
+        return null;
+    }
+
+    return p;
+}
+
+function promiseShouldReject(expr, reasonArg) {
+    return new Promise(function (done) {
+        var p = ensurePromise(expr);
+        if (!p) {
+            done();
+            return;
+        }
+
+        p.then(function () {
+            testFailed("promise " + expr + " fulfilled unexpectedly.");
+            done();
+        })
+        .catch(function (actualReason) {
+            if (!reasonArg) {
+                testPassed("promise " + expr + " rejected with " + actualReason);
+            } else {
+                var reasonValue;
+                try {
+                    reasonValue = eval(reasonArg);
+                } catch (ex) {
+                    debug("promiseShouldReject: Error evaluating reason: " + ex);
+                }
+
+                if (actualReason == reasonValue)
+                    testPassed("promise " + expr + " rejected with " + actualReason);
+                else
+                    testFailed("promise " + expr + " rejected with " + actualReason + "; expected reason " + reasonValue);
+            }
+
+            done();
+        });
+    });
+}
+
+function promiseShouldNotRejectWithTypeError(expr) {
+    return new Promise(function (done) {
+        var p = ensurePromise(expr);
+        if (!p) {
+            done();
+            return;
+        }
+
+        p.then(function () {
+            testPassed("promise " + expr + " did not reject with TypeError.");
+            done();
+        })
+        .catch(function (reason) {
+            if (reason instanceof TypeError)
+                testFailed("promise " + expr + " rejected with TypeError");
+            else
+                testPassed("promise " + expr + " did not reject with TypeError.");
+            done();
+        });
+    });
+}
index 81cc95c..c45dddd 100644 (file)
@@ -1,3 +1,37 @@
+2016-05-27  Adam Bergkvist  <adam.bergkvist@ericsson.com>
+
+        WebRTC: Update RTCPeerConnection overloaded legacy operations to return a Promise
+        https://bugs.webkit.org/show_bug.cgi?id=158114
+
+        Reviewed by Eric Carlson.
+
+        Update overloaded operations so that the legacy callback versions also return a promise
+        and never throw [1].
+
+        [1] https://w3c.github.io/webrtc-pc/archives/20160513/webrtc.html#legacy-interface-extensions
+
+        Updated existing tests.
+        - fast/mediastream/RTCPeerConnection-overloaded-operations-params.html
+        - fast/mediastream/RTCPeerConnection-overloaded-operations.html
+
+        * Modules/mediastream/RTCPeerConnection.idl:
+        Updated legacy signatures (just for documentation purposes)
+        * Modules/mediastream/RTCPeerConnection.js:
+        Implements the promise overload and the legacy callbacks overload (using the promise version)
+        as specified in [1] (above).
+        (createOffer):
+        (createAnswer):
+        (setLocalDescription):
+        (setRemoteDescription):
+        (addIceCandidate):
+        (getStats):
+        * Modules/mediastream/RTCPeerConnectionInternals.js:
+        Added helper functions objectAndCallbacksOverload and callbacksAndDictionaryOverload that
+        process an argument list and determine which overloaded version to use.
+        (callbacksAndDictionaryOverload):
+        (setLocalOrRemoteDescription): Deleted.
+        (extractCallbackArg): Deleted.
+
 2016-05-27  Antoine Quint  <graouts@apple.com>
 
         Video play glyph not visible if initially invisible when contained in a "-webkit-overflow-scrolling: touch" container
index 8b6fb30..b0adea3 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Copyright (C) 2012 Google Inc. All rights reserved.
  * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
- * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
     [Private] Promise queuedSetRemoteDescription(RTCSessionDescription description);
     [Private] Promise queuedAddIceCandidate(RTCIceCandidate candidate);
 
-    [Private] Promise privateGetStats(optional MediaStreamTrack? selector = null);
+    [Private] Promise privateGetStats(MediaStreamTrack? selector);
 
     [JSBuiltin] Promise createOffer(optional Dictionary offerOptions);
-    // Legacy signature: void createOffer(RTCSessionDescriptionCallback successCallback
-    //                                    RTCPeerConnectionErrorCallback errorCallback,
-    //                                    optional Dictionary offerOptions);
+    // Legacy signature: Promise createOffer(RTCSessionDescriptionCallback successCallback
+    //                                       RTCPeerConnectionErrorCallback errorCallback,
+    //                                       optional Dictionary offerOptions);
 
     [JSBuiltin] Promise createAnswer(optional Dictionary answerOptions);
-    // Legacy signature: void createAnswer(RTCSessionDescriptionCallback successCallback
-    //                                    RTCPeerConnectionErrorCallback errorCallback,
-    //                                    optional Dictionary answerOptions);
+    // Legacy signature: Promise createAnswer(RTCSessionDescriptionCallback successCallback
+    //                                        RTCPeerConnectionErrorCallback errorCallback,
+    //                                        optional Dictionary answerOptions);
 
     sequence<RTCRtpSender> getSenders();
     sequence<RTCRtpReceiver> getReceivers();
     [StrictTypeChecking, RaisesException] void removeTrack(RTCRtpSender sender);
 
     [JSBuiltin] Promise setLocalDescription(RTCSessionDescription description);
-    // Legacy signature: void setLocalDescription(RTCSessionDescription description
-    //                                            VoidCallback successCallback,
-    //                                            RTCPeerConnectionErrorCallback errorCallback);
+    // Legacy signature: Promise setLocalDescription(RTCSessionDescription description
+    //                                               VoidCallback successCallback,
+    //                                               RTCPeerConnectionErrorCallback errorCallback);
 
     readonly attribute RTCSessionDescription localDescription;
     readonly attribute RTCSessionDescription currentLocalDescription;
     readonly attribute RTCSessionDescription pendingLocalDescription;
 
     [JSBuiltin] Promise setRemoteDescription(RTCSessionDescription description);
-    // Legacy signature: void setRemoteDescription(RTCSessionDescription description
-    //                                            VoidCallback successCallback,
-    //                                            RTCPeerConnectionErrorCallback errorCallback);
+    // Legacy signature: Promise setRemoteDescription(RTCSessionDescription description
+    //                                                VoidCallback successCallback,
+    //                                                RTCPeerConnectionErrorCallback errorCallback);
 
     readonly attribute RTCSessionDescription remoteDescription;
     readonly attribute RTCSessionDescription currentRemoteDescription;
@@ -85,9 +85,9 @@
     readonly attribute DOMString signalingState;
 
     [JSBuiltin] Promise addIceCandidate(RTCIceCandidate candidate);
-    // Legacy signature: void addIceCandidate(RTCIceCandidate candidate
-    //                                        VoidCallback successCallback,
-    //                                        RTCPeerConnectionErrorCallback errorCallback);
+    // Legacy signature: Promise addIceCandidate(RTCIceCandidate candidate
+    //                                           VoidCallback successCallback,
+    //                                           RTCPeerConnectionErrorCallback errorCallback);
 
     readonly attribute DOMString iceGatheringState;
     readonly attribute DOMString iceConnectionState;
     RTCConfiguration getConfiguration();
     [RaisesException] void setConfiguration(Dictionary configuration);
 
-    [JSBuiltin] Promise getStats(optional MediaStreamTrack selector);
-    // Legacy signature: void getStats(MediaStreamTrack selector
-    //                                 RTCStatsCallback successCallback,
-    //                                 RTCPeerConnectionErrorCallback errorCallback);
+    [JSBuiltin] Promise getStats(optional MediaStreamTrack? selector = null);
+    // Legacy signature: Promise getStats(MediaStreamTrack? selector
+    //                                    RTCStatsCallback successCallback,
+    //                                    RTCPeerConnectionErrorCallback errorCallback);
 
     [RaisesException] RTCDataChannel createDataChannel([TreatNullAs=EmptyString] DOMString label, optional Dictionary options);
 
index c3c2d18..b36559c 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -34,59 +34,119 @@ function createOffer()
 {
     "use strict";
 
-    return @createOfferOrAnswer(this, this.@queuedCreateOffer, "createOffer", arguments);
+    const peerConnection = this;
+
+    return @callbacksAndDictionaryOverload(arguments, "createOffer", function (options) {
+        // Promise mode
+        return @enqueueOperation(peerConnection, function () {
+            return peerConnection.@queuedCreateOffer(options);
+        });
+    }, function (successCallback, errorCallback, options) {
+        // Legacy callbacks mode
+        @enqueueOperation(peerConnection, function () {
+            return peerConnection.@queuedCreateOffer(options).then(successCallback, errorCallback);
+        });
+
+        return @Promise.@resolve(@undefined);
+    });
 }
 
 function createAnswer()
 {
     "use strict";
 
-    return @createOfferOrAnswer(this, this.@queuedCreateAnswer, "createAnswer", arguments);
+    const peerConnection = this;
+
+    return @callbacksAndDictionaryOverload(arguments, "createAnswer", function (options) {
+        // Promise mode
+        return @enqueueOperation(peerConnection, function () {
+            return peerConnection.@queuedCreateAnswer(options);
+        });
+    }, function (successCallback, errorCallback, options) {
+        // Legacy callbacks mode
+        @enqueueOperation(peerConnection, function () {
+            return peerConnection.@queuedCreateAnswer(options).then(successCallback, errorCallback);
+        });
+
+        return @Promise.@resolve(@undefined);
+    });
 }
 
 function setLocalDescription()
 {
     "use strict";
 
-    return @setLocalOrRemoteDescription(this, this.@queuedSetLocalDescription, "setLocalDescription", arguments);
+    const peerConnection = this;
+
+    const objectInfo = {
+        "constructor": @RTCSessionDescription,
+        "argName": "description",
+        "argType": "RTCSessionDescription"
+    };
+    return @objectAndCallbacksOverload(arguments, "setLocalDescription", objectInfo, function (description) {
+        // Promise mode
+        return @enqueueOperation(peerConnection, function () {
+            return peerConnection.@queuedSetLocalDescription(description);
+        });
+    }, function (description, successCallback, errorCallback) {
+        // Legacy callbacks mode
+        @enqueueOperation(peerConnection, function () {
+            return peerConnection.@queuedSetLocalDescription(description).then(successCallback, errorCallback);
+        });
+
+        return @Promise.@resolve(@undefined);
+    });
 }
 
 function setRemoteDescription()
 {
     "use strict";
 
-    return @setLocalOrRemoteDescription(this, this.@queuedSetRemoteDescription, "setRemoteDescription", arguments);
+    const peerConnection = this;
+
+    const objectInfo = {
+        "constructor": @RTCSessionDescription,
+        "argName": "description",
+        "argType": "RTCSessionDescription"
+    };
+    return @objectAndCallbacksOverload(arguments, "setRemoteDescription", objectInfo, function (description) {
+        // Promise mode
+        return @enqueueOperation(peerConnection, function () {
+            return peerConnection.@queuedSetRemoteDescription(description);
+        });
+    }, function (description, successCallback, errorCallback) {
+        // Legacy callbacks mode
+        @enqueueOperation(peerConnection, function () {
+            return peerConnection.@queuedSetRemoteDescription(description).then(successCallback, errorCallback);
+        });
+
+        return @Promise.@resolve(@undefined);
+    });
 }
 
 function addIceCandidate()
 {
     "use strict";
 
-    var peerConnection = this;
+    const peerConnection = this;
 
-    if (arguments.length < 1)
-        throw new @TypeError("Not enough arguments");
-
-    var candidate = arguments[0];
-    if (!(candidate instanceof @RTCIceCandidate))
-        throw new @TypeError("Argument 1 ('candidate') to RTCPeerConnection.addIceCandidate must be an instance of RTCIceCandidate");
-
-    if (arguments.length == 1) {
+    const objectInfo = {
+        "constructor": @RTCIceCandidate,
+        "argName": "candidate",
+        "argType": "RTCIceCandidate"
+    };
+    return @objectAndCallbacksOverload(arguments, "addIceCandidate", objectInfo, function (candidate) {
         // Promise mode
         return @enqueueOperation(peerConnection, function () {
             return peerConnection.@queuedAddIceCandidate(candidate);
         });
-    }
-
-    // Legacy callbacks mode (3 arguments)
-    if (arguments.length < 3)
-        throw new @TypeError("Not enough arguments");
-
-    var successCallback = @extractCallbackArg(arguments, 1, "successCallback", "addIceCandidate");
-    var errorCallback = @extractCallbackArg(arguments, 2, "errorCallback", "addIceCandidate");
+    }, function (candidate, successCallback, errorCallback) {
+        // Legacy callbacks mode
+        @enqueueOperation(peerConnection, function () {
+            return peerConnection.@queuedAddIceCandidate(candidate).then(successCallback, errorCallback);
+        });
 
-    @enqueueOperation(peerConnection, function () {
-        return peerConnection.@queuedAddIceCandidate(candidate).then(successCallback, errorCallback);
+        return @Promise.@resolve(@undefined);
     });
 }
 
@@ -94,26 +154,21 @@ function getStats()
 {
     "use strict";
 
-    var peerConnection = this;
-    var selector = null;
+    const peerConnection = this;
 
-    if (arguments.length) {
-        selector = arguments[0];
-        if (selector != null && !(selector instanceof @MediaStreamTrack))
-            throw new @TypeError("Argument 1 ('selector') to RTCPeerConnection.getStats must be an instance of MediaStreamTrack");
-    }
-
-    if (arguments.length <= 1) {
+    const objectInfo = {
+        "constructor": @MediaStreamTrack,
+        "argName": "selector",
+        "argType": "MediaStreamTrack",
+        "defaultsToNull": true
+    };
+    return @objectAndCallbacksOverload(arguments, "getStats", objectInfo, function (selector) {
         // Promise mode
         return peerConnection.@privateGetStats(selector);
-    }
-
-    // Legacy callbacks mode (3 arguments)
-    if (arguments.length < 3)
-        throw new @TypeError("Not enough arguments");
+    }, function (selector, successCallback, errorCallback) {
+        // Legacy callbacks mode
+        peerConnection.@privateGetStats(selector).then(successCallback, errorCallback);
 
-    var successCallback = @extractCallbackArg(arguments, 1, "successCallback", "getStats");
-    var errorCallback = @extractCallbackArg(arguments, 2, "errorCallback", "getStats");
-
-    peerConnection.@privateGetStats(selector).then(successCallback, errorCallback);
+        return @Promise.@resolve(@undefined);
+    });
 }
index 6ace5fd..898212a 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2015 Ericsson AB. All rights reserved.
+ * Copyright (C) 2015, 2016 Ericsson AB. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -39,7 +39,7 @@ function enqueueOperation(peerConnection, operation)
     if (!peerConnection.@operations)
         peerConnection.@operations = [];
 
-    var operations = peerConnection.@operations;
+    const operations = peerConnection.@operations;
 
     function runNext() {
         operations.@shift();
@@ -57,71 +57,74 @@ function enqueueOperation(peerConnection, operation)
     });
 }
 
-function createOfferOrAnswer(peerConnection, targetFunction, functionName, args)
+function objectAndCallbacksOverload(args, functionName, objectInfo, promiseMode, legacyMode)
 {
     "use strict";
 
-    var options = {};
+    let argsCount = args.length;
+    let objectArg = args[0];
+    let objectArgOk = false;
 
-    if (args.length <= 1) {
-        // Promise mode
-        if (args.length && @isDictionary(args[0]))
-            options = args[0]
+    if (!argsCount) {
+        if (!objectInfo.defaultsToNull)
+            return @Promise.@reject(new @TypeError("Not enough arguments"));
 
-        return @enqueueOperation(peerConnection, function () {
-            return targetFunction.@call(peerConnection, options);
-        });
+        objectArg = null;
+        objectArgOk = true;
+        argsCount = 1;
+    } else {
+        const hasMatchingType = objectArg instanceof objectInfo.constructor;
+        objectArgOk = objectInfo.defaultsToNull ? (objectArg === null || typeof objectArg === "undefined" || hasMatchingType) : hasMatchingType;
     }
 
-    // Legacy callbacks mode (2 or 3 arguments)
-    var successCallback = @extractCallbackArg(args, 0, "successCallback", functionName);
-    var errorCallback = @extractCallbackArg(args, 1, "errorCallback", functionName);
+    if (!objectArgOk)
+        return @Promise.@reject(new @TypeError(`Argument 1 ('${objectInfo.argName}') to RTCPeerConnection.${functionName} must be an instance of ${objectInfo.argType}`));
 
-    if (args.length > 2 && @isDictionary(args[2]))
-        options = args[2];
+    if (argsCount === 1)
+        return promiseMode(objectArg);
 
-    @enqueueOperation(peerConnection, function () {
-        return targetFunction.@call(peerConnection, options).then(successCallback, errorCallback);
-    });
+    // More than one argument: Legacy mode
+    if (argsCount < 3)
+        return @Promise.@reject(new @TypeError("Not enough arguments"));
+
+    const successCallback = args[1];
+    const errorCallback = args[2];
+
+    if (typeof successCallback !== "function")
+        return @Promise.@reject(new @TypeError(`Argument 2 ('successCallback') to RTCPeerConnection.${functionName} must be a function`));
+
+    if (typeof errorCallback !== "function")
+        return @Promise.@reject(new @TypeError(`Argument 3 ('errorCallback') to RTCPeerConnection.${functionName} must be a function`));
+
+    return legacyMode(objectArg, successCallback, errorCallback);
 }
 
-function setLocalOrRemoteDescription(peerConnection, targetFunction, functionName, args)
+function callbacksAndDictionaryOverload(args, functionName, promiseMode, legacyMode)
 {
     "use strict";
 
-    if (args.length < 1)
-        throw new @TypeError("Not enough arguments");
-
-    var description = args[0];
-    if (!(description instanceof @RTCSessionDescription))
-        throw new @TypeError("Argument 1 ('description') to RTCPeerConnection." + functionName + " must be an instance of RTCSessionDescription");
+    if (args.length <= 1) {
+        // Zero or one arguments: Promise mode
+        const options = args[0];
+        if (args.length && !@isDictionary(options))
+            return @Promise.@reject(new @TypeError(`Argument 1 ('options') to RTCPeerConnection.${functionName} must be a Dictionary`));
 
-    if (args.length == 1) {
-        // Promise mode
-        return @enqueueOperation(peerConnection, function () {
-            return targetFunction.@call(peerConnection, description);
-        });
+        return promiseMode(options);
     }
 
-    // Legacy callbacks mode (3 arguments)
-    if (args.length < 3)
-        throw new @TypeError("Not enough arguments");
+    // More than one argument: Legacy mode
+    const successCallback = args[0];
+    const errorCallback = args[1];
+    const options = args[2];
 
-    var successCallback = @extractCallbackArg(args, 1, "successCallback", functionName);
-    var errorCallback = @extractCallbackArg(args, 2, "errorCallback", functionName);
+    if (typeof successCallback !== "function")
+        return @Promise.@reject(new @TypeError(`Argument 1 ('successCallback') to RTCPeerConnection.${functionName} must be a function`));
 
-    @enqueueOperation(peerConnection, function () {
-        return targetFunction.@call(peerConnection, description).then(successCallback, errorCallback);
-    });
-}
-
-function extractCallbackArg(args, index, name, parentFunctionName)
-{
-    "use strict";
+    if (typeof errorCallback !== "function")
+        return @Promise.@reject(new @TypeError(`Argument 2 ('errorCallback') to RTCPeerConnection.${functionName} must be a function`));
 
-    var callback = args[index];
-    if (typeof callback !== "function")
-        throw new @TypeError("Argument " + (index + 1) + " ('" + name + "') to RTCPeerConnection." + parentFunctionName + " must be a Function");
+    if (args.length > 2 && !@isDictionary(options))
+        return @Promise.@reject(new @TypeError(`Argument 3 ('options') to RTCPeerConnection.${functionName} must be a Dictionary`));
 
-    return callback;
+    return legacyMode(successCallback, errorCallback, args[2]);
 }