File: RTCPeerConnection-onnegotiationneeded.html

package info (click to toggle)
thunderbird 1%3A60.9.0-1~deb9u1
  • links: PTS, VCS
  • area: main
  • in suites: stretch
  • size: 2,339,492 kB
  • sloc: cpp: 5,457,040; ansic: 2,360,385; python: 596,167; asm: 340,963; java: 326,296; xml: 258,830; sh: 84,445; makefile: 23,705; perl: 17,317; objc: 3,768; yacc: 1,766; ada: 1,681; lex: 1,364; pascal: 1,264; cs: 879; exp: 527; php: 436; lisp: 258; ruby: 153; awk: 152; sed: 53; csh: 27
file content (262 lines) | stat: -rw-r--r-- 9,790 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
<!doctype html>
<meta charset="utf-8">
<title>Test RTCPeerConnection.prototype.onnegotiationneeded</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="RTCPeerConnection-helper.js"></script>
<script>
  'use strict';

  // Test is based on the following editor draft:
  // https://w3c.github.io/webrtc-pc/archives/20170605/webrtc.html

  // The following helper functions are called from RTCPeerConnection-helper.js:
  //   generateAnswer
  //   test_never_resolve

  // Listen to the negotiationneeded event on a peer connection
  // Returns a promise that resolves when the first event is fired.
  // The resolve result is a dictionary with event and nextPromise,
  // which resolves when the next negotiationneeded event is fired.
  // This allow us to promisify the event listening and assert whether
  // an event is fired or not by testing whether a promise is resolved.
  function awaitNegotiation(pc) {
    if(pc.onnegotiationneeded) {
      throw new Error('connection is already attached with onnegotiationneeded event handler');
    }

    function waitNextNegotiation() {
      return new Promise(resolve => {
        pc.onnegotiationneeded = event => {
          const nextPromise = waitNextNegotiation();
          resolve({ nextPromise, event });
        }
      });
    }

    return waitNextNegotiation();
  }

  // Return a promise that rejects if the first promise is resolved before second promise.
  // Also rejects when either promise rejects.
  function assert_first_promise_fulfill_after_second(promise1, promise2, message) {
    if(!message) {
      message = 'first promise is resolved before second promise';
    }

    return new Promise((resolve, reject) => {
      let secondResolved = false;

      promise1.then(() => {
        if(secondResolved) {
          resolve();
        } else {
          assert_unreached(message);
        }
      })
      .catch(reject);

      promise2.then(() => {
        secondResolved = true;
      }, reject);
    });
  }

  /*
    4.7.3.  Updating the Negotiation-Needed flag

      To update the negotiation-needed flag
      5.  Set connection's [[needNegotiation]] slot to true.
      6.  Queue a task that runs the following steps:
          3.  Fire a simple event named negotiationneeded at connection.

      To check if negotiation is needed
      2.  If connection has created any RTCDataChannels, and no m= section has
          been negotiated yet for data, return "true".

    6.1.  RTCPeerConnection Interface Extensions

      createDataChannel
        14. If channel was the first RTCDataChannel created on connection,
            update the negotiation-needed flag for connection.
   */
  promise_test(t => {
    const pc = new RTCPeerConnection();
    const negotiated = awaitNegotiation(pc);

    pc.createDataChannel('test');
    return negotiated;
  }, 'Creating first data channel should fire negotiationneeded event');

  test_never_resolve(t => {
    const pc = new RTCPeerConnection();
    const negotiated = awaitNegotiation(pc);

    pc.createDataChannel('foo');
    return negotiated
      .then(({nextPromise}) => {
      pc.createDataChannel('bar');
      return nextPromise;
    });
  }, 'calling createDataChannel twice should fire negotiationneeded event once');

  /*
    4.7.3.  Updating the Negotiation-Needed flag
      To check if negotiation is needed
      3.  For each transceiver t in connection's set of transceivers, perform
          the following checks:
          1.  If t isn't stopped and isn't yet associated with an m= section
              according to [JSEP] (section 3.4.1.), return "true".

    5.1.  RTCPeerConnection Interface Extensions
      addTransceiver
        9.  Update the negotiation-needed flag for connection.
   */
  promise_test(t => {
    const pc = new RTCPeerConnection();
    const negotiated = awaitNegotiation(pc);

    pc.addTransceiver('audio');
    return negotiated;
  }, 'addTransceiver() should fire negotiationneeded event');

  /*
    4.7.3.  Updating the Negotiation-Needed flag
      To update the negotiation-needed flag
      4.  If connection's [[needNegotiation]] slot is already true, abort these steps.
   */
  test_never_resolve(t => {
    const pc = new RTCPeerConnection();
    const negotiated = awaitNegotiation(pc);

    pc.addTransceiver('audio');
    return negotiated
    .then(({nextPromise}) => {
      pc.addTransceiver('video');
      return nextPromise;
    });
  }, 'Calling addTransceiver() twice should fire negotiationneeded event once');

  /*
    4.7.3.  Updating the Negotiation-Needed flag
      To update the negotiation-needed flag
      4.  If connection's [[needNegotiation]] slot is already true, abort these steps.
   */
  test_never_resolve(t => {
    const pc = new RTCPeerConnection();
    const negotiated = awaitNegotiation(pc);

    pc.createDataChannel('test');
    return negotiated
    .then(({nextPromise}) => {
      pc.addTransceiver('video');
      return nextPromise;
    });
  }, 'Calling both addTransceiver() and createDataChannel() should fire negotiationneeded event once');

  /*
    4.7.3.  Updating the Negotiation-Needed flag
      To update the negotiation-needed flag
      2.  If connection's signaling state is not "stable", abort these steps.
   */
  test_never_resolve(t => {
    const pc = new RTCPeerConnection();
    const negotiated = awaitNegotiation(pc);

    return pc.createOffer({ offerToReceiveAudio: true })
    .then(offer => pc.setLocalDescription(offer))
    .then(() => negotiated)
    .then(({nextPromise}) => {
      assert_equals(pc.signalingState, 'have-local-offer');
      pc.createDataChannel('test');
      return nextPromise;
    });
  }, 'negotiationneeded event should not fire if signaling state is not stable');

  /*
    4.4.1.6.  Set the RTCSessionSessionDescription
      2.2.10. If connection's signaling state is now stable, update the negotiation-needed
              flag. If connection's [[NegotiationNeeded]] slot was true both before and after
              this update, queue a task that runs the following steps:
        2.  If connection's [[NegotiationNeeded]] slot is false, abort these steps.
        3.  Fire a simple event named negotiationneeded at connection.
   */
  promise_test(t => {
    const pc = new RTCPeerConnection();

    return assert_first_promise_fulfill_after_second(
      awaitNegotiation(pc),
      pc.createOffer({ offerToReceiveAudio: true })
      .then(offer =>
        pc.setLocalDescription(offer)
        .then(() => {
          pc.createDataChannel('test');
          return generateAnswer(offer);
        }))
      .then(answer => pc.setRemoteDescription(answer)),
      'Expect negotiationneeded promise to resolve after pc has set remote answer and go back to stable state');
  }, 'negotiationneeded event should fire only after signaling state go back to stable');

  /*
    TODO
    4.7.3.  Updating the Negotiation-Needed flag

      To update the negotiation-needed flag
      3.  If the result of checking if negotiation is needed is "false",
          clear the negotiation-needed flag by setting connection's
          [[needNegotiation]] slot to false, and abort these steps.
      6.  Queue a task that runs the following steps:
          2.  If connection's [[needNegotiation]] slot is false, abort these steps.

      To check if negotiation is needed
      3.  For each transceiver t in connection's set of transceivers, perform
          the following checks:
          2.  If t isn't stopped and is associated with an m= section according
              to [JSEP] (section 3.4.1.), then perform the following checks:
              1.  If t's direction is "sendrecv" or "sendonly", and the
                  associated m= section in connection's currentLocalDescription
                  doesn't contain an "a=msid" line, return "true".
              2.  If connection's currentLocalDescription if of type "offer",
                  and the direction of the associated m= section in neither the
                  offer nor answer matches t's direction, return "true".
              3.  If connection's currentLocalDescription if of type "answer",
                  and the direction of the associated m= section in the answer
                  does not match t's direction intersected with the offered
                  direction (as described in [JSEP] (section 5.3.1.)),
                  return "true".
          3.  If t is stopped and is associated with an m= section according
              to [JSEP] (section 3.4.1.), but the associated m= section is
              not yet rejected in connection's currentLocalDescription or
              currentRemoteDescription , return "true".
      4.  If all the preceding checks were performed and "true" was not returned,
          nothing remains to be negotiated; return "false".

    4.3.1.  RTCPeerConnection Operation

      When the RTCPeerConnection() constructor is invoked
        7.  Let connection have a [[needNegotiation]] internal slot, initialized to false.

    5.1.  RTCPeerConnection Interface Extensions

      addTrack
        10. Update the negotiation-needed flag for connection.

      removeTrack
        12. Update the negotiation-needed flag for connection.

    5.4.  RTCRtpTransceiver Interface

      setDirection
        7.  Update the negotiation-needed flag for connection.

      stop
        11. Update the negotiation-needed flag for connection.

    Untestable
    4.7.3.  Updating the Negotiation-Needed flag
      1.  If connection's [[isClosed]] slot is true, abort these steps.
      6.  Queue a task that runs the following steps:
          1.  If connection's [[isClosed]] slot is true, abort these steps.
   */

</script>