initial import
[vuplus_webkit] / Source / WebCore / page / MediaStreamFrameController.cpp
1 /*
2  * Copyright (C) 2011 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  * 1.  Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "config.h"
26 #include "MediaStreamFrameController.h"
27
28 #if ENABLE(MEDIA_STREAM)
29
30 #include "DOMWindow.h"
31 #include "Document.h"
32 #include "Frame.h"
33 #include "LocalMediaStream.h"
34 #include "MediaStreamController.h"
35 #include "MediaStreamTrackList.h"
36 #include "NavigatorUserMediaErrorCallback.h"
37 #include "NavigatorUserMediaSuccessCallback.h"
38 #include "Page.h"
39 #include "PeerConnection.h"
40 #include "SecurityOrigin.h"
41 #include "SignalingCallback.h"
42 #include <wtf/RefCounted.h>
43
44 namespace WebCore {
45
46 class MediaStreamFrameController::Request : public RefCounted<Request> {
47     WTF_MAKE_NONCOPYABLE(Request);
48 public:
49     virtual ~Request() { }
50
51     ScriptExecutionContext* scriptExecutionContext() const { return m_scriptExecutionContext; }
52     virtual bool isGenerateStreamRequest() const { return false; }
53     virtual bool isRecordedDataRequest() const { return false; }
54     virtual bool isSignalingRequest() const { return false; }
55
56     virtual void abort() = 0;
57
58 protected:
59     Request(ScriptExecutionContext* scriptExecutionContext)
60         : m_scriptExecutionContext(scriptExecutionContext) { }
61
62 private:
63     // This is guaranteed to have the lifetime of the Frame, and it's only used to make
64     // the callback asynchronous. The original callback context is used in the call.
65     ScriptExecutionContext* m_scriptExecutionContext;
66 };
67
68 class MediaStreamFrameController::GenerateStreamRequest : public Request {
69     WTF_MAKE_NONCOPYABLE(GenerateStreamRequest);
70 public:
71     static PassRefPtr<GenerateStreamRequest> create(ScriptExecutionContext* scriptExecutionContext,
72                                                     PassRefPtr<NavigatorUserMediaSuccessCallback> successCallback,
73                                                     PassRefPtr<NavigatorUserMediaErrorCallback> errorCallback)
74     {
75         return adoptRef(new GenerateStreamRequest(scriptExecutionContext, successCallback, errorCallback));
76     }
77
78     virtual ~GenerateStreamRequest() { }
79
80     virtual bool isGenerateStreamRequest() const { return true; }
81
82     virtual void abort()
83     {
84         if (m_errorCallback) {
85             RefPtr<NavigatorUserMediaError> error = NavigatorUserMediaError::create(NavigatorUserMediaError::PERMISSION_DENIED);
86             // The callback itself is made with the JS callback's context, not with the frame's context.
87             m_errorCallback->scheduleCallback(scriptExecutionContext(), error);
88         }
89     }
90
91     PassRefPtr<NavigatorUserMediaSuccessCallback> successCallback() const { return m_successCallback; }
92     PassRefPtr<NavigatorUserMediaErrorCallback> errorCallback() const { return m_errorCallback; }
93
94 private:
95     GenerateStreamRequest(ScriptExecutionContext* scriptExecutionContext,
96                           PassRefPtr<NavigatorUserMediaSuccessCallback> successCallback,
97                           PassRefPtr<NavigatorUserMediaErrorCallback> errorCallback)
98         : Request(scriptExecutionContext)
99         , m_successCallback(successCallback)
100         , m_errorCallback(errorCallback) { }
101
102     RefPtr<NavigatorUserMediaSuccessCallback> m_successCallback;
103     RefPtr<NavigatorUserMediaErrorCallback> m_errorCallback;
104 };
105
106 void MediaStreamFrameController::RequestMap::abort(int requestId)
107 {
108     get(requestId)->abort();
109     remove(requestId);
110 }
111
112 void MediaStreamFrameController::RequestMap::abortAll()
113 {
114     while (!isEmpty()) {
115         begin()->second->abort();
116         remove(begin());
117     }
118 }
119
120 template <typename T>
121 void MediaStreamFrameController::ClientMapBase<T>::unregisterAll()
122 {
123     while (!this->isEmpty()) {
124         T key = this->begin()->first;
125         // unregister should remove the element from the map.
126         this->begin()->second->unregister();
127         ASSERT_UNUSED(key, !this->contains(key));
128     }
129 }
130
131 template <typename T>
132 void MediaStreamFrameController::ClientMapBase<T>::detachEmbedder()
133 {
134     for (typename MapType::iterator it = this->begin(); it != this->end(); ++it)
135         it->second->detachEmbedder();
136 }
137
138 MediaStreamFrameController::MediaStreamFrameController(Frame* frame)
139     : m_frame(frame)
140     , m_isInDetachedState(false)
141 {
142     if (!isClientAvailable())
143         enterDetachedState();
144 }
145
146 MediaStreamFrameController::~MediaStreamFrameController()
147 {
148 }
149
150 SecurityOrigin* MediaStreamFrameController::securityOrigin() const
151 {
152     return m_frame ? m_frame->existingDOMWindow()->securityOrigin() : 0;
153 }
154
155 ScriptExecutionContext* MediaStreamFrameController::scriptExecutionContext() const
156 {
157     return m_frame ? m_frame->existingDOMWindow()->scriptExecutionContext() : 0;
158 }
159
160 MediaStreamController* MediaStreamFrameController::pageController() const
161 {
162     return m_frame && m_frame->page() ? m_frame->page()->mediaStreamController() : 0;
163 }
164
165 void MediaStreamFrameController::unregister(MediaStreamClient* client)
166 {
167     ASSERT(m_streams.contains(client->clientId()));
168
169     // Assuming we should stop any live streams when losing access to the embedder.
170     if (client->isLocalMediaStream()) {
171         LocalMediaStream* stream = static_cast<LocalMediaStream*>(client);
172         if (stream->readyState() == MediaStream::LIVE)
173             stopGeneratedStream(stream->label());
174     }
175
176     m_streams.remove(client->clientId());
177 }
178
179 void MediaStreamFrameController::unregister(GenericClient* client)
180 {
181     ASSERT(m_clients.contains(client->clientId()));
182     m_clients.remove(client->clientId());
183 }
184
185 MediaStream* MediaStreamFrameController::getStreamFromLabel(const String& label) const
186 {
187     ASSERT(m_streams.contains(label));
188     ASSERT(m_streams.get(label)->isMediaStream());
189     return static_cast<MediaStream*>(m_streams.get(label));
190 }
191
192 void MediaStreamFrameController::enterDetachedState()
193 {
194     if (m_isInDetachedState) {
195         ASSERT(m_requests.isEmpty());
196         return;
197     }
198
199     m_requests.abortAll();
200     m_streams.detachEmbedder();
201     m_clients.detachEmbedder();
202     m_isInDetachedState = true;
203 }
204
205 bool MediaStreamFrameController::isClientAvailable() const
206 {
207     if (m_isInDetachedState)
208         return false;
209
210     MediaStreamController* controller = pageController();
211     return controller && controller->isClientAvailable();
212 }
213
214 // Called also when the frame is detached from the page, in which case the page controller will remain alive.
215 void MediaStreamFrameController::disconnectPage()
216 {
217     if (pageController())
218         pageController()->unregisterFrameController(this);
219
220     enterDetachedState();
221 }
222
223 // Called when the frame is being destroyed. Since the frame controller is owned by the frame it will die shortly after this.
224 void MediaStreamFrameController::disconnectFrame()
225 {
226     disconnectPage();
227
228     ASSERT(m_requests.isEmpty());
229     m_streams.unregisterAll();
230     m_clients.unregisterAll();
231
232     m_frame = 0;
233 }
234
235 void MediaStreamFrameController::transferToNewPage(Page*)
236 {
237     // FIXME: In the future we should keep running the media stream services while transfering frames between pages.
238     // However, until a proper way to do this is decided, we're shutting down services.
239     disconnectPage();
240 }
241
242 GenerateStreamOptionFlags MediaStreamFrameController::parseGenerateStreamOptions(const String& options)
243 {
244     GenerateStreamOptionFlags flags = 0;
245     Vector<String> optionList;
246     options.split(',', optionList);
247
248     for (Vector<String>::const_iterator option = optionList.begin(); option != optionList.end(); ++option) {
249         Vector<String> suboptionList;
250         option->split(' ', suboptionList);
251
252         if (suboptionList.first() == "audio")
253             flags |= GenerateStreamRequestAudio;
254         else if (suboptionList.first() == "video") {
255             bool videoSuboptions = false;
256             Vector<String>::const_iterator suboption = suboptionList.begin();
257             for (++suboption; suboption != suboptionList.end(); ++suboption)
258                 if (*suboption == "user") {
259                     flags |= GenerateStreamRequestVideoFacingUser;
260                     videoSuboptions = true;
261                 } else if (*suboption == "environment") {
262                     flags |= GenerateStreamRequestVideoFacingEnvironment;
263                     videoSuboptions = true;
264                 }
265
266             // Ask for all kind of cameras if no suboption was specified.
267             if (!videoSuboptions)
268                 flags |= GenerateStreamRequestVideoFacingUser | GenerateStreamRequestVideoFacingEnvironment;
269         }
270     }
271
272     return flags;
273 }
274
275 // Implements the getUserMedia method from http://www.whatwg.org/specs/web-apps/current-work/#dom-navigator-getusermedia.
276 void MediaStreamFrameController::generateStream(const String& options,
277                                                 PassRefPtr<NavigatorUserMediaSuccessCallback> successCallback,
278                                                 PassRefPtr<NavigatorUserMediaErrorCallback> errorCallback,
279                                                 ExceptionCode& ec)
280 {
281     ec = 0;
282     if (!successCallback)
283         return;
284
285     GenerateStreamOptionFlags flags = parseGenerateStreamOptions(options);
286     if (!flags) {
287         ec = NOT_SUPPORTED_ERR;
288         return;
289     }
290
291     int requestId = m_requests.getNextId();
292     m_requests.add(requestId, GenerateStreamRequest::create(scriptExecutionContext(), successCallback, errorCallback));
293
294     if (!isClientAvailable()) {
295         // This makes sure to call the error callback if provided.
296         m_requests.abort(requestId);
297         return;
298     }
299
300     pageController()->generateStream(this, requestId, flags, securityOrigin());
301 }
302
303 void MediaStreamFrameController::stopGeneratedStream(const String& streamLabel)
304 {
305     ASSERT(m_streams.contains(streamLabel));
306     ASSERT(m_streams.get(streamLabel)->isLocalMediaStream());
307
308     if (isClientAvailable())
309         pageController()->stopGeneratedStream(streamLabel);
310 }
311
312 void MediaStreamFrameController::setMediaStreamTrackEnabled(const String& trackId, bool enabled)
313 {
314     if (isClientAvailable())
315         pageController()->setMediaStreamTrackEnabled(trackId, enabled);
316 }
317
318 void MediaStreamFrameController::streamGenerated(int requestId, const String& label, PassRefPtr<MediaStreamTrackList> tracksParam)
319 {
320     // Don't assert since the request can have been aborted as a result of embedder detachment.
321     if (!m_requests.contains(requestId))
322         return;
323
324     ASSERT(m_requests.get(requestId)->isGenerateStreamRequest());
325     ASSERT(!label.isNull());
326     ASSERT(tracksParam);
327
328     RefPtr<MediaStreamTrackList> tracks = tracksParam;
329
330     for (unsigned i = 0; i < tracks->length(); ++i) {
331         int trackClientId = m_clients.getNextId();
332         RefPtr<MediaStreamTrack> track = tracks->item(i);
333         track->associateFrameController(this, trackClientId);
334         m_clients.add(trackClientId, track.get());
335     }
336
337     RefPtr<GenerateStreamRequest> streamRequest = static_cast<GenerateStreamRequest*>(m_requests.get(requestId).get());
338     RefPtr<LocalMediaStream> generatedStream = LocalMediaStream::create(this, label, tracks.release());
339     m_streams.add(label, generatedStream.get());
340     m_requests.remove(requestId);
341     streamRequest->successCallback()->handleEvent(generatedStream.get());
342 }
343
344 void MediaStreamFrameController::streamGenerationFailed(int requestId, NavigatorUserMediaError::ErrorCode code)
345 {
346     // Don't assert since the request can have been aborted as a result of embedder detachment.
347     if (!m_requests.contains(requestId))
348         return;
349
350     ASSERT(m_requests.get(requestId)->isGenerateStreamRequest());
351
352     RefPtr<GenerateStreamRequest> streamRequest = static_cast<GenerateStreamRequest*>(m_requests.get(requestId).get());
353     m_requests.remove(requestId);
354
355     if (streamRequest->errorCallback()) {
356         RefPtr<NavigatorUserMediaError> error = NavigatorUserMediaError::create(code);
357         streamRequest->errorCallback()->handleEvent(error.get());
358     }
359 }
360
361 void MediaStreamFrameController::streamFailed(const String& label)
362 {
363     getStreamFromLabel(label)->streamEnded();
364 }
365
366 PassRefPtr<PeerConnection> MediaStreamFrameController::createPeerConnection(const String& configuration, PassRefPtr<SignalingCallback> signalingCallback)
367 {
368     int clientId = m_clients.getNextId();
369     RefPtr<PeerConnection> peerConnection = PeerConnection::create(this, clientId, configuration, signalingCallback);
370     if (peerConnection)
371         m_clients.add(clientId, peerConnection.get());
372     return peerConnection.release();
373 }
374
375 void MediaStreamFrameController::newPeerConnection(int peerConnectionId, const String& configuration)
376 {
377     if (pageController())
378         pageController()->newPeerConnection(this, peerConnectionId, configuration);
379 }
380
381 void MediaStreamFrameController::closePeerConnection(int peerConnectionId)
382 {
383     if (pageController())
384         pageController()->closePeerConnection(this, peerConnectionId);
385 }
386
387 void MediaStreamFrameController::startNegotiation(int peerConnectionId)
388 {
389     if (pageController())
390         pageController()->startNegotiation(this, peerConnectionId);
391 }
392
393 void MediaStreamFrameController::processSignalingMessage(int peerConnectionId, const String& message)
394 {
395     if (pageController())
396         pageController()->processSignalingMessage(this, peerConnectionId, message);
397 }
398
399 void MediaStreamFrameController::message(int peerConnectionId, const String& message)
400 {
401     if (pageController())
402         pageController()->message(this, peerConnectionId, message);
403 }
404
405 void MediaStreamFrameController::addStream(int peerConnectionId, PassRefPtr<MediaStream> prpStream)
406 {
407     RefPtr<MediaStream> stream = prpStream;
408     ASSERT(m_streams.contains(stream->label()));
409
410     if (pageController())
411         pageController()->addStream(this, peerConnectionId, stream->label());
412 }
413
414 void MediaStreamFrameController::removeStream(int peerConnectionId, PassRefPtr<MediaStream> prpStream)
415 {
416     RefPtr<MediaStream> stream = prpStream;
417     ASSERT(m_streams.contains(stream->label()));
418
419     if (pageController())
420         pageController()->removeStream(this, peerConnectionId, stream->label());
421 }
422
423 void MediaStreamFrameController::onMessage(int peerConnectionId, const String& message)
424 {
425     ASSERT(m_clients.contains(peerConnectionId));
426     ASSERT(m_clients.get(peerConnectionId)->isPeerConnection());
427
428     RefPtr<PeerConnection> peerConnection = static_cast<PeerConnection*>(m_clients.get(peerConnectionId));
429
430     peerConnection->onMessage(message);
431 }
432
433 void MediaStreamFrameController::onAddStream(int peerConnectionId, const String& streamLabel, PassRefPtr<MediaStreamTrackList> tracks)
434 {
435     ASSERT(!streamLabel.isEmpty());
436     ASSERT(m_clients.contains(peerConnectionId));
437     ASSERT(m_clients.get(peerConnectionId)->isPeerConnection());
438
439     RefPtr<MediaStream> stream = MediaStream::create(this, streamLabel, tracks);
440
441     RefPtr<PeerConnection> peerConnection = static_cast<PeerConnection*>(m_clients.get(peerConnectionId));
442     peerConnection->streamAdded(stream.release());
443 }
444
445 void MediaStreamFrameController::onRemoveStream(int peerConnectionId, const String& streamLabel)
446 {
447     ASSERT(!streamLabel.isEmpty());
448     ASSERT(m_clients.contains(peerConnectionId));
449     ASSERT(m_clients.get(peerConnectionId)->isPeerConnection());
450
451     RefPtr<PeerConnection> peerConnection = static_cast<PeerConnection*>(m_clients.get(peerConnectionId));
452     peerConnection->streamRemoved(streamLabel);
453 }
454
455 void MediaStreamFrameController::onSignalingMessage(int peerConnectionId, const String& message)
456 {
457     ASSERT(m_clients.contains(peerConnectionId));
458     ASSERT(m_clients.get(peerConnectionId)->isPeerConnection());
459
460     RefPtr<PeerConnection> peerConnection = static_cast<PeerConnection*>(m_clients.get(peerConnectionId));
461     peerConnection->onSignalingMessage(message);
462 }
463
464 void MediaStreamFrameController::onNegotiationStarted(int peerConnectionId)
465 {
466     // FIXME: Assert or not?
467     ASSERT(m_clients.contains(peerConnectionId));
468     ASSERT(m_clients.get(peerConnectionId)->isPeerConnection());
469
470     RefPtr<PeerConnection> peerConnection = static_cast<PeerConnection*>(m_clients.get(peerConnectionId));
471
472     peerConnection->onNegotiationStarted();
473 }
474
475 void MediaStreamFrameController::onNegotiationDone(int peerConnectionId)
476 {
477     // FIXME: Assert or not?
478     ASSERT(m_clients.contains(peerConnectionId));
479     ASSERT(m_clients.get(peerConnectionId)->isPeerConnection());
480
481     RefPtr<PeerConnection> peerConnection = static_cast<PeerConnection*>(m_clients.get(peerConnectionId));
482
483     peerConnection->onNegotiationDone();
484 }
485
486 } // namespace WebCore
487
488 #endif // ENABLE(MEDIA_STREAM)