1/*
2 * Copyright (C) 2012 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer
12 *    in the documentation and/or other materials provided with the
13 *    distribution.
14 * 3. Neither the name of Google Inc. nor the names of its contributors
15 *    may be used to endorse or promote products derived from this
16 *    software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31#include "config.h"
32
33#include "modules/mediastream/RTCPeerConnection.h"
34
35#include "bindings/v8/ArrayValue.h"
36#include "bindings/v8/ExceptionState.h"
37#include "core/dom/Document.h"
38#include "core/events/Event.h"
39#include "core/dom/ExceptionCode.h"
40#include "core/dom/ExecutionContext.h"
41#include "core/html/VoidCallback.h"
42#include "core/loader/FrameLoader.h"
43#include "core/loader/FrameLoaderClient.h"
44#include "core/frame/Frame.h"
45#include "core/platform/mediastream/RTCDataChannelHandler.h"
46#include "modules/mediastream/MediaConstraintsImpl.h"
47#include "modules/mediastream/MediaStreamEvent.h"
48#include "modules/mediastream/RTCDTMFSender.h"
49#include "modules/mediastream/RTCDataChannel.h"
50#include "modules/mediastream/RTCDataChannelEvent.h"
51#include "modules/mediastream/RTCErrorCallback.h"
52#include "modules/mediastream/RTCIceCandidateEvent.h"
53#include "modules/mediastream/RTCSessionDescription.h"
54#include "modules/mediastream/RTCSessionDescriptionCallback.h"
55#include "modules/mediastream/RTCSessionDescriptionRequestImpl.h"
56#include "modules/mediastream/RTCStatsCallback.h"
57#include "modules/mediastream/RTCStatsRequestImpl.h"
58#include "modules/mediastream/RTCVoidRequestImpl.h"
59#include "platform/mediastream/RTCConfiguration.h"
60#include "public/platform/WebRTCDataChannelInit.h"
61#include "public/platform/WebRTCICECandidate.h"
62#include "public/platform/WebRTCSessionDescription.h"
63
64namespace WebCore {
65
66PassRefPtr<RTCConfiguration> RTCPeerConnection::parseConfiguration(const Dictionary& configuration, ExceptionState& exceptionState)
67{
68    if (configuration.isUndefinedOrNull())
69        return 0;
70
71    ArrayValue iceServers;
72    bool ok = configuration.get("iceServers", iceServers);
73    if (!ok || iceServers.isUndefinedOrNull()) {
74        exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
75        return 0;
76    }
77
78    size_t numberOfServers;
79    ok = iceServers.length(numberOfServers);
80    if (!ok) {
81        exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
82        return 0;
83    }
84
85    RefPtr<RTCConfiguration> rtcConfiguration = RTCConfiguration::create();
86
87    for (size_t i = 0; i < numberOfServers; ++i) {
88        Dictionary iceServer;
89        ok = iceServers.get(i, iceServer);
90        if (!ok) {
91            exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
92            return 0;
93        }
94
95        String urlString, username, credential;
96        ok = iceServer.get("url", urlString);
97        if (!ok) {
98            exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
99            return 0;
100        }
101        KURL url(KURL(), urlString);
102        if (!url.isValid() || !(url.protocolIs("turn") || url.protocolIs("turns") || url.protocolIs("stun"))) {
103            exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
104            return 0;
105        }
106
107        iceServer.get("username", username);
108        iceServer.get("credential", credential);
109
110        rtcConfiguration->appendServer(RTCIceServer::create(url, username, credential));
111    }
112
113    return rtcConfiguration.release();
114}
115
116PassRefPtr<RTCPeerConnection> RTCPeerConnection::create(ExecutionContext* context, const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionState& exceptionState)
117{
118    RefPtr<RTCConfiguration> configuration = parseConfiguration(rtcConfiguration, exceptionState);
119    if (exceptionState.hadException())
120        return 0;
121
122    RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState);
123    if (exceptionState.hadException())
124        return 0;
125
126    RefPtr<RTCPeerConnection> peerConnection = adoptRef(new RTCPeerConnection(context, configuration.release(), constraints.release(), exceptionState));
127    peerConnection->suspendIfNeeded();
128    if (exceptionState.hadException())
129        return 0;
130
131    return peerConnection.release();
132}
133
134RTCPeerConnection::RTCPeerConnection(ExecutionContext* context, PassRefPtr<RTCConfiguration> configuration, PassRefPtr<MediaConstraints> constraints, ExceptionState& exceptionState)
135    : ActiveDOMObject(context)
136    , m_signalingState(SignalingStateStable)
137    , m_iceGatheringState(IceGatheringStateNew)
138    , m_iceConnectionState(IceConnectionStateNew)
139    , m_dispatchScheduledEventRunner(this, &RTCPeerConnection::dispatchScheduledEvent)
140    , m_stopped(false)
141{
142    ScriptWrappable::init(this);
143    Document* document = toDocument(executionContext());
144
145    if (!document->frame()) {
146        exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
147        return;
148    }
149
150    m_peerHandler = RTCPeerConnectionHandler::create(this);
151    if (!m_peerHandler) {
152        exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
153        return;
154    }
155
156    document->frame()->loader().client()->dispatchWillStartUsingPeerConnectionHandler(m_peerHandler.get());
157
158    if (!m_peerHandler->initialize(configuration, constraints)) {
159        exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
160        return;
161    }
162}
163
164RTCPeerConnection::~RTCPeerConnection()
165{
166    stop();
167}
168
169void RTCPeerConnection::createOffer(PassOwnPtr<RTCSessionDescriptionCallback> successCallback, PassOwnPtr<RTCErrorCallback> errorCallback, const Dictionary& mediaConstraints, ExceptionState& exceptionState)
170{
171    if (m_signalingState == SignalingStateClosed) {
172        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
173        return;
174    }
175
176    if (!successCallback) {
177        exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
178        return;
179    }
180
181    RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState);
182    if (exceptionState.hadException())
183        return;
184
185    RefPtr<RTCSessionDescriptionRequestImpl> request = RTCSessionDescriptionRequestImpl::create(executionContext(), successCallback, errorCallback);
186    m_peerHandler->createOffer(request.release(), constraints);
187}
188
189void RTCPeerConnection::createAnswer(PassOwnPtr<RTCSessionDescriptionCallback> successCallback, PassOwnPtr<RTCErrorCallback> errorCallback, const Dictionary& mediaConstraints, ExceptionState& exceptionState)
190{
191    if (m_signalingState == SignalingStateClosed) {
192        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
193        return;
194    }
195
196    if (!successCallback) {
197        exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
198        return;
199    }
200
201    RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState);
202    if (exceptionState.hadException())
203        return;
204
205    RefPtr<RTCSessionDescriptionRequestImpl> request = RTCSessionDescriptionRequestImpl::create(executionContext(), successCallback, errorCallback);
206    m_peerHandler->createAnswer(request.release(), constraints.release());
207}
208
209void RTCPeerConnection::setLocalDescription(PassRefPtr<RTCSessionDescription> prpSessionDescription, PassOwnPtr<VoidCallback> successCallback, PassOwnPtr<RTCErrorCallback> errorCallback, ExceptionState& exceptionState)
210{
211    if (m_signalingState == SignalingStateClosed) {
212        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
213        return;
214    }
215
216    RefPtr<RTCSessionDescription> sessionDescription = prpSessionDescription;
217    if (!sessionDescription) {
218        exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
219        return;
220    }
221
222    RefPtr<RTCVoidRequestImpl> request = RTCVoidRequestImpl::create(executionContext(), successCallback, errorCallback);
223    m_peerHandler->setLocalDescription(request.release(), sessionDescription->webSessionDescription());
224}
225
226PassRefPtr<RTCSessionDescription> RTCPeerConnection::localDescription(ExceptionState& exceptionState)
227{
228    blink::WebRTCSessionDescription webSessionDescription = m_peerHandler->localDescription();
229    if (webSessionDescription.isNull())
230        return 0;
231
232    RefPtr<RTCSessionDescription> sessionDescription = RTCSessionDescription::create(webSessionDescription);
233    return sessionDescription.release();
234}
235
236void RTCPeerConnection::setRemoteDescription(PassRefPtr<RTCSessionDescription> prpSessionDescription, PassOwnPtr<VoidCallback> successCallback, PassOwnPtr<RTCErrorCallback> errorCallback, ExceptionState& exceptionState)
237{
238    if (m_signalingState == SignalingStateClosed) {
239        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
240        return;
241    }
242
243    RefPtr<RTCSessionDescription> sessionDescription = prpSessionDescription;
244    if (!sessionDescription) {
245        exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
246        return;
247    }
248
249    RefPtr<RTCVoidRequestImpl> request = RTCVoidRequestImpl::create(executionContext(), successCallback, errorCallback);
250    m_peerHandler->setRemoteDescription(request.release(), sessionDescription->webSessionDescription());
251}
252
253PassRefPtr<RTCSessionDescription> RTCPeerConnection::remoteDescription(ExceptionState& exceptionState)
254{
255    blink::WebRTCSessionDescription webSessionDescription = m_peerHandler->remoteDescription();
256    if (webSessionDescription.isNull())
257        return 0;
258
259    RefPtr<RTCSessionDescription> desc = RTCSessionDescription::create(webSessionDescription);
260    return desc.release();
261}
262
263void RTCPeerConnection::updateIce(const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionState& exceptionState)
264{
265    if (m_signalingState == SignalingStateClosed) {
266        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
267        return;
268    }
269
270    RefPtr<RTCConfiguration> configuration = parseConfiguration(rtcConfiguration, exceptionState);
271    if (exceptionState.hadException())
272        return;
273
274    RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState);
275    if (exceptionState.hadException())
276        return;
277
278    bool valid = m_peerHandler->updateIce(configuration, constraints);
279    if (!valid)
280        exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
281}
282
283void RTCPeerConnection::addIceCandidate(RTCIceCandidate* iceCandidate, ExceptionState& exceptionState)
284{
285    if (m_signalingState == SignalingStateClosed) {
286        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
287        return;
288    }
289
290    if (!iceCandidate) {
291        exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
292        return;
293    }
294
295    bool valid = m_peerHandler->addIceCandidate(iceCandidate->webCandidate());
296    if (!valid)
297        exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
298}
299
300void RTCPeerConnection::addIceCandidate(RTCIceCandidate* iceCandidate, PassOwnPtr<VoidCallback> successCallback, PassOwnPtr<RTCErrorCallback> errorCallback, ExceptionState& exceptionState)
301{
302    if (m_signalingState == SignalingStateClosed) {
303        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
304        return;
305    }
306
307    if (!iceCandidate || !successCallback || !errorCallback) {
308        exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
309        return;
310    }
311
312    RefPtr<RTCVoidRequestImpl> request = RTCVoidRequestImpl::create(executionContext(), successCallback, errorCallback);
313
314    bool implemented = m_peerHandler->addIceCandidate(request.release(), iceCandidate->webCandidate());
315    if (!implemented)
316        exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
317}
318
319String RTCPeerConnection::signalingState() const
320{
321    switch (m_signalingState) {
322    case SignalingStateStable:
323        return "stable";
324    case SignalingStateHaveLocalOffer:
325        return "have-local-offer";
326    case SignalingStateHaveRemoteOffer:
327        return "have-remote-offer";
328    case SignalingStateHaveLocalPrAnswer:
329        return "have-local-pranswer";
330    case SignalingStateHaveRemotePrAnswer:
331        return "have-remote-pranswer";
332    case SignalingStateClosed:
333        return "closed";
334    }
335
336    ASSERT_NOT_REACHED();
337    return String();
338}
339
340String RTCPeerConnection::iceGatheringState() const
341{
342    switch (m_iceGatheringState) {
343    case IceGatheringStateNew:
344        return "new";
345    case IceGatheringStateGathering:
346        return "gathering";
347    case IceGatheringStateComplete:
348        return "complete";
349    }
350
351    ASSERT_NOT_REACHED();
352    return String();
353}
354
355String RTCPeerConnection::iceConnectionState() const
356{
357    switch (m_iceConnectionState) {
358    case IceConnectionStateNew:
359        return "new";
360    case IceConnectionStateChecking:
361        return "checking";
362    case IceConnectionStateConnected:
363        return "connected";
364    case IceConnectionStateCompleted:
365        return "completed";
366    case IceConnectionStateFailed:
367        return "failed";
368    case IceConnectionStateDisconnected:
369        return "disconnected";
370    case IceConnectionStateClosed:
371        return "closed";
372    }
373
374    ASSERT_NOT_REACHED();
375    return String();
376}
377
378void RTCPeerConnection::addStream(PassRefPtr<MediaStream> prpStream, const Dictionary& mediaConstraints, ExceptionState& exceptionState)
379{
380    if (m_signalingState == SignalingStateClosed) {
381        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
382        return;
383    }
384
385    RefPtr<MediaStream> stream = prpStream;
386    if (!stream) {
387        exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
388        return;
389    }
390
391    if (m_localStreams.contains(stream))
392        return;
393
394    RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState);
395    if (exceptionState.hadException())
396        return;
397
398    m_localStreams.append(stream);
399
400    bool valid = m_peerHandler->addStream(stream->descriptor(), constraints);
401    if (!valid)
402        exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
403}
404
405void RTCPeerConnection::removeStream(PassRefPtr<MediaStream> prpStream, ExceptionState& exceptionState)
406{
407    if (m_signalingState == SignalingStateClosed) {
408        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
409        return;
410    }
411
412    if (!prpStream) {
413        exceptionState.throwUninformativeAndGenericDOMException(TypeMismatchError);
414        return;
415    }
416
417    RefPtr<MediaStream> stream = prpStream;
418
419    size_t pos = m_localStreams.find(stream);
420    if (pos == kNotFound)
421        return;
422
423    m_localStreams.remove(pos);
424
425    m_peerHandler->removeStream(stream->descriptor());
426}
427
428MediaStreamVector RTCPeerConnection::getLocalStreams() const
429{
430    return m_localStreams;
431}
432
433MediaStreamVector RTCPeerConnection::getRemoteStreams() const
434{
435    return m_remoteStreams;
436}
437
438MediaStream* RTCPeerConnection::getStreamById(const String& streamId)
439{
440    for (MediaStreamVector::iterator iter = m_localStreams.begin(); iter != m_localStreams.end(); ++iter) {
441        if ((*iter)->id() == streamId)
442            return iter->get();
443    }
444
445    for (MediaStreamVector::iterator iter = m_remoteStreams.begin(); iter != m_remoteStreams.end(); ++iter) {
446        if ((*iter)->id() == streamId)
447            return iter->get();
448    }
449
450    return 0;
451}
452
453void RTCPeerConnection::getStats(PassOwnPtr<RTCStatsCallback> successCallback, PassRefPtr<MediaStreamTrack> selector)
454{
455    RefPtr<RTCStatsRequestImpl> statsRequest = RTCStatsRequestImpl::create(executionContext(), successCallback, selector);
456    // FIXME: Add passing selector as part of the statsRequest.
457    m_peerHandler->getStats(statsRequest.release());
458}
459
460PassRefPtr<RTCDataChannel> RTCPeerConnection::createDataChannel(String label, const Dictionary& options, ExceptionState& exceptionState)
461{
462    if (m_signalingState == SignalingStateClosed) {
463        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
464        return 0;
465    }
466
467    blink::WebRTCDataChannelInit init;
468    options.get("ordered", init.ordered);
469    options.get("negotiated", init.negotiated);
470
471    unsigned short value = 0;
472    if (options.get("id", value))
473        init.id = value;
474    if (options.get("maxRetransmits", value))
475        init.maxRetransmits = value;
476    if (options.get("maxRetransmitTime", value))
477        init.maxRetransmitTime = value;
478
479    String protocolString;
480    options.get("protocol", protocolString);
481    init.protocol = protocolString;
482
483    RefPtr<RTCDataChannel> channel = RTCDataChannel::create(executionContext(), m_peerHandler.get(), label, init, exceptionState);
484    if (exceptionState.hadException())
485        return 0;
486    m_dataChannels.append(channel);
487    return channel.release();
488}
489
490bool RTCPeerConnection::hasLocalStreamWithTrackId(const String& trackId)
491{
492    for (MediaStreamVector::iterator iter = m_localStreams.begin(); iter != m_localStreams.end(); ++iter) {
493        if ((*iter)->getTrackById(trackId))
494            return true;
495    }
496    return false;
497}
498
499PassRefPtr<RTCDTMFSender> RTCPeerConnection::createDTMFSender(PassRefPtr<MediaStreamTrack> prpTrack, ExceptionState& exceptionState)
500{
501    if (m_signalingState == SignalingStateClosed) {
502        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
503        return 0;
504    }
505
506    if (!prpTrack) {
507        exceptionState.throwUninformativeAndGenericTypeError();
508        return 0;
509    }
510
511    RefPtr<MediaStreamTrack> track = prpTrack;
512
513    if (!hasLocalStreamWithTrackId(track->id())) {
514        exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
515        return 0;
516    }
517
518    RefPtr<RTCDTMFSender> dtmfSender = RTCDTMFSender::create(executionContext(), m_peerHandler.get(), track.release(), exceptionState);
519    if (exceptionState.hadException())
520        return 0;
521    return dtmfSender.release();
522}
523
524void RTCPeerConnection::close(ExceptionState& exceptionState)
525{
526    if (m_signalingState == SignalingStateClosed) {
527        exceptionState.throwUninformativeAndGenericDOMException(InvalidStateError);
528        return;
529    }
530
531    m_peerHandler->stop();
532
533    changeIceConnectionState(IceConnectionStateClosed);
534    changeIceGatheringState(IceGatheringStateComplete);
535    changeSignalingState(SignalingStateClosed);
536}
537
538void RTCPeerConnection::negotiationNeeded()
539{
540    scheduleDispatchEvent(Event::create(EventTypeNames::negotiationneeded));
541}
542
543void RTCPeerConnection::didGenerateIceCandidate(blink::WebRTCICECandidate webCandidate)
544{
545    ASSERT(executionContext()->isContextThread());
546    if (webCandidate.isNull())
547        scheduleDispatchEvent(RTCIceCandidateEvent::create(false, false, 0));
548    else {
549        RefPtr<RTCIceCandidate> iceCandidate = RTCIceCandidate::create(webCandidate);
550        scheduleDispatchEvent(RTCIceCandidateEvent::create(false, false, iceCandidate.release()));
551    }
552}
553
554void RTCPeerConnection::didChangeSignalingState(SignalingState newState)
555{
556    ASSERT(executionContext()->isContextThread());
557    changeSignalingState(newState);
558}
559
560void RTCPeerConnection::didChangeIceGatheringState(IceGatheringState newState)
561{
562    ASSERT(executionContext()->isContextThread());
563    changeIceGatheringState(newState);
564}
565
566void RTCPeerConnection::didChangeIceConnectionState(IceConnectionState newState)
567{
568    ASSERT(executionContext()->isContextThread());
569    changeIceConnectionState(newState);
570}
571
572void RTCPeerConnection::didAddRemoteStream(PassRefPtr<MediaStreamDescriptor> streamDescriptor)
573{
574    ASSERT(executionContext()->isContextThread());
575
576    if (m_signalingState == SignalingStateClosed)
577        return;
578
579    RefPtr<MediaStream> stream = MediaStream::create(executionContext(), streamDescriptor);
580    m_remoteStreams.append(stream);
581
582    scheduleDispatchEvent(MediaStreamEvent::create(EventTypeNames::addstream, false, false, stream.release()));
583}
584
585void RTCPeerConnection::didRemoveRemoteStream(MediaStreamDescriptor* streamDescriptor)
586{
587    ASSERT(executionContext()->isContextThread());
588    ASSERT(streamDescriptor->client());
589
590    RefPtr<MediaStream> stream = static_cast<MediaStream*>(streamDescriptor->client());
591    stream->streamEnded();
592
593    if (m_signalingState == SignalingStateClosed)
594        return;
595
596    size_t pos = m_remoteStreams.find(stream);
597    ASSERT(pos != kNotFound);
598    m_remoteStreams.remove(pos);
599
600    scheduleDispatchEvent(MediaStreamEvent::create(EventTypeNames::removestream, false, false, stream.release()));
601}
602
603void RTCPeerConnection::didAddRemoteDataChannel(PassOwnPtr<RTCDataChannelHandler> handler)
604{
605    ASSERT(executionContext()->isContextThread());
606
607    if (m_signalingState == SignalingStateClosed)
608        return;
609
610    RefPtr<RTCDataChannel> channel = RTCDataChannel::create(executionContext(), handler);
611    m_dataChannels.append(channel);
612
613    scheduleDispatchEvent(RTCDataChannelEvent::create(EventTypeNames::datachannel, false, false, channel.release()));
614}
615
616const AtomicString& RTCPeerConnection::interfaceName() const
617{
618    return EventTargetNames::RTCPeerConnection;
619}
620
621ExecutionContext* RTCPeerConnection::executionContext() const
622{
623    return ActiveDOMObject::executionContext();
624}
625
626void RTCPeerConnection::suspend()
627{
628    m_dispatchScheduledEventRunner.suspend();
629}
630
631void RTCPeerConnection::resume()
632{
633    m_dispatchScheduledEventRunner.resume();
634}
635
636void RTCPeerConnection::stop()
637{
638    if (m_stopped)
639        return;
640
641    m_stopped = true;
642    m_iceConnectionState = IceConnectionStateClosed;
643    m_signalingState = SignalingStateClosed;
644
645    Vector<RefPtr<RTCDataChannel> >::iterator i = m_dataChannels.begin();
646    for (; i != m_dataChannels.end(); ++i)
647        (*i)->stop();
648
649    m_dispatchScheduledEventRunner.stop();
650}
651
652void RTCPeerConnection::changeSignalingState(SignalingState signalingState)
653{
654    if (m_signalingState != SignalingStateClosed && m_signalingState != signalingState) {
655        m_signalingState = signalingState;
656        scheduleDispatchEvent(Event::create(EventTypeNames::signalingstatechange));
657    }
658}
659
660void RTCPeerConnection::changeIceGatheringState(IceGatheringState iceGatheringState)
661{
662    m_iceGatheringState = iceGatheringState;
663}
664
665void RTCPeerConnection::changeIceConnectionState(IceConnectionState iceConnectionState)
666{
667    if (m_iceConnectionState != IceConnectionStateClosed && m_iceConnectionState != iceConnectionState) {
668        m_iceConnectionState = iceConnectionState;
669        scheduleDispatchEvent(Event::create(EventTypeNames::iceconnectionstatechange));
670    }
671}
672
673void RTCPeerConnection::scheduleDispatchEvent(PassRefPtr<Event> event)
674{
675    m_scheduledEvents.append(event);
676
677    m_dispatchScheduledEventRunner.runAsync();
678}
679
680void RTCPeerConnection::dispatchScheduledEvent()
681{
682    if (m_stopped)
683        return;
684
685    Vector<RefPtr<Event> > events;
686    events.swap(m_scheduledEvents);
687
688    Vector<RefPtr<Event> >::iterator it = events.begin();
689    for (; it != events.end(); ++it)
690        dispatchEvent((*it).release());
691
692    events.clear();
693}
694
695} // namespace WebCore
696