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/ExceptionMessages.h"
37#include "bindings/v8/ExceptionState.h"
38#include "core/dom/Document.h"
39#include "core/dom/ExceptionCode.h"
40#include "core/dom/ExecutionContext.h"
41#include "core/frame/LocalFrame.h"
42#include "core/html/VoidCallback.h"
43#include "core/loader/FrameLoader.h"
44#include "core/loader/FrameLoaderClient.h"
45#include "modules/mediastream/MediaConstraintsImpl.h"
46#include "modules/mediastream/MediaStreamEvent.h"
47#include "modules/mediastream/RTCDTMFSender.h"
48#include "modules/mediastream/RTCDataChannel.h"
49#include "modules/mediastream/RTCDataChannelEvent.h"
50#include "modules/mediastream/RTCErrorCallback.h"
51#include "modules/mediastream/RTCIceCandidateEvent.h"
52#include "modules/mediastream/RTCSessionDescription.h"
53#include "modules/mediastream/RTCSessionDescriptionCallback.h"
54#include "modules/mediastream/RTCSessionDescriptionRequestImpl.h"
55#include "modules/mediastream/RTCStatsCallback.h"
56#include "modules/mediastream/RTCStatsRequestImpl.h"
57#include "modules/mediastream/RTCVoidRequestImpl.h"
58#include "platform/mediastream/RTCConfiguration.h"
59#include "public/platform/Platform.h"
60#include "public/platform/WebMediaStream.h"
61#include "public/platform/WebRTCConfiguration.h"
62#include "public/platform/WebRTCDataChannelHandler.h"
63#include "public/platform/WebRTCDataChannelInit.h"
64#include "public/platform/WebRTCICECandidate.h"
65#include "public/platform/WebRTCSessionDescription.h"
66#include "public/platform/WebRTCSessionDescriptionRequest.h"
67#include "public/platform/WebRTCStatsRequest.h"
68#include "public/platform/WebRTCVoidRequest.h"
69
70namespace WebCore {
71
72namespace {
73
74static bool throwExceptionIfSignalingStateClosed(RTCPeerConnection::SignalingState state, ExceptionState& exceptionState)
75{
76    if (state == RTCPeerConnection::SignalingStateClosed) {
77        exceptionState.throwDOMException(InvalidStateError, "The RTCPeerConnection's signalingState is 'closed'.");
78        return true;
79    }
80
81    return false;
82}
83
84} // namespace
85
86PassRefPtr<RTCConfiguration> RTCPeerConnection::parseConfiguration(const Dictionary& configuration, ExceptionState& exceptionState)
87{
88    if (configuration.isUndefinedOrNull())
89        return nullptr;
90
91    ArrayValue iceServers;
92    bool ok = configuration.get("iceServers", iceServers);
93    if (!ok || iceServers.isUndefinedOrNull()) {
94        exceptionState.throwTypeError("Malformed RTCConfiguration");
95        return nullptr;
96    }
97
98    size_t numberOfServers;
99    ok = iceServers.length(numberOfServers);
100    if (!ok) {
101        exceptionState.throwTypeError("Malformed RTCConfiguration");
102        return nullptr;
103    }
104
105    RefPtr<RTCConfiguration> rtcConfiguration = RTCConfiguration::create();
106
107    for (size_t i = 0; i < numberOfServers; ++i) {
108        Dictionary iceServer;
109        ok = iceServers.get(i, iceServer);
110        if (!ok) {
111            exceptionState.throwTypeError("Malformed RTCIceServer");
112            return nullptr;
113        }
114
115        Vector<String> names;
116        iceServer.getOwnPropertyNames(names);
117
118        Vector<String> urlStrings;
119        if (names.contains("urls")) {
120            if (!iceServer.get("urls", urlStrings) || !urlStrings.size()) {
121                String urlString;
122                if (iceServer.get("urls", urlString)) {
123                    urlStrings.append(urlString);
124                } else {
125                    exceptionState.throwTypeError("Malformed RTCIceServer");
126                    return nullptr;
127                }
128            }
129        } else if (names.contains("url")) {
130            String urlString;
131            if (iceServer.get("url", urlString)) {
132                urlStrings.append(urlString);
133            } else {
134                exceptionState.throwTypeError("Malformed RTCIceServer");
135                return nullptr;
136            }
137        } else {
138            exceptionState.throwTypeError("Malformed RTCIceServer");
139            return nullptr;
140        }
141
142        String username, credential;
143        iceServer.get("username", username);
144        iceServer.get("credential", credential);
145
146        for (Vector<String>::iterator iter = urlStrings.begin(); iter != urlStrings.end(); ++iter) {
147            KURL url(KURL(), *iter);
148            if (!url.isValid() || !(url.protocolIs("turn") || url.protocolIs("turns") || url.protocolIs("stun"))) {
149                exceptionState.throwTypeError("Malformed URL");
150                return nullptr;
151            }
152
153            rtcConfiguration->appendServer(RTCIceServer::create(url, username, credential));
154        }
155    }
156
157    return rtcConfiguration.release();
158}
159
160PassRefPtrWillBeRawPtr<RTCPeerConnection> RTCPeerConnection::create(ExecutionContext* context, const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionState& exceptionState)
161{
162    RefPtr<RTCConfiguration> configuration = parseConfiguration(rtcConfiguration, exceptionState);
163    if (exceptionState.hadException())
164        return nullptr;
165
166    blink::WebMediaConstraints constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState);
167    if (exceptionState.hadException())
168        return nullptr;
169
170    RefPtrWillBeRawPtr<RTCPeerConnection> peerConnection = adoptRefWillBeRefCountedGarbageCollected(new RTCPeerConnection(context, configuration.release(), constraints, exceptionState));
171    peerConnection->suspendIfNeeded();
172    if (exceptionState.hadException())
173        return nullptr;
174
175    return peerConnection.release();
176}
177
178RTCPeerConnection::RTCPeerConnection(ExecutionContext* context, PassRefPtr<RTCConfiguration> configuration, blink::WebMediaConstraints constraints, ExceptionState& exceptionState)
179    : ActiveDOMObject(context)
180    , m_signalingState(SignalingStateStable)
181    , m_iceGatheringState(ICEGatheringStateNew)
182    , m_iceConnectionState(ICEConnectionStateNew)
183    , m_dispatchScheduledEventRunner(this, &RTCPeerConnection::dispatchScheduledEvent)
184    , m_stopped(false)
185    , m_closed(false)
186{
187    ScriptWrappable::init(this);
188    Document* document = toDocument(executionContext());
189
190    // If we fail, set |m_closed| and |m_stopped| to true, to avoid hitting the assert in the destructor.
191
192    if (!document->frame()) {
193        m_closed = true;
194        m_stopped = true;
195        exceptionState.throwDOMException(NotSupportedError, "PeerConnections may not be created in detached documents.");
196        return;
197    }
198
199    m_peerHandler = adoptPtr(blink::Platform::current()->createRTCPeerConnectionHandler(this));
200    if (!m_peerHandler) {
201        m_closed = true;
202        m_stopped = true;
203        exceptionState.throwDOMException(NotSupportedError, "No PeerConnection handler can be created, perhaps WebRTC is disabled?");
204        return;
205    }
206
207    document->frame()->loader().client()->dispatchWillStartUsingPeerConnectionHandler(m_peerHandler.get());
208
209    if (!m_peerHandler->initialize(configuration, constraints)) {
210        m_closed = true;
211        m_stopped = true;
212        exceptionState.throwDOMException(NotSupportedError, "Failed to initialize native PeerConnection.");
213        return;
214    }
215}
216
217RTCPeerConnection::~RTCPeerConnection()
218{
219    // This checks that close() or stop() is called before the destructor.
220    // We are assuming that a wrapper is always created when RTCPeerConnection is created.
221    ASSERT(m_closed || m_stopped);
222
223#if !ENABLE(OILPAN)
224    stop();
225#endif
226}
227
228void RTCPeerConnection::createOffer(PassOwnPtr<RTCSessionDescriptionCallback> successCallback, PassOwnPtr<RTCErrorCallback> errorCallback, const Dictionary& mediaConstraints, ExceptionState& exceptionState)
229{
230    if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState))
231        return;
232
233    ASSERT(successCallback);
234
235    blink::WebMediaConstraints constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState);
236    if (exceptionState.hadException())
237        return;
238
239    RefPtr<RTCSessionDescriptionRequest> request = RTCSessionDescriptionRequestImpl::create(executionContext(), this, successCallback, errorCallback);
240    m_peerHandler->createOffer(request.release(), constraints);
241}
242
243void RTCPeerConnection::createAnswer(PassOwnPtr<RTCSessionDescriptionCallback> successCallback, PassOwnPtr<RTCErrorCallback> errorCallback, const Dictionary& mediaConstraints, ExceptionState& exceptionState)
244{
245    if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState))
246        return;
247
248    ASSERT(successCallback);
249
250    blink::WebMediaConstraints constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState);
251    if (exceptionState.hadException())
252        return;
253
254    RefPtr<RTCSessionDescriptionRequest> request = RTCSessionDescriptionRequestImpl::create(executionContext(), this, successCallback, errorCallback);
255    m_peerHandler->createAnswer(request.release(), constraints);
256}
257
258void RTCPeerConnection::setLocalDescription(PassRefPtrWillBeRawPtr<RTCSessionDescription> prpSessionDescription, PassOwnPtr<VoidCallback> successCallback, PassOwnPtr<RTCErrorCallback> errorCallback, ExceptionState& exceptionState)
259{
260    if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState))
261        return;
262
263    RefPtrWillBeRawPtr<RTCSessionDescription> sessionDescription = prpSessionDescription;
264    if (!sessionDescription) {
265        exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "RTCSessionDescription"));
266        return;
267    }
268
269    RefPtr<RTCVoidRequest> request = RTCVoidRequestImpl::create(executionContext(), this, successCallback, errorCallback);
270    m_peerHandler->setLocalDescription(request.release(), sessionDescription->webSessionDescription());
271}
272
273PassRefPtrWillBeRawPtr<RTCSessionDescription> RTCPeerConnection::localDescription(ExceptionState& exceptionState)
274{
275    blink::WebRTCSessionDescription webSessionDescription = m_peerHandler->localDescription();
276    if (webSessionDescription.isNull())
277        return nullptr;
278
279    return RTCSessionDescription::create(webSessionDescription);
280}
281
282void RTCPeerConnection::setRemoteDescription(PassRefPtrWillBeRawPtr<RTCSessionDescription> prpSessionDescription, PassOwnPtr<VoidCallback> successCallback, PassOwnPtr<RTCErrorCallback> errorCallback, ExceptionState& exceptionState)
283{
284    if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState))
285        return;
286
287    RefPtrWillBeRawPtr<RTCSessionDescription> sessionDescription = prpSessionDescription;
288    if (!sessionDescription) {
289        exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "RTCSessionDescription"));
290        return;
291    }
292
293    RefPtr<RTCVoidRequest> request = RTCVoidRequestImpl::create(executionContext(), this, successCallback, errorCallback);
294    m_peerHandler->setRemoteDescription(request.release(), sessionDescription->webSessionDescription());
295}
296
297PassRefPtrWillBeRawPtr<RTCSessionDescription> RTCPeerConnection::remoteDescription(ExceptionState& exceptionState)
298{
299    blink::WebRTCSessionDescription webSessionDescription = m_peerHandler->remoteDescription();
300    if (webSessionDescription.isNull())
301        return nullptr;
302
303    return RTCSessionDescription::create(webSessionDescription);
304}
305
306void RTCPeerConnection::updateIce(const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionState& exceptionState)
307{
308    if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState))
309        return;
310
311    RefPtr<RTCConfiguration> configuration = parseConfiguration(rtcConfiguration, exceptionState);
312    if (exceptionState.hadException())
313        return;
314
315    blink::WebMediaConstraints constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState);
316    if (exceptionState.hadException())
317        return;
318
319    bool valid = m_peerHandler->updateICE(configuration.release(), constraints);
320    if (!valid)
321        exceptionState.throwDOMException(SyntaxError, "Could not update the ICE Agent with the given configuration.");
322}
323
324void RTCPeerConnection::addIceCandidate(RTCIceCandidate* iceCandidate, ExceptionState& exceptionState)
325{
326    if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState))
327        return;
328
329    if (!iceCandidate) {
330        exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "RTCIceCandidate"));
331        return;
332    }
333
334    bool valid = m_peerHandler->addICECandidate(iceCandidate->webCandidate());
335    if (!valid)
336        exceptionState.throwDOMException(SyntaxError, "The ICE candidate could not be added.");
337}
338
339void RTCPeerConnection::addIceCandidate(RTCIceCandidate* iceCandidate, PassOwnPtr<VoidCallback> successCallback, PassOwnPtr<RTCErrorCallback> errorCallback, ExceptionState& exceptionState)
340{
341    if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState))
342        return;
343
344    if (!iceCandidate) {
345        exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "RTCIceCandidate"));
346        return;
347    }
348    ASSERT(successCallback);
349    ASSERT(errorCallback);
350
351    RefPtr<RTCVoidRequest> request = RTCVoidRequestImpl::create(executionContext(), this, successCallback, errorCallback);
352
353    bool implemented = m_peerHandler->addICECandidate(request.release(), iceCandidate->webCandidate());
354    if (!implemented) {
355        exceptionState.throwDOMException(NotSupportedError, "This method is not yet implemented.");
356    }
357}
358
359String RTCPeerConnection::signalingState() const
360{
361    switch (m_signalingState) {
362    case SignalingStateStable:
363        return "stable";
364    case SignalingStateHaveLocalOffer:
365        return "have-local-offer";
366    case SignalingStateHaveRemoteOffer:
367        return "have-remote-offer";
368    case SignalingStateHaveLocalPrAnswer:
369        return "have-local-pranswer";
370    case SignalingStateHaveRemotePrAnswer:
371        return "have-remote-pranswer";
372    case SignalingStateClosed:
373        return "closed";
374    }
375
376    ASSERT_NOT_REACHED();
377    return String();
378}
379
380String RTCPeerConnection::iceGatheringState() const
381{
382    switch (m_iceGatheringState) {
383    case ICEGatheringStateNew:
384        return "new";
385    case ICEGatheringStateGathering:
386        return "gathering";
387    case ICEGatheringStateComplete:
388        return "complete";
389    }
390
391    ASSERT_NOT_REACHED();
392    return String();
393}
394
395String RTCPeerConnection::iceConnectionState() const
396{
397    switch (m_iceConnectionState) {
398    case ICEConnectionStateNew:
399        return "new";
400    case ICEConnectionStateChecking:
401        return "checking";
402    case ICEConnectionStateConnected:
403        return "connected";
404    case ICEConnectionStateCompleted:
405        return "completed";
406    case ICEConnectionStateFailed:
407        return "failed";
408    case ICEConnectionStateDisconnected:
409        return "disconnected";
410    case ICEConnectionStateClosed:
411        return "closed";
412    }
413
414    ASSERT_NOT_REACHED();
415    return String();
416}
417
418void RTCPeerConnection::addStream(PassRefPtrWillBeRawPtr<MediaStream> prpStream, const Dictionary& mediaConstraints, ExceptionState& exceptionState)
419{
420    if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState))
421        return;
422
423    RefPtrWillBeRawPtr<MediaStream> stream = prpStream;
424    if (!stream) {
425        exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "MediaStream"));
426        return;
427    }
428
429    if (m_localStreams.contains(stream))
430        return;
431
432    blink::WebMediaConstraints constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState);
433    if (exceptionState.hadException())
434        return;
435
436    m_localStreams.append(stream);
437
438    bool valid = m_peerHandler->addStream(stream->descriptor(), constraints);
439    if (!valid)
440        exceptionState.throwDOMException(SyntaxError, "Unable to add the provided stream.");
441}
442
443void RTCPeerConnection::removeStream(PassRefPtrWillBeRawPtr<MediaStream> prpStream, ExceptionState& exceptionState)
444{
445    if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState))
446        return;
447
448    if (!prpStream) {
449        exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "MediaStream"));
450        return;
451    }
452
453    RefPtrWillBeRawPtr<MediaStream> stream = prpStream;
454
455    size_t pos = m_localStreams.find(stream);
456    if (pos == kNotFound)
457        return;
458
459    m_localStreams.remove(pos);
460
461    m_peerHandler->removeStream(stream->descriptor());
462}
463
464MediaStreamVector RTCPeerConnection::getLocalStreams() const
465{
466    return m_localStreams;
467}
468
469MediaStreamVector RTCPeerConnection::getRemoteStreams() const
470{
471    return m_remoteStreams;
472}
473
474MediaStream* RTCPeerConnection::getStreamById(const String& streamId)
475{
476    for (MediaStreamVector::iterator iter = m_localStreams.begin(); iter != m_localStreams.end(); ++iter) {
477        if ((*iter)->id() == streamId)
478            return iter->get();
479    }
480
481    for (MediaStreamVector::iterator iter = m_remoteStreams.begin(); iter != m_remoteStreams.end(); ++iter) {
482        if ((*iter)->id() == streamId)
483            return iter->get();
484    }
485
486    return 0;
487}
488
489void RTCPeerConnection::getStats(PassOwnPtr<RTCStatsCallback> successCallback, PassRefPtr<MediaStreamTrack> selector)
490{
491    RefPtr<RTCStatsRequest> statsRequest = RTCStatsRequestImpl::create(executionContext(), this, successCallback, selector);
492    // FIXME: Add passing selector as part of the statsRequest.
493    m_peerHandler->getStats(statsRequest.release());
494}
495
496PassRefPtrWillBeRawPtr<RTCDataChannel> RTCPeerConnection::createDataChannel(String label, const Dictionary& options, ExceptionState& exceptionState)
497{
498    if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState))
499        return nullptr;
500
501    blink::WebRTCDataChannelInit init;
502    options.get("ordered", init.ordered);
503    options.get("negotiated", init.negotiated);
504
505    unsigned short value = 0;
506    if (options.get("id", value))
507        init.id = value;
508    if (options.get("maxRetransmits", value))
509        init.maxRetransmits = value;
510    if (options.get("maxRetransmitTime", value))
511        init.maxRetransmitTime = value;
512
513    String protocolString;
514    options.get("protocol", protocolString);
515    init.protocol = protocolString;
516
517    RefPtrWillBeRawPtr<RTCDataChannel> channel = RTCDataChannel::create(executionContext(), this, m_peerHandler.get(), label, init, exceptionState);
518    if (exceptionState.hadException())
519        return nullptr;
520    m_dataChannels.append(channel);
521    return channel.release();
522}
523
524bool RTCPeerConnection::hasLocalStreamWithTrackId(const String& trackId)
525{
526    for (MediaStreamVector::iterator iter = m_localStreams.begin(); iter != m_localStreams.end(); ++iter) {
527        if ((*iter)->getTrackById(trackId))
528            return true;
529    }
530    return false;
531}
532
533PassRefPtrWillBeRawPtr<RTCDTMFSender> RTCPeerConnection::createDTMFSender(PassRefPtrWillBeRawPtr<MediaStreamTrack> prpTrack, ExceptionState& exceptionState)
534{
535    if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState))
536        return nullptr;
537
538    if (!prpTrack) {
539        exceptionState.throwTypeError(ExceptionMessages::argumentNullOrIncorrectType(1, "MediaStreamTrack"));
540        return nullptr;
541    }
542
543    RefPtrWillBeRawPtr<MediaStreamTrack> track = prpTrack;
544
545    if (!hasLocalStreamWithTrackId(track->id())) {
546        exceptionState.throwDOMException(SyntaxError, "No local stream is available for the track provided.");
547        return nullptr;
548    }
549
550    RefPtrWillBeRawPtr<RTCDTMFSender> dtmfSender = RTCDTMFSender::create(executionContext(), m_peerHandler.get(), track.release(), exceptionState);
551    if (exceptionState.hadException())
552        return nullptr;
553    return dtmfSender.release();
554}
555
556void RTCPeerConnection::close(ExceptionState& exceptionState)
557{
558    if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState))
559        return;
560
561    m_peerHandler->stop();
562    m_closed = true;
563
564    changeIceConnectionState(ICEConnectionStateClosed);
565    changeIceGatheringState(ICEGatheringStateComplete);
566    changeSignalingState(SignalingStateClosed);
567}
568
569void RTCPeerConnection::negotiationNeeded()
570{
571    ASSERT(!m_closed);
572    scheduleDispatchEvent(Event::create(EventTypeNames::negotiationneeded));
573}
574
575void RTCPeerConnection::didGenerateICECandidate(const blink::WebRTCICECandidate& webCandidate)
576{
577    ASSERT(!m_closed);
578    ASSERT(executionContext()->isContextThread());
579    if (webCandidate.isNull())
580        scheduleDispatchEvent(RTCIceCandidateEvent::create(false, false, nullptr));
581    else {
582        RefPtrWillBeRawPtr<RTCIceCandidate> iceCandidate = RTCIceCandidate::create(webCandidate);
583        scheduleDispatchEvent(RTCIceCandidateEvent::create(false, false, iceCandidate.release()));
584    }
585}
586
587void RTCPeerConnection::didChangeSignalingState(SignalingState newState)
588{
589    ASSERT(!m_closed);
590    ASSERT(executionContext()->isContextThread());
591    changeSignalingState(newState);
592}
593
594void RTCPeerConnection::didChangeICEGatheringState(ICEGatheringState newState)
595{
596    ASSERT(!m_closed);
597    ASSERT(executionContext()->isContextThread());
598    changeIceGatheringState(newState);
599}
600
601void RTCPeerConnection::didChangeICEConnectionState(ICEConnectionState newState)
602{
603    ASSERT(!m_closed);
604    ASSERT(executionContext()->isContextThread());
605    changeIceConnectionState(newState);
606}
607
608void RTCPeerConnection::didAddRemoteStream(const blink::WebMediaStream& remoteStream)
609{
610    ASSERT(!m_closed);
611    ASSERT(executionContext()->isContextThread());
612
613    if (m_signalingState == SignalingStateClosed)
614        return;
615
616    RefPtrWillBeRawPtr<MediaStream> stream = MediaStream::create(executionContext(), remoteStream);
617    m_remoteStreams.append(stream);
618
619    scheduleDispatchEvent(MediaStreamEvent::create(EventTypeNames::addstream, false, false, stream.release()));
620}
621
622void RTCPeerConnection::didRemoveRemoteStream(const blink::WebMediaStream& remoteStream)
623{
624    ASSERT(!m_closed);
625    ASSERT(executionContext()->isContextThread());
626
627    MediaStreamDescriptor* streamDescriptor = remoteStream;
628    ASSERT(streamDescriptor->client());
629
630    RefPtrWillBeRawPtr<MediaStream> stream = static_cast<MediaStream*>(streamDescriptor->client());
631    stream->streamEnded();
632
633    if (m_signalingState == SignalingStateClosed)
634        return;
635
636    size_t pos = m_remoteStreams.find(stream);
637    ASSERT(pos != kNotFound);
638    m_remoteStreams.remove(pos);
639
640    scheduleDispatchEvent(MediaStreamEvent::create(EventTypeNames::removestream, false, false, stream.release()));
641}
642
643void RTCPeerConnection::didAddRemoteDataChannel(blink::WebRTCDataChannelHandler* handler)
644{
645    ASSERT(!m_closed);
646    ASSERT(executionContext()->isContextThread());
647
648    if (m_signalingState == SignalingStateClosed)
649        return;
650
651    RefPtrWillBeRawPtr<RTCDataChannel> channel = RTCDataChannel::create(executionContext(), this, adoptPtr(handler));
652    m_dataChannels.append(channel);
653
654    scheduleDispatchEvent(RTCDataChannelEvent::create(EventTypeNames::datachannel, false, false, channel.release()));
655}
656
657void RTCPeerConnection::releasePeerConnectionHandler()
658{
659    stop();
660}
661
662const AtomicString& RTCPeerConnection::interfaceName() const
663{
664    return EventTargetNames::RTCPeerConnection;
665}
666
667ExecutionContext* RTCPeerConnection::executionContext() const
668{
669    return ActiveDOMObject::executionContext();
670}
671
672void RTCPeerConnection::suspend()
673{
674    m_dispatchScheduledEventRunner.suspend();
675}
676
677void RTCPeerConnection::resume()
678{
679    m_dispatchScheduledEventRunner.resume();
680}
681
682void RTCPeerConnection::stop()
683{
684    if (m_stopped)
685        return;
686
687    m_stopped = true;
688    m_iceConnectionState = ICEConnectionStateClosed;
689    m_signalingState = SignalingStateClosed;
690
691    WillBeHeapVector<RefPtrWillBeMember<RTCDataChannel> >::iterator i = m_dataChannels.begin();
692    for (; i != m_dataChannels.end(); ++i)
693        (*i)->stop();
694    m_dataChannels.clear();
695
696    m_dispatchScheduledEventRunner.stop();
697
698    m_peerHandler.clear();
699}
700
701void RTCPeerConnection::changeSignalingState(SignalingState signalingState)
702{
703    if (m_signalingState != SignalingStateClosed && m_signalingState != signalingState) {
704        m_signalingState = signalingState;
705        scheduleDispatchEvent(Event::create(EventTypeNames::signalingstatechange));
706    }
707}
708
709void RTCPeerConnection::changeIceGatheringState(ICEGatheringState iceGatheringState)
710{
711    m_iceGatheringState = iceGatheringState;
712}
713
714void RTCPeerConnection::changeIceConnectionState(ICEConnectionState iceConnectionState)
715{
716    if (m_iceConnectionState != ICEConnectionStateClosed && m_iceConnectionState != iceConnectionState) {
717        m_iceConnectionState = iceConnectionState;
718        scheduleDispatchEvent(Event::create(EventTypeNames::iceconnectionstatechange));
719    }
720}
721
722void RTCPeerConnection::scheduleDispatchEvent(PassRefPtrWillBeRawPtr<Event> event)
723{
724    m_scheduledEvents.append(event);
725
726    m_dispatchScheduledEventRunner.runAsync();
727}
728
729void RTCPeerConnection::dispatchScheduledEvent()
730{
731    if (m_stopped)
732        return;
733
734    WillBeHeapVector<RefPtrWillBeMember<Event> > events;
735    events.swap(m_scheduledEvents);
736
737    WillBeHeapVector<RefPtrWillBeMember<Event> >::iterator it = events.begin();
738    for (; it != events.end(); ++it)
739        dispatchEvent((*it).release());
740
741    events.clear();
742}
743
744void RTCPeerConnection::trace(Visitor* visitor)
745{
746    visitor->trace(m_localStreams);
747    visitor->trace(m_remoteStreams);
748    visitor->trace(m_dataChannels);
749    visitor->trace(m_scheduledEvents);
750    EventTargetWithInlineData::trace(visitor);
751}
752
753} // namespace WebCore
754