[MediaStream] defer resolution of getUserMedia promise made in a background tab
[WebKit-https.git] / Source / WebCore / Modules / mediastream / MediaStream.cpp
1 /*
2  * Copyright (C) 2011 Google Inc. All rights reserved.
3  * Copyright (C) 2011, 2012, 2015 Ericsson AB. All rights reserved.
4  * Copyright (C) 2013-2016 Apple Inc. All rights reserved.
5  * Copyright (C) 2013 Nokia Corporation and/or its subsidiary(-ies).
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1.  Redistributions of source code must retain the above copyright
11  *     notice, this list of conditions and the following disclaimer.
12  * 2.  Redistributions in binary form must reproduce the above copyright
13  *     notice, this list of conditions and the following disclaimer in the
14  *     documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
23  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27
28 #include "config.h"
29 #include "MediaStream.h"
30
31 #if ENABLE(MEDIA_STREAM)
32
33 #include "Document.h"
34 #include "Event.h"
35 #include "EventNames.h"
36 #include "ExceptionCode.h"
37 #include "Logging.h"
38 #include "MediaStreamRegistry.h"
39 #include "MediaStreamTrackEvent.h"
40 #include "Page.h"
41 #include "RealtimeMediaSource.h"
42 #include "URL.h"
43
44 namespace WebCore {
45
46 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context)
47 {
48     return MediaStream::create(context, MediaStreamPrivate::create(Vector<RefPtr<MediaStreamTrackPrivate>>()));
49 }
50
51 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, MediaStream& stream)
52 {
53     return adoptRef(*new MediaStream(context, stream.getTracks()));
54 }
55
56 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, const MediaStreamTrackVector& tracks)
57 {
58     return adoptRef(*new MediaStream(context, tracks));
59 }
60
61 Ref<MediaStream> MediaStream::create(ScriptExecutionContext& context, RefPtr<MediaStreamPrivate>&& streamPrivate)
62 {
63     return adoptRef(*new MediaStream(context, WTFMove(streamPrivate)));
64 }
65
66 MediaStream::MediaStream(ScriptExecutionContext& context, const MediaStreamTrackVector& tracks)
67     : ContextDestructionObserver(&context)
68     , m_activityEventTimer(*this, &MediaStream::activityEventTimerFired)
69 {
70     // This constructor preserves MediaStreamTrack instances and must be used by calls originating
71     // from the JavaScript MediaStream constructor.
72     MediaStreamTrackPrivateVector trackPrivates;
73     trackPrivates.reserveCapacity(tracks.size());
74
75     for (auto& track : tracks) {
76         track->addObserver(this);
77         m_trackSet.add(track->id(), track);
78         trackPrivates.append(&track->privateTrack());
79     }
80
81     m_private = MediaStreamPrivate::create(trackPrivates);
82     setIsActive(m_private->active());
83     m_private->addObserver(*this);
84     MediaStreamRegistry::shared().registerStream(*this);
85     document()->addAudioProducer(this);
86 }
87
88 MediaStream::MediaStream(ScriptExecutionContext& context, RefPtr<MediaStreamPrivate>&& streamPrivate)
89     : ContextDestructionObserver(&context)
90     , m_private(streamPrivate)
91     , m_activityEventTimer(*this, &MediaStream::activityEventTimerFired)
92 {
93     ASSERT(m_private);
94     setIsActive(m_private->active());
95     m_private->addObserver(*this);
96     MediaStreamRegistry::shared().registerStream(*this);
97
98     for (auto& trackPrivate : m_private->tracks()) {
99         auto track = MediaStreamTrack::create(context, *trackPrivate);
100         track->addObserver(this);
101         m_trackSet.add(track->id(), WTFMove(track));
102     }
103     document()->addAudioProducer(this);
104 }
105
106 MediaStream::~MediaStream()
107 {
108     // Set isActive to false immediately so an callbacks triggered by shutting down, e.g.
109     // mediaState(), are short circuited.
110     m_isActive = false;
111     MediaStreamRegistry::shared().unregisterStream(*this);
112     m_private->removeObserver(*this);
113     for (auto& track : m_trackSet.values())
114         track->removeObserver(this);
115     if (Document* document = this->document()) {
116         document->removeAudioProducer(this);
117         if (m_isWaitingUntilMediaCanStart)
118             document->removeMediaCanStartListener(this);
119     }
120 }
121
122 RefPtr<MediaStream> MediaStream::clone()
123 {
124     MediaStreamTrackVector clonedTracks;
125     clonedTracks.reserveCapacity(m_trackSet.size());
126
127     for (auto& track : m_trackSet.values())
128         clonedTracks.append(track->clone());
129
130     return MediaStream::create(*scriptExecutionContext(), clonedTracks);
131 }
132
133 void MediaStream::addTrack(MediaStreamTrack& track)
134 {
135     if (!internalAddTrack(track, StreamModifier::DomAPI))
136         return;
137
138     for (auto& observer : m_observers)
139         observer->didAddOrRemoveTrack();
140 }
141
142 void MediaStream::removeTrack(MediaStreamTrack& track)
143 {
144     if (!internalRemoveTrack(track.id(), StreamModifier::DomAPI))
145         return;
146
147     for (auto& observer : m_observers)
148         observer->didAddOrRemoveTrack();
149 }
150
151 MediaStreamTrack* MediaStream::getTrackById(String id)
152 {
153     auto it = m_trackSet.find(id);
154     if (it != m_trackSet.end())
155         return it->value.get();
156
157     return nullptr;
158 }
159
160 MediaStreamTrackVector MediaStream::getAudioTracks() const
161 {
162     return trackVectorForType(RealtimeMediaSource::Audio);
163 }
164
165 MediaStreamTrackVector MediaStream::getVideoTracks() const
166 {
167     return trackVectorForType(RealtimeMediaSource::Video);
168 }
169
170 MediaStreamTrackVector MediaStream::getTracks() const
171 {
172     MediaStreamTrackVector tracks;
173     tracks.reserveCapacity(m_trackSet.size());
174     copyValuesToVector(m_trackSet, tracks);
175
176     return tracks;
177 }
178
179 void MediaStream::contextDestroyed()
180 {
181     ContextDestructionObserver::contextDestroyed();
182 }
183
184 void MediaStream::trackDidEnd()
185 {
186     m_private->updateActiveState(MediaStreamPrivate::NotifyClientOption::Notify);
187 }
188
189 void MediaStream::activeStatusChanged()
190 {
191     // Schedule the active state change and event dispatch since this callback may be called
192     // synchronously from the DOM API (e.g. as a result of addTrack()).
193     scheduleActiveStateChange();
194 }
195
196 void MediaStream::didAddTrack(MediaStreamTrackPrivate& trackPrivate)
197 {
198     ScriptExecutionContext* context = scriptExecutionContext();
199     if (!context)
200         return;
201
202     if (!getTrackById(trackPrivate.id()))
203         internalAddTrack(MediaStreamTrack::create(*context, trackPrivate), StreamModifier::Platform);
204 }
205
206 void MediaStream::didRemoveTrack(MediaStreamTrackPrivate& trackPrivate)
207 {
208     internalRemoveTrack(trackPrivate.id(), StreamModifier::Platform);
209 }
210
211 bool MediaStream::internalAddTrack(Ref<MediaStreamTrack>&& trackToAdd, StreamModifier streamModifier)
212 {
213     auto result = m_trackSet.add(trackToAdd->id(), WTFMove(trackToAdd));
214     if (!result.isNewEntry)
215         return false;
216
217     ASSERT(result.iterator->value);
218     auto& track = *result.iterator->value;
219     track.addObserver(this);
220
221     if (streamModifier == StreamModifier::DomAPI)
222         m_private->addTrack(&track.privateTrack(), MediaStreamPrivate::NotifyClientOption::DontNotify);
223     else
224         dispatchEvent(MediaStreamTrackEvent::create(eventNames().addtrackEvent, false, false, &track));
225
226     return true;
227 }
228
229 bool MediaStream::internalRemoveTrack(const String& trackId, StreamModifier streamModifier)
230 {
231     auto track = m_trackSet.take(trackId);
232     if (!track)
233         return false;
234
235     track->removeObserver(this);
236
237     if (streamModifier == StreamModifier::DomAPI)
238         m_private->removeTrack(track->privateTrack(), MediaStreamPrivate::NotifyClientOption::DontNotify);
239     else
240         dispatchEvent(MediaStreamTrackEvent::create(eventNames().removetrackEvent, false, false, WTFMove(track)));
241
242     return true;
243 }
244
245 void MediaStream::setIsActive(bool active)
246 {
247     if (m_isActive == active)
248         return;
249
250     m_isActive = active;
251     statusDidChange();
252 }
253
254 void MediaStream::mediaCanStart(Document& document)
255 {
256     ASSERT_UNUSED(document, &document == this->document());
257     ASSERT(m_isWaitingUntilMediaCanStart);
258     if (m_isWaitingUntilMediaCanStart) {
259         m_isWaitingUntilMediaCanStart = false;
260         startProducingData();
261     }
262 }
263
264 void MediaStream::startProducingData()
265 {
266     Document* document = this->document();
267     if (!document || !document->page())
268         return;
269
270     // If we can't start a load right away, start it later.
271     if (!document->page()->canStartMedia()) {
272         LOG(Media, "MediaStream::startProducingData(%p) - not allowed to start in background, waiting", this);
273         if (m_isWaitingUntilMediaCanStart)
274             return;
275
276         m_isWaitingUntilMediaCanStart = true;
277         document->addMediaCanStartListener(this);
278         return;
279     }
280
281     m_private->startProducingData();
282 }
283
284 void MediaStream::stopProducingData()
285 {
286     m_private->stopProducingData();
287 }
288
289 void MediaStream::pageMutedStateDidChange()
290 {
291     if (!m_isActive)
292         return;
293
294     Document* document = this->document();
295     if (!document)
296         return;
297
298     bool pageMuted = document->page()->isMediaCaptureMuted();
299     if (m_externallyMuted == pageMuted)
300         return;
301
302     m_externallyMuted = pageMuted;
303     if (pageMuted)
304         stopProducingData();
305     else
306         startProducingData();
307 }
308
309 MediaProducer::MediaStateFlags MediaStream::mediaState() const
310 {
311     MediaStateFlags state = IsNotPlaying;
312
313     if (!m_isActive)
314         return state;
315
316     state |= HasMediaCaptureDevice;
317     if (m_private->isProducingData())
318         state |= HasActiveMediaCaptureDevice;
319
320     if (m_private->hasAudio() || m_private->hasVideo())
321         state |= HasAudioOrVideo;
322
323     return state;
324 }
325
326 void MediaStream::statusDidChange()
327 {
328     if (Document* document = this->document()) {
329         if (m_isActive)
330             document->setHasActiveMediaStreamTrack();
331         document->updateIsPlayingMedia();
332     }
333 }
334
335 void MediaStream::characteristicsChanged()
336 {
337     bool muted = m_private->muted();
338     if (m_isMuted != muted) {
339         m_isMuted = muted;
340         statusDidChange();
341     }
342 }
343
344 void MediaStream::scheduleActiveStateChange()
345 {
346     bool active = false;
347     for (auto& track : m_trackSet.values()) {
348         if (!track->ended()) {
349             active = true;
350             break;
351         }
352     }
353
354     if (m_isActive == active)
355         return;
356
357     setIsActive(active);
358
359     const AtomicString& eventName = m_isActive ? eventNames().inactiveEvent : eventNames().activeEvent;
360     m_scheduledActivityEvents.append(Event::create(eventName, false, false));
361
362     if (!m_activityEventTimer.isActive())
363         m_activityEventTimer.startOneShot(0);
364 }
365
366 void MediaStream::activityEventTimerFired()
367 {
368     Vector<Ref<Event>> events;
369     events.swap(m_scheduledActivityEvents);
370
371     for (auto& event : events)
372         dispatchEvent(event);
373 }
374
375 URLRegistry& MediaStream::registry() const
376 {
377     return MediaStreamRegistry::shared();
378 }
379
380 MediaStreamTrackVector MediaStream::trackVectorForType(RealtimeMediaSource::Type filterType) const
381 {
382     MediaStreamTrackVector tracks;
383     for (auto& track : m_trackSet.values()) {
384         if (track->source().type() == filterType)
385             tracks.append(track);
386     }
387
388     return tracks;
389 }
390
391 void MediaStream::addObserver(MediaStream::Observer* observer)
392 {
393     if (m_observers.find(observer) == notFound)
394         m_observers.append(observer);
395 }
396
397 void MediaStream::removeObserver(MediaStream::Observer* observer)
398 {
399     size_t pos = m_observers.find(observer);
400     if (pos != notFound)
401         m_observers.remove(pos);
402 }
403
404 Document* MediaStream::document() const
405 {
406     return downcast<Document>(scriptExecutionContext());
407 }
408
409 } // namespace WebCore
410
411 #endif // ENABLE(MEDIA_STREAM)