/* * Copyright (C) 2012 Google Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer * in the documentation and/or other materials provided with the * distribution. * 3. Neither the name of Google Inc. nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "config.h" #include "modules/mediastream/RTCPeerConnection.h" #include "bindings/core/v8/ArrayValue.h" #include "bindings/core/v8/ExceptionMessages.h" #include "bindings/core/v8/ExceptionState.h" #include "core/dom/Document.h" #include "core/dom/ExceptionCode.h" #include "core/dom/ExecutionContext.h" #include "core/frame/LocalFrame.h" #include "core/html/VoidCallback.h" #include "core/loader/FrameLoader.h" #include "core/loader/FrameLoaderClient.h" #include "modules/mediastream/MediaConstraintsImpl.h" #include "modules/mediastream/MediaStreamEvent.h" #include "modules/mediastream/RTCDTMFSender.h" #include "modules/mediastream/RTCDataChannel.h" #include "modules/mediastream/RTCDataChannelEvent.h" #include "modules/mediastream/RTCErrorCallback.h" #include "modules/mediastream/RTCIceCandidateEvent.h" #include "modules/mediastream/RTCSessionDescription.h" #include "modules/mediastream/RTCSessionDescriptionCallback.h" #include "modules/mediastream/RTCSessionDescriptionRequestImpl.h" #include "modules/mediastream/RTCStatsCallback.h" #include "modules/mediastream/RTCStatsRequestImpl.h" #include "modules/mediastream/RTCVoidRequestImpl.h" #include "platform/mediastream/RTCConfiguration.h" #include "platform/mediastream/RTCOfferOptions.h" #include "public/platform/Platform.h" #include "public/platform/WebMediaStream.h" #include "public/platform/WebRTCConfiguration.h" #include "public/platform/WebRTCDataChannelHandler.h" #include "public/platform/WebRTCDataChannelInit.h" #include "public/platform/WebRTCICECandidate.h" #include "public/platform/WebRTCOfferOptions.h" #include "public/platform/WebRTCSessionDescription.h" #include "public/platform/WebRTCSessionDescriptionRequest.h" #include "public/platform/WebRTCStatsRequest.h" #include "public/platform/WebRTCVoidRequest.h" namespace blink { namespace { static bool throwExceptionIfSignalingStateClosed(RTCPeerConnection::SignalingState state, ExceptionState& exceptionState) { if (state == RTCPeerConnection::SignalingStateClosed) { exceptionState.throwDOMException(InvalidStateError, "The RTCPeerConnection's signalingState is 'closed'."); return true; } return false; } } // namespace RTCConfiguration* RTCPeerConnection::parseConfiguration(const Dictionary& configuration, ExceptionState& exceptionState) { if (configuration.isUndefinedOrNull()) return 0; RTCIceTransports iceTransports = RTCIceTransportsAll; String iceTransportsString; if (DictionaryHelper::get(configuration, "iceTransports", iceTransportsString)) { if (iceTransportsString == "none") { iceTransports = RTCIceTransportsNone; } else if (iceTransportsString == "relay") { iceTransports = RTCIceTransportsRelay; } else if (iceTransportsString != "all") { exceptionState.throwTypeError("Malformed RTCIceTransports"); return 0; } } ArrayValue iceServers; bool ok = DictionaryHelper::get(configuration, "iceServers", iceServers); if (!ok || iceServers.isUndefinedOrNull()) { exceptionState.throwTypeError("Malformed RTCConfiguration"); return 0; } size_t numberOfServers; ok = iceServers.length(numberOfServers); if (!ok) { exceptionState.throwTypeError("Malformed RTCConfiguration"); return 0; } RTCConfiguration* rtcConfiguration = RTCConfiguration::create(); rtcConfiguration->setIceTransports(iceTransports); for (size_t i = 0; i < numberOfServers; ++i) { Dictionary iceServer; ok = iceServers.get(i, iceServer); if (!ok) { exceptionState.throwTypeError("Malformed RTCIceServer"); return 0; } Vector names; iceServer.getOwnPropertyNames(names); Vector urlStrings; if (names.contains("urls")) { if (!DictionaryHelper::get(iceServer, "urls", urlStrings) || !urlStrings.size()) { String urlString; if (DictionaryHelper::get(iceServer, "urls", urlString)) { urlStrings.append(urlString); } else { exceptionState.throwTypeError("Malformed RTCIceServer"); return 0; } } } else if (names.contains("url")) { String urlString; if (DictionaryHelper::get(iceServer, "url", urlString)) { urlStrings.append(urlString); } else { exceptionState.throwTypeError("Malformed RTCIceServer"); return 0; } } else { exceptionState.throwTypeError("Malformed RTCIceServer"); return 0; } String username, credential; DictionaryHelper::get(iceServer, "username", username); DictionaryHelper::get(iceServer, "credential", credential); for (Vector::iterator iter = urlStrings.begin(); iter != urlStrings.end(); ++iter) { KURL url(KURL(), *iter); if (!url.isValid() || !(url.protocolIs("turn") || url.protocolIs("turns") || url.protocolIs("stun"))) { exceptionState.throwTypeError("Malformed URL"); return 0; } rtcConfiguration->appendServer(RTCIceServer::create(url, username, credential)); } } return rtcConfiguration; } RTCOfferOptions* RTCPeerConnection::parseOfferOptions(const Dictionary& options, ExceptionState& exceptionState) { if (options.isUndefinedOrNull()) return 0; Vector propertyNames; options.getOwnPropertyNames(propertyNames); // Treat |options| as MediaConstraints if it is empty or has "optional" or "mandatory" properties for compatibility. // TODO(jiayl): remove constraints when RTCOfferOptions reaches Stable and client code is ready. if (propertyNames.isEmpty() || propertyNames.contains("optional") || propertyNames.contains("mandatory")) return 0; int32_t offerToReceiveVideo = -1; int32_t offerToReceiveAudio = -1; bool voiceActivityDetection = true; bool iceRestart = false; if (DictionaryHelper::get(options, "offerToReceiveVideo", offerToReceiveVideo) && offerToReceiveVideo < 0) { exceptionState.throwTypeError("Invalid offerToReceiveVideo"); return 0; } if (DictionaryHelper::get(options, "offerToReceiveAudio", offerToReceiveAudio) && offerToReceiveAudio < 0) { exceptionState.throwTypeError("Invalid offerToReceiveAudio"); return 0; } DictionaryHelper::get(options, "voiceActivityDetection", voiceActivityDetection); DictionaryHelper::get(options, "iceRestart", iceRestart); RTCOfferOptions* rtcOfferOptions = RTCOfferOptions::create(offerToReceiveVideo, offerToReceiveAudio, voiceActivityDetection, iceRestart); return rtcOfferOptions; } RTCPeerConnection* RTCPeerConnection::create(ExecutionContext* context, const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionState& exceptionState) { RTCConfiguration* configuration = parseConfiguration(rtcConfiguration, exceptionState); if (exceptionState.hadException()) return 0; WebMediaConstraints constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState); if (exceptionState.hadException()) return 0; RTCPeerConnection* peerConnection = new RTCPeerConnection(context, configuration, constraints, exceptionState); peerConnection->suspendIfNeeded(); if (exceptionState.hadException()) return 0; return peerConnection; } RTCPeerConnection::RTCPeerConnection(ExecutionContext* context, RTCConfiguration* configuration, WebMediaConstraints constraints, ExceptionState& exceptionState) : ActiveDOMObject(context) , m_signalingState(SignalingStateStable) , m_iceGatheringState(ICEGatheringStateNew) , m_iceConnectionState(ICEConnectionStateNew) , m_dispatchScheduledEventRunner(this, &RTCPeerConnection::dispatchScheduledEvent) , m_stopped(false) , m_closed(false) { Document* document = toDocument(executionContext()); // If we fail, set |m_closed| and |m_stopped| to true, to avoid hitting the assert in the destructor. if (!document->frame()) { m_closed = true; m_stopped = true; exceptionState.throwDOMException(NotSupportedError, "PeerConnections may not be created in detached documents."); return; } m_peerHandler = adoptPtr(Platform::current()->createRTCPeerConnectionHandler(this)); if (!m_peerHandler) { m_closed = true; m_stopped = true; exceptionState.throwDOMException(NotSupportedError, "No PeerConnection handler can be created, perhaps WebRTC is disabled?"); return; } document->frame()->loader().client()->dispatchWillStartUsingPeerConnectionHandler(m_peerHandler.get()); if (!m_peerHandler->initialize(configuration, constraints)) { m_closed = true; m_stopped = true; exceptionState.throwDOMException(NotSupportedError, "Failed to initialize native PeerConnection."); return; } } RTCPeerConnection::~RTCPeerConnection() { // This checks that close() or stop() is called before the destructor. // We are assuming that a wrapper is always created when RTCPeerConnection is created. ASSERT(m_closed || m_stopped); } void RTCPeerConnection::createOffer(RTCSessionDescriptionCallback* successCallback, RTCErrorCallback* errorCallback, const Dictionary& rtcOfferOptions, ExceptionState& exceptionState) { if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) return; ASSERT(successCallback); RTCOfferOptions* offerOptions = parseOfferOptions(rtcOfferOptions, exceptionState); if (exceptionState.hadException()) return; RTCSessionDescriptionRequest* request = RTCSessionDescriptionRequestImpl::create(executionContext(), this, successCallback, errorCallback); if (offerOptions) { m_peerHandler->createOffer(request, offerOptions); } else { WebMediaConstraints constraints = MediaConstraintsImpl::create(rtcOfferOptions, exceptionState); if (exceptionState.hadException()) return; m_peerHandler->createOffer(request, constraints); } } void RTCPeerConnection::createAnswer(RTCSessionDescriptionCallback* successCallback, RTCErrorCallback* errorCallback, const Dictionary& mediaConstraints, ExceptionState& exceptionState) { if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) return; ASSERT(successCallback); WebMediaConstraints constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState); if (exceptionState.hadException()) return; RTCSessionDescriptionRequest* request = RTCSessionDescriptionRequestImpl::create(executionContext(), this, successCallback, errorCallback); m_peerHandler->createAnswer(request, constraints); } void RTCPeerConnection::setLocalDescription(RTCSessionDescription* sessionDescription, VoidCallback* successCallback, RTCErrorCallback* errorCallback, ExceptionState& exceptionState) { if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) return; if (!sessionDescription) { exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "RTCSessionDescription")); return; } RTCVoidRequest* request = RTCVoidRequestImpl::create(executionContext(), this, successCallback, errorCallback); m_peerHandler->setLocalDescription(request, sessionDescription->webSessionDescription()); } RTCSessionDescription* RTCPeerConnection::localDescription(ExceptionState& exceptionState) { WebRTCSessionDescription webSessionDescription = m_peerHandler->localDescription(); if (webSessionDescription.isNull()) return nullptr; return RTCSessionDescription::create(webSessionDescription); } void RTCPeerConnection::setRemoteDescription(RTCSessionDescription* sessionDescription, VoidCallback* successCallback, RTCErrorCallback* errorCallback, ExceptionState& exceptionState) { if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) return; if (!sessionDescription) { exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "RTCSessionDescription")); return; } RTCVoidRequest* request = RTCVoidRequestImpl::create(executionContext(), this, successCallback, errorCallback); m_peerHandler->setRemoteDescription(request, sessionDescription->webSessionDescription()); } RTCSessionDescription* RTCPeerConnection::remoteDescription(ExceptionState& exceptionState) { WebRTCSessionDescription webSessionDescription = m_peerHandler->remoteDescription(); if (webSessionDescription.isNull()) return nullptr; return RTCSessionDescription::create(webSessionDescription); } void RTCPeerConnection::updateIce(const Dictionary& rtcConfiguration, const Dictionary& mediaConstraints, ExceptionState& exceptionState) { if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) return; RTCConfiguration* configuration = parseConfiguration(rtcConfiguration, exceptionState); if (exceptionState.hadException()) return; WebMediaConstraints constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState); if (exceptionState.hadException()) return; bool valid = m_peerHandler->updateICE(configuration, constraints); if (!valid) exceptionState.throwDOMException(SyntaxError, "Could not update the ICE Agent with the given configuration."); } void RTCPeerConnection::addIceCandidate(RTCIceCandidate* iceCandidate, ExceptionState& exceptionState) { if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) return; if (!iceCandidate) { exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "RTCIceCandidate")); return; } bool valid = m_peerHandler->addICECandidate(iceCandidate->webCandidate()); if (!valid) exceptionState.throwDOMException(SyntaxError, "The ICE candidate could not be added."); } void RTCPeerConnection::addIceCandidate(RTCIceCandidate* iceCandidate, VoidCallback* successCallback, RTCErrorCallback* errorCallback, ExceptionState& exceptionState) { if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) return; if (!iceCandidate) { exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "RTCIceCandidate")); return; } ASSERT(successCallback); ASSERT(errorCallback); RTCVoidRequest* request = RTCVoidRequestImpl::create(executionContext(), this, successCallback, errorCallback); bool implemented = m_peerHandler->addICECandidate(request, iceCandidate->webCandidate()); if (!implemented) { exceptionState.throwDOMException(NotSupportedError, "This method is not yet implemented."); } } String RTCPeerConnection::signalingState() const { switch (m_signalingState) { case SignalingStateStable: return "stable"; case SignalingStateHaveLocalOffer: return "have-local-offer"; case SignalingStateHaveRemoteOffer: return "have-remote-offer"; case SignalingStateHaveLocalPrAnswer: return "have-local-pranswer"; case SignalingStateHaveRemotePrAnswer: return "have-remote-pranswer"; case SignalingStateClosed: return "closed"; } ASSERT_NOT_REACHED(); return String(); } String RTCPeerConnection::iceGatheringState() const { switch (m_iceGatheringState) { case ICEGatheringStateNew: return "new"; case ICEGatheringStateGathering: return "gathering"; case ICEGatheringStateComplete: return "complete"; } ASSERT_NOT_REACHED(); return String(); } String RTCPeerConnection::iceConnectionState() const { switch (m_iceConnectionState) { case ICEConnectionStateNew: return "new"; case ICEConnectionStateChecking: return "checking"; case ICEConnectionStateConnected: return "connected"; case ICEConnectionStateCompleted: return "completed"; case ICEConnectionStateFailed: return "failed"; case ICEConnectionStateDisconnected: return "disconnected"; case ICEConnectionStateClosed: return "closed"; } ASSERT_NOT_REACHED(); return String(); } void RTCPeerConnection::addStream(MediaStream* stream, const Dictionary& mediaConstraints, ExceptionState& exceptionState) { if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) return; if (!stream) { exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "MediaStream")); return; } if (m_localStreams.contains(stream)) return; WebMediaConstraints constraints = MediaConstraintsImpl::create(mediaConstraints, exceptionState); if (exceptionState.hadException()) return; m_localStreams.append(stream); bool valid = m_peerHandler->addStream(stream->descriptor(), constraints); if (!valid) exceptionState.throwDOMException(SyntaxError, "Unable to add the provided stream."); } void RTCPeerConnection::removeStream(MediaStream* stream, ExceptionState& exceptionState) { if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) return; if (!stream) { exceptionState.throwDOMException(TypeMismatchError, ExceptionMessages::argumentNullOrIncorrectType(1, "MediaStream")); return; } size_t pos = m_localStreams.find(stream); if (pos == kNotFound) return; m_localStreams.remove(pos); m_peerHandler->removeStream(stream->descriptor()); } MediaStreamVector RTCPeerConnection::getLocalStreams() const { return m_localStreams; } MediaStreamVector RTCPeerConnection::getRemoteStreams() const { return m_remoteStreams; } MediaStream* RTCPeerConnection::getStreamById(const String& streamId) { for (MediaStreamVector::iterator iter = m_localStreams.begin(); iter != m_localStreams.end(); ++iter) { if ((*iter)->id() == streamId) return iter->get(); } for (MediaStreamVector::iterator iter = m_remoteStreams.begin(); iter != m_remoteStreams.end(); ++iter) { if ((*iter)->id() == streamId) return iter->get(); } return 0; } void RTCPeerConnection::getStats(RTCStatsCallback* successCallback, MediaStreamTrack* selector) { RTCStatsRequest* statsRequest = RTCStatsRequestImpl::create(executionContext(), this, successCallback, selector); // FIXME: Add passing selector as part of the statsRequest. m_peerHandler->getStats(statsRequest); } RTCDataChannel* RTCPeerConnection::createDataChannel(String label, const Dictionary& options, ExceptionState& exceptionState) { if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) return nullptr; WebRTCDataChannelInit init; DictionaryHelper::get(options, "ordered", init.ordered); DictionaryHelper::get(options, "negotiated", init.negotiated); unsigned short value = 0; if (DictionaryHelper::get(options, "id", value)) init.id = value; if (DictionaryHelper::get(options, "maxRetransmits", value)) init.maxRetransmits = value; if (DictionaryHelper::get(options, "maxRetransmitTime", value)) init.maxRetransmitTime = value; String protocolString; DictionaryHelper::get(options, "protocol", protocolString); init.protocol = protocolString; RTCDataChannel* channel = RTCDataChannel::create(executionContext(), this, m_peerHandler.get(), label, init, exceptionState); if (exceptionState.hadException()) return nullptr; m_dataChannels.append(channel); return channel; } bool RTCPeerConnection::hasLocalStreamWithTrackId(const String& trackId) { for (MediaStreamVector::iterator iter = m_localStreams.begin(); iter != m_localStreams.end(); ++iter) { if ((*iter)->getTrackById(trackId)) return true; } return false; } RTCDTMFSender* RTCPeerConnection::createDTMFSender(MediaStreamTrack* track, ExceptionState& exceptionState) { if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) return nullptr; if (!track) { exceptionState.throwTypeError(ExceptionMessages::argumentNullOrIncorrectType(1, "MediaStreamTrack")); return nullptr; } if (!hasLocalStreamWithTrackId(track->id())) { exceptionState.throwDOMException(SyntaxError, "No local stream is available for the track provided."); return nullptr; } RTCDTMFSender* dtmfSender = RTCDTMFSender::create(executionContext(), m_peerHandler.get(), track, exceptionState); if (exceptionState.hadException()) return nullptr; return dtmfSender; } void RTCPeerConnection::close(ExceptionState& exceptionState) { if (throwExceptionIfSignalingStateClosed(m_signalingState, exceptionState)) return; closeInternal(); } void RTCPeerConnection::negotiationNeeded() { ASSERT(!m_closed); scheduleDispatchEvent(Event::create(EventTypeNames::negotiationneeded)); } void RTCPeerConnection::didGenerateICECandidate(const WebRTCICECandidate& webCandidate) { ASSERT(!m_closed); ASSERT(executionContext()->isContextThread()); if (webCandidate.isNull()) scheduleDispatchEvent(RTCIceCandidateEvent::create(false, false, nullptr)); else { RTCIceCandidate* iceCandidate = RTCIceCandidate::create(webCandidate); scheduleDispatchEvent(RTCIceCandidateEvent::create(false, false, iceCandidate)); } } void RTCPeerConnection::didChangeSignalingState(SignalingState newState) { ASSERT(!m_closed); ASSERT(executionContext()->isContextThread()); changeSignalingState(newState); } void RTCPeerConnection::didChangeICEGatheringState(ICEGatheringState newState) { ASSERT(!m_closed); ASSERT(executionContext()->isContextThread()); changeIceGatheringState(newState); } void RTCPeerConnection::didChangeICEConnectionState(ICEConnectionState newState) { ASSERT(!m_closed); ASSERT(executionContext()->isContextThread()); changeIceConnectionState(newState); } void RTCPeerConnection::didAddRemoteStream(const WebMediaStream& remoteStream) { ASSERT(!m_closed); ASSERT(executionContext()->isContextThread()); if (m_signalingState == SignalingStateClosed) return; MediaStream* stream = MediaStream::create(executionContext(), remoteStream); m_remoteStreams.append(stream); scheduleDispatchEvent(MediaStreamEvent::create(EventTypeNames::addstream, false, false, stream)); } void RTCPeerConnection::didRemoveRemoteStream(const WebMediaStream& remoteStream) { ASSERT(!m_closed); ASSERT(executionContext()->isContextThread()); MediaStreamDescriptor* streamDescriptor = remoteStream; ASSERT(streamDescriptor->client()); MediaStream* stream = static_cast(streamDescriptor->client()); stream->streamEnded(); if (m_signalingState == SignalingStateClosed) return; size_t pos = m_remoteStreams.find(stream); ASSERT(pos != kNotFound); m_remoteStreams.remove(pos); scheduleDispatchEvent(MediaStreamEvent::create(EventTypeNames::removestream, false, false, stream)); } void RTCPeerConnection::didAddRemoteDataChannel(WebRTCDataChannelHandler* handler) { ASSERT(!m_closed); ASSERT(executionContext()->isContextThread()); if (m_signalingState == SignalingStateClosed) return; RTCDataChannel* channel = RTCDataChannel::create(executionContext(), this, adoptPtr(handler)); m_dataChannels.append(channel); scheduleDispatchEvent(RTCDataChannelEvent::create(EventTypeNames::datachannel, false, false, channel)); } void RTCPeerConnection::releasePeerConnectionHandler() { stop(); } void RTCPeerConnection::closePeerConnection() { ASSERT(m_signalingState != RTCPeerConnection::SignalingStateClosed); closeInternal(); } const AtomicString& RTCPeerConnection::interfaceName() const { return EventTargetNames::RTCPeerConnection; } ExecutionContext* RTCPeerConnection::executionContext() const { return ActiveDOMObject::executionContext(); } void RTCPeerConnection::suspend() { m_dispatchScheduledEventRunner.suspend(); } void RTCPeerConnection::resume() { m_dispatchScheduledEventRunner.resume(); } void RTCPeerConnection::stop() { if (m_stopped) return; m_stopped = true; m_iceConnectionState = ICEConnectionStateClosed; m_signalingState = SignalingStateClosed; HeapVector >::iterator i = m_dataChannels.begin(); for (; i != m_dataChannels.end(); ++i) (*i)->stop(); m_dataChannels.clear(); m_dispatchScheduledEventRunner.stop(); m_peerHandler.clear(); } void RTCPeerConnection::changeSignalingState(SignalingState signalingState) { if (m_signalingState != SignalingStateClosed && m_signalingState != signalingState) { m_signalingState = signalingState; scheduleDispatchEvent(Event::create(EventTypeNames::signalingstatechange)); } } void RTCPeerConnection::changeIceGatheringState(ICEGatheringState iceGatheringState) { m_iceGatheringState = iceGatheringState; } void RTCPeerConnection::changeIceConnectionState(ICEConnectionState iceConnectionState) { if (m_iceConnectionState != ICEConnectionStateClosed && m_iceConnectionState != iceConnectionState) { m_iceConnectionState = iceConnectionState; scheduleDispatchEvent(Event::create(EventTypeNames::iceconnectionstatechange)); } } void RTCPeerConnection::closeInternal() { ASSERT(m_signalingState != RTCPeerConnection::SignalingStateClosed); m_peerHandler->stop(); m_closed = true; changeIceConnectionState(ICEConnectionStateClosed); changeIceGatheringState(ICEGatheringStateComplete); changeSignalingState(SignalingStateClosed); } void RTCPeerConnection::scheduleDispatchEvent(PassRefPtrWillBeRawPtr event) { m_scheduledEvents.append(event); m_dispatchScheduledEventRunner.runAsync(); } void RTCPeerConnection::dispatchScheduledEvent() { if (m_stopped) return; WillBeHeapVector > events; events.swap(m_scheduledEvents); WillBeHeapVector >::iterator it = events.begin(); for (; it != events.end(); ++it) dispatchEvent((*it).release()); events.clear(); } void RTCPeerConnection::trace(Visitor* visitor) { visitor->trace(m_localStreams); visitor->trace(m_remoteStreams); visitor->trace(m_dataChannels); #if ENABLE(OILPAN) visitor->trace(m_scheduledEvents); #endif EventTargetWithInlineData::trace(visitor); } } // namespace blink