Make some things that return never-null pointers return references instead.
[WebKit-https.git] / Source / WebCore / Modules / mediastream / RTCPeerConnection.cpp
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 #if ENABLE(MEDIA_STREAM)
34
35 #include "RTCPeerConnection.h"
36
37 #include "ArrayValue.h"
38 #include "Document.h"
39 #include "Event.h"
40 #include "ExceptionCode.h"
41 #include "Frame.h"
42 #include "FrameLoader.h"
43 #include "FrameLoaderClient.h"
44 #include "MediaConstraintsImpl.h"
45 #include "MediaStreamEvent.h"
46 #include "RTCConfiguration.h"
47 #include "RTCDTMFSender.h"
48 #include "RTCDataChannel.h"
49 #include "RTCDataChannelEvent.h"
50 #include "RTCDataChannelHandler.h"
51 #include "RTCErrorCallback.h"
52 #include "RTCIceCandidate.h"
53 #include "RTCIceCandidateDescriptor.h"
54 #include "RTCIceCandidateEvent.h"
55 #include "RTCSessionDescription.h"
56 #include "RTCSessionDescriptionCallback.h"
57 #include "RTCSessionDescriptionDescriptor.h"
58 #include "RTCSessionDescriptionRequestImpl.h"
59 #include "RTCStatsCallback.h"
60 #include "RTCStatsRequestImpl.h"
61 #include "RTCVoidRequestImpl.h"
62 #include "ScriptExecutionContext.h"
63 #include "VoidCallback.h"
64
65 namespace WebCore {
66
67 PassRefPtr<RTCConfiguration> RTCPeerConnection::parseConfiguration(const Dictionary& configuration, ExceptionCode& ec)
68 {
69     if (configuration.isUndefinedOrNull())
70         return 0;
71
72     ArrayValue iceServers;
73     bool ok = configuration.get("iceServers", iceServers);
74     if (!ok || iceServers.isUndefinedOrNull()) {
75         ec = TYPE_MISMATCH_ERR;
76         return 0;
77     }
78
79     size_t numberOfServers;
80     ok = iceServers.length(numberOfServers);
81     if (!ok) {
82         ec = TYPE_MISMATCH_ERR;
83         return 0;
84     }
85
86     RefPtr<RTCConfiguration> rtcConfiguration = RTCConfiguration::create();
87
88     for (size_t i = 0; i < numberOfServers; ++i) {
89         Dictionary iceServer;
90         ok = iceServers.get(i, iceServer);
91         if (!ok) {
92             ec = TYPE_MISMATCH_ERR;
93             return 0;
94         }
95
96         String urlString, credential;
97         ok = iceServer.get("url", urlString);
98         if (!ok) {
99             ec = TYPE_MISMATCH_ERR;
100             return 0;
101         }
102         KURL url(KURL(), urlString);
103         if (!url.isValid() || !(url.protocolIs("turn") || url.protocolIs("stun"))) {
104             ec = TYPE_MISMATCH_ERR;
105             return 0;
106         }
107
108         iceServer.get("credential", credential);
109
110         rtcConfiguration->appendServer(RTCIceServer::create(url, credential));
111     }
112
113     return rtcConfiguration.release();
114 }
115
116 PassRefPtr<RTCPeerConnection> RTCPeerConnection::create(ScriptExecutionContext* context, const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionCode& ec)
117 {
118     RefPtr<RTCConfiguration> configuration = parseConfiguration(rtcConfiguration, ec);
119     if (ec)
120         return 0;
121
122     RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, ec);
123     if (ec)
124         return 0;
125
126     RefPtr<RTCPeerConnection> peerConnection = adoptRef(new RTCPeerConnection(context, configuration.release(), constraints.release(), ec));
127     peerConnection->suspendIfNeeded();
128     if (ec)
129         return 0;
130
131     return peerConnection.release();
132 }
133
134 RTCPeerConnection::RTCPeerConnection(ScriptExecutionContext* context, PassRefPtr<RTCConfiguration> configuration, PassRefPtr<MediaConstraints> constraints, ExceptionCode& ec)
135     : ActiveDOMObject(context)
136     , m_signalingState(SignalingStateStable)
137     , m_iceGatheringState(IceGatheringStateNew)
138     , m_iceConnectionState(IceConnectionStateNew)
139     , m_scheduledEventTimer(this, &RTCPeerConnection::scheduledEventTimerFired)
140     , m_stopped(false)
141 {
142     Document* document = toDocument(m_scriptExecutionContext);
143
144     if (!document->frame()) {
145         ec = NOT_SUPPORTED_ERR;
146         return;
147     }
148
149     m_peerHandler = RTCPeerConnectionHandler::create(this);
150     if (!m_peerHandler) {
151         ec = NOT_SUPPORTED_ERR;
152         return;
153     }
154
155     document->frame()->loader()->client()->dispatchWillStartUsingPeerConnectionHandler(m_peerHandler.get());
156
157     if (!m_peerHandler->initialize(configuration, constraints)) {
158         ec = NOT_SUPPORTED_ERR;
159         return;
160     }
161 }
162
163 RTCPeerConnection::~RTCPeerConnection()
164 {
165     stop();
166 }
167
168 void RTCPeerConnection::createOffer(PassRefPtr<RTCSessionDescriptionCallback> successCallback, PassRefPtr<RTCErrorCallback> errorCallback, const Dictionary& mediaConstraints, ExceptionCode& ec)
169 {
170     if (m_signalingState == SignalingStateClosed) {
171         ec = INVALID_STATE_ERR;
172         return;
173     }
174
175     if (!successCallback) {
176         ec = TYPE_MISMATCH_ERR;
177         return;
178     }
179
180     RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, ec);
181     if (ec)
182         return;
183
184     RefPtr<RTCSessionDescriptionRequestImpl> request = RTCSessionDescriptionRequestImpl::create(scriptExecutionContext(), successCallback, errorCallback);
185     m_peerHandler->createOffer(request.release(), constraints);
186 }
187
188 void RTCPeerConnection::createAnswer(PassRefPtr<RTCSessionDescriptionCallback> successCallback, PassRefPtr<RTCErrorCallback> errorCallback, const Dictionary& mediaConstraints, ExceptionCode& ec)
189 {
190     if (m_signalingState == SignalingStateClosed) {
191         ec = INVALID_STATE_ERR;
192         return;
193     }
194
195     if (!successCallback) {
196         ec = TYPE_MISMATCH_ERR;
197         return;
198     }
199
200     RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, ec);
201     if (ec)
202         return;
203
204     RefPtr<RTCSessionDescriptionRequestImpl> request = RTCSessionDescriptionRequestImpl::create(scriptExecutionContext(), successCallback, errorCallback);
205     m_peerHandler->createAnswer(request.release(), constraints.release());
206 }
207
208 void RTCPeerConnection::setLocalDescription(PassRefPtr<RTCSessionDescription> prpSessionDescription, PassRefPtr<VoidCallback> successCallback, PassRefPtr<RTCErrorCallback> errorCallback, ExceptionCode& ec)
209 {
210     if (m_signalingState == SignalingStateClosed) {
211         ec = INVALID_STATE_ERR;
212         return;
213     }
214
215     RefPtr<RTCSessionDescription> sessionDescription = prpSessionDescription;
216     if (!sessionDescription) {
217         ec = TYPE_MISMATCH_ERR;
218         return;
219     }
220
221     RefPtr<RTCVoidRequestImpl> request = RTCVoidRequestImpl::create(scriptExecutionContext(), successCallback, errorCallback);
222     m_peerHandler->setLocalDescription(request.release(), sessionDescription->descriptor());
223 }
224
225 PassRefPtr<RTCSessionDescription> RTCPeerConnection::localDescription(ExceptionCode& ec)
226 {
227     RefPtr<RTCSessionDescriptionDescriptor> descriptor = m_peerHandler->localDescription();
228     if (!descriptor)
229         return 0;
230
231     RefPtr<RTCSessionDescription> sessionDescription = RTCSessionDescription::create(descriptor.release());
232     return sessionDescription.release();
233 }
234
235 void RTCPeerConnection::setRemoteDescription(PassRefPtr<RTCSessionDescription> prpSessionDescription, PassRefPtr<VoidCallback> successCallback, PassRefPtr<RTCErrorCallback> errorCallback, ExceptionCode& ec)
236 {
237     if (m_signalingState == SignalingStateClosed) {
238         ec = INVALID_STATE_ERR;
239         return;
240     }
241
242     RefPtr<RTCSessionDescription> sessionDescription = prpSessionDescription;
243     if (!sessionDescription) {
244         ec = TYPE_MISMATCH_ERR;
245         return;
246     }
247
248     RefPtr<RTCVoidRequestImpl> request = RTCVoidRequestImpl::create(scriptExecutionContext(), successCallback, errorCallback);
249     m_peerHandler->setRemoteDescription(request.release(), sessionDescription->descriptor());
250 }
251
252 PassRefPtr<RTCSessionDescription> RTCPeerConnection::remoteDescription(ExceptionCode& ec)
253 {
254     RefPtr<RTCSessionDescriptionDescriptor> descriptor = m_peerHandler->remoteDescription();
255     if (!descriptor)
256         return 0;
257
258     RefPtr<RTCSessionDescription> desc = RTCSessionDescription::create(descriptor.release());
259     return desc.release();
260 }
261
262 void RTCPeerConnection::updateIce(const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionCode& ec)
263 {
264     if (m_signalingState == SignalingStateClosed) {
265         ec = INVALID_STATE_ERR;
266         return;
267     }
268
269     RefPtr<RTCConfiguration> configuration = parseConfiguration(rtcConfiguration, ec);
270     if (ec)
271         return;
272
273     RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, ec);
274     if (ec)
275         return;
276
277     bool valid = m_peerHandler->updateIce(configuration, constraints);
278     if (!valid)
279         ec = SYNTAX_ERR;
280 }
281
282 void RTCPeerConnection::addIceCandidate(RTCIceCandidate* iceCandidate, ExceptionCode& ec)
283 {
284     if (m_signalingState == SignalingStateClosed) {
285         ec = INVALID_STATE_ERR;
286         return;
287     }
288
289     if (!iceCandidate) {
290         ec = TYPE_MISMATCH_ERR;
291         return;
292     }
293
294     bool valid = m_peerHandler->addIceCandidate(iceCandidate->descriptor());
295     if (!valid)
296         ec = SYNTAX_ERR;
297 }
298
299 String RTCPeerConnection::signalingState() const
300 {
301     switch (m_signalingState) {
302     case SignalingStateStable:
303         return ASCIILiteral("stable");
304     case SignalingStateHaveLocalOffer:
305         return ASCIILiteral("have-local-offer");
306     case SignalingStateHaveRemoteOffer:
307         return ASCIILiteral("have-remote-offer");
308     case SignalingStateHaveLocalPrAnswer:
309         return ASCIILiteral("have-local-pranswer");
310     case SignalingStateHaveRemotePrAnswer:
311         return ASCIILiteral("have-remote-pranswer");
312     case SignalingStateClosed:
313         return ASCIILiteral("closed");
314     }
315
316     ASSERT_NOT_REACHED();
317     return String();
318 }
319
320 String RTCPeerConnection::iceGatheringState() const
321 {
322     switch (m_iceGatheringState) {
323     case IceGatheringStateNew:
324         return ASCIILiteral("new");
325     case IceGatheringStateGathering:
326         return ASCIILiteral("gathering");
327     case IceGatheringStateComplete:
328         return ASCIILiteral("complete");
329     }
330
331     ASSERT_NOT_REACHED();
332     return String();
333 }
334
335 String RTCPeerConnection::iceConnectionState() const
336 {
337     switch (m_iceConnectionState) {
338     case IceConnectionStateNew:
339         return ASCIILiteral("new");
340     case IceConnectionStateChecking:
341         return ASCIILiteral("checking");
342     case IceConnectionStateConnected:
343         return ASCIILiteral("connected");
344     case IceConnectionStateCompleted:
345         return ASCIILiteral("completed");
346     case IceConnectionStateFailed:
347         return ASCIILiteral("failed");
348     case IceConnectionStateDisconnected:
349         return ASCIILiteral("disconnected");
350     case IceConnectionStateClosed:
351         return ASCIILiteral("closed");
352     }
353
354     ASSERT_NOT_REACHED();
355     return String();
356 }
357
358 void RTCPeerConnection::addStream(PassRefPtr<MediaStream> prpStream, const Dictionary& mediaConstraints, ExceptionCode& ec)
359 {
360     if (m_signalingState == SignalingStateClosed) {
361         ec = INVALID_STATE_ERR;
362         return;
363     }
364
365     RefPtr<MediaStream> stream = prpStream;
366     if (!stream) {
367         ec = TYPE_MISMATCH_ERR;
368         return;
369     }
370
371     if (m_localStreams.contains(stream))
372         return;
373
374     RefPtr<MediaConstraints> constraints = MediaConstraintsImpl::create(mediaConstraints, ec);
375     if (ec)
376         return;
377
378     m_localStreams.append(stream);
379
380     bool valid = m_peerHandler->addStream(stream->descriptor(), constraints);
381     if (!valid)
382         ec = SYNTAX_ERR;
383 }
384
385 void RTCPeerConnection::removeStream(PassRefPtr<MediaStream> prpStream, ExceptionCode& ec)
386 {
387     if (m_signalingState == SignalingStateClosed) {
388         ec = INVALID_STATE_ERR;
389         return;
390     }
391
392     if (!prpStream) {
393         ec = TYPE_MISMATCH_ERR;
394         return;
395     }
396
397     RefPtr<MediaStream> stream = prpStream;
398
399     size_t pos = m_localStreams.find(stream);
400     if (pos == notFound)
401         return;
402
403     m_localStreams.remove(pos);
404
405     m_peerHandler->removeStream(stream->descriptor());
406 }
407
408 MediaStreamVector RTCPeerConnection::getLocalStreams() const
409 {
410     return m_localStreams;
411 }
412
413 MediaStreamVector RTCPeerConnection::getRemoteStreams() const
414 {
415     return m_remoteStreams;
416 }
417
418 MediaStream* RTCPeerConnection::getStreamById(const String& streamId)
419 {
420     for (MediaStreamVector::iterator iter = m_localStreams.begin(); iter != m_localStreams.end(); ++iter) {
421         if ((*iter)->id() == streamId)
422             return iter->get();
423     }
424
425     for (MediaStreamVector::iterator iter = m_remoteStreams.begin(); iter != m_remoteStreams.end(); ++iter) {
426         if ((*iter)->id() == streamId)
427             return iter->get();
428     }
429
430     return 0;
431 }
432
433 void RTCPeerConnection::getStats(PassRefPtr<RTCStatsCallback> successCallback, PassRefPtr<MediaStreamTrack> selector)
434 {
435     RefPtr<RTCStatsRequestImpl> statsRequest = RTCStatsRequestImpl::create(scriptExecutionContext(), successCallback, selector);
436     // FIXME: Add passing selector as part of the statsRequest.
437     m_peerHandler->getStats(statsRequest.release());
438 }
439
440 PassRefPtr<RTCDataChannel> RTCPeerConnection::createDataChannel(String label, const Dictionary& options, ExceptionCode& ec)
441 {
442     if (m_signalingState == SignalingStateClosed) {
443         ec = INVALID_STATE_ERR;
444         return 0;
445     }
446
447     bool reliable = true;
448     options.get("reliable", reliable);
449     RefPtr<RTCDataChannel> channel = RTCDataChannel::create(scriptExecutionContext(), m_peerHandler.get(), label, reliable, ec);
450     if (ec)
451         return 0;
452     m_dataChannels.append(channel);
453     return channel.release();
454 }
455
456 bool RTCPeerConnection::hasLocalStreamWithTrackId(const String& trackId)
457 {
458     for (MediaStreamVector::iterator iter = m_localStreams.begin(); iter != m_localStreams.end(); ++iter) {
459         if ((*iter)->getTrackById(trackId))
460             return true;
461     }
462     return false;
463 }
464
465 PassRefPtr<RTCDTMFSender> RTCPeerConnection::createDTMFSender(PassRefPtr<MediaStreamTrack> prpTrack, ExceptionCode& ec)
466 {
467     if (m_signalingState == SignalingStateClosed) {
468         ec = INVALID_STATE_ERR;
469         return 0;
470     }
471
472     if (!prpTrack) {
473         ec = TypeError;
474         return 0;
475     }
476
477     RefPtr<MediaStreamTrack> track = prpTrack;
478
479     if (!hasLocalStreamWithTrackId(track->id())) {
480         ec = SYNTAX_ERR;
481         return 0;
482     }
483
484     RefPtr<RTCDTMFSender> dtmfSender = RTCDTMFSender::create(scriptExecutionContext(), m_peerHandler.get(), track.release(), ec);
485     if (ec)
486         return 0;
487     return dtmfSender.release();
488 }
489
490 void RTCPeerConnection::close(ExceptionCode& ec)
491 {
492     if (m_signalingState == SignalingStateClosed) {
493         ec = INVALID_STATE_ERR;
494         return;
495     }
496
497     m_peerHandler->stop();
498
499     changeIceConnectionState(IceConnectionStateClosed);
500     changeIceGatheringState(IceGatheringStateComplete);
501     changeSignalingState(SignalingStateClosed);
502 }
503
504 void RTCPeerConnection::negotiationNeeded()
505 {
506     scheduleDispatchEvent(Event::create(eventNames().negotiationneededEvent, false, false));
507 }
508
509 void RTCPeerConnection::didGenerateIceCandidate(PassRefPtr<RTCIceCandidateDescriptor> iceCandidateDescriptor)
510 {
511     ASSERT(scriptExecutionContext()->isContextThread());
512     if (!iceCandidateDescriptor)
513         scheduleDispatchEvent(RTCIceCandidateEvent::create(false, false, 0));
514     else {
515         RefPtr<RTCIceCandidate> iceCandidate = RTCIceCandidate::create(iceCandidateDescriptor);
516         scheduleDispatchEvent(RTCIceCandidateEvent::create(false, false, iceCandidate.release()));
517     }
518 }
519
520 void RTCPeerConnection::didChangeSignalingState(SignalingState newState)
521 {
522     ASSERT(scriptExecutionContext()->isContextThread());
523     changeSignalingState(newState);
524 }
525
526 void RTCPeerConnection::didChangeIceGatheringState(IceGatheringState newState)
527 {
528     ASSERT(scriptExecutionContext()->isContextThread());
529     changeIceGatheringState(newState);
530 }
531
532 void RTCPeerConnection::didChangeIceConnectionState(IceConnectionState newState)
533 {
534     ASSERT(scriptExecutionContext()->isContextThread());
535     changeIceConnectionState(newState);
536 }
537
538 void RTCPeerConnection::didAddRemoteStream(PassRefPtr<MediaStreamDescriptor> streamDescriptor)
539 {
540     ASSERT(scriptExecutionContext()->isContextThread());
541
542     if (m_signalingState == SignalingStateClosed)
543         return;
544
545     RefPtr<MediaStream> stream = MediaStream::create(scriptExecutionContext(), streamDescriptor);
546     m_remoteStreams.append(stream);
547
548     scheduleDispatchEvent(MediaStreamEvent::create(eventNames().addstreamEvent, false, false, stream.release()));
549 }
550
551 void RTCPeerConnection::didRemoveRemoteStream(MediaStreamDescriptor* streamDescriptor)
552 {
553     ASSERT(scriptExecutionContext()->isContextThread());
554     ASSERT(streamDescriptor->client());
555
556     RefPtr<MediaStream> stream = static_cast<MediaStream*>(streamDescriptor->client());
557     stream->streamEnded();
558
559     if (m_signalingState == SignalingStateClosed)
560         return;
561
562     size_t pos = m_remoteStreams.find(stream);
563     ASSERT(pos != notFound);
564     m_remoteStreams.remove(pos);
565
566     scheduleDispatchEvent(MediaStreamEvent::create(eventNames().removestreamEvent, false, false, stream.release()));
567 }
568
569 void RTCPeerConnection::didAddRemoteDataChannel(PassOwnPtr<RTCDataChannelHandler> handler)
570 {
571     ASSERT(scriptExecutionContext()->isContextThread());
572
573     if (m_signalingState == SignalingStateClosed)
574         return;
575
576     RefPtr<RTCDataChannel> channel = RTCDataChannel::create(scriptExecutionContext(), handler);
577     m_dataChannels.append(channel);
578
579     scheduleDispatchEvent(RTCDataChannelEvent::create(eventNames().datachannelEvent, false, false, channel.release()));
580 }
581
582 const AtomicString& RTCPeerConnection::interfaceName() const
583 {
584     return eventNames().interfaceForRTCPeerConnection;
585 }
586
587 ScriptExecutionContext* RTCPeerConnection::scriptExecutionContext() const
588 {
589     return ActiveDOMObject::scriptExecutionContext();
590 }
591
592 void RTCPeerConnection::stop()
593 {
594     if (m_stopped)
595         return;
596
597     m_stopped = true;
598     m_iceConnectionState = IceConnectionStateClosed;
599     m_signalingState = SignalingStateClosed;
600
601     Vector<RefPtr<RTCDataChannel> >::iterator i = m_dataChannels.begin();
602     for (; i != m_dataChannels.end(); ++i)
603         (*i)->stop();
604 }
605
606 EventTargetData* RTCPeerConnection::eventTargetData()
607 {
608     return &m_eventTargetData;
609 }
610
611 EventTargetData& RTCPeerConnection::ensureEventTargetData()
612 {
613     return m_eventTargetData;
614 }
615
616 void RTCPeerConnection::changeSignalingState(SignalingState signalingState)
617 {
618     if (m_signalingState != SignalingStateClosed && m_signalingState != signalingState) {
619         m_signalingState = signalingState;
620         scheduleDispatchEvent(Event::create(eventNames().signalingstatechangeEvent, false, false));
621     }
622 }
623
624 void RTCPeerConnection::changeIceGatheringState(IceGatheringState iceGatheringState)
625 {
626     m_iceGatheringState = iceGatheringState;
627 }
628
629 void RTCPeerConnection::changeIceConnectionState(IceConnectionState iceConnectionState)
630 {
631     if (m_iceConnectionState != IceConnectionStateClosed && m_iceConnectionState != iceConnectionState) {
632         m_iceConnectionState = iceConnectionState;
633         scheduleDispatchEvent(Event::create(eventNames().iceconnectionstatechangeEvent, false, false));
634     }
635 }
636
637 void RTCPeerConnection::scheduleDispatchEvent(PassRefPtr<Event> event)
638 {
639     m_scheduledEvents.append(event);
640
641     if (!m_scheduledEventTimer.isActive())
642         m_scheduledEventTimer.startOneShot(0);
643 }
644
645 void RTCPeerConnection::scheduledEventTimerFired(Timer<RTCPeerConnection>*)
646 {
647     if (m_stopped)
648         return;
649
650     Vector<RefPtr<Event> > events;
651     events.swap(m_scheduledEvents);
652
653     Vector<RefPtr<Event> >::iterator it = events.begin();
654     for (; it != events.end(); ++it)
655         dispatchEvent((*it).release());
656
657     events.clear();
658 }
659
660 } // namespace WebCore
661
662 #endif // ENABLE(MEDIA_STREAM)