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