1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
4  * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 #include "DOMMediaStream.h"
7 
8 #include "AudioCaptureStream.h"
9 #include "AudioChannelAgent.h"
10 #include "AudioStreamTrack.h"
11 #include "Layers.h"
12 #include "MediaStreamGraph.h"
13 #include "MediaStreamGraphImpl.h"
14 #include "MediaStreamListener.h"
15 #include "VideoStreamTrack.h"
16 #include "mozilla/dom/AudioNode.h"
17 #include "mozilla/dom/AudioTrack.h"
18 #include "mozilla/dom/AudioTrackList.h"
19 #include "mozilla/dom/DocGroup.h"
20 #include "mozilla/dom/HTMLCanvasElement.h"
21 #include "mozilla/dom/LocalMediaStreamBinding.h"
22 #include "mozilla/dom/MediaStreamBinding.h"
23 #include "mozilla/dom/MediaStreamTrackEvent.h"
24 #include "mozilla/dom/Promise.h"
25 #include "mozilla/dom/VideoTrack.h"
26 #include "mozilla/dom/VideoTrackList.h"
27 #include "mozilla/media/MediaUtils.h"
28 #include "nsContentUtils.h"
29 #include "nsIScriptError.h"
30 #include "nsIUUIDGenerator.h"
31 #include "nsPIDOMWindow.h"
32 #include "nsProxyRelease.h"
33 #include "nsRFPService.h"
34 #include "nsServiceManagerUtils.h"
35 
36 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
37 // GetTickCount() and conflicts with NS_DECL_NSIDOMMEDIASTREAM, containing
38 // currentTime getter.
39 #ifdef GetCurrentTime
40 #undef GetCurrentTime
41 #endif
42 
43 #ifdef LOG
44 #undef LOG
45 #endif
46 
47 // GetCurrentTime is defined in winbase.h as zero argument macro forwarding to
48 // GetTickCount() and conflicts with MediaStream::GetCurrentTime.
49 #ifdef GetCurrentTime
50 #undef GetCurrentTime
51 #endif
52 
53 using namespace mozilla;
54 using namespace mozilla::dom;
55 using namespace mozilla::layers;
56 using namespace mozilla::media;
57 
58 static LazyLogModule gMediaStreamLog("MediaStream");
59 #define LOG(type, msg) MOZ_LOG(gMediaStreamLog, type, msg)
60 
61 const TrackID TRACK_VIDEO_PRIMARY = 1;
62 
ContainsLiveTracks(nsTArray<RefPtr<DOMMediaStream::TrackPort>> & aTracks)63 static bool ContainsLiveTracks(
64     nsTArray<RefPtr<DOMMediaStream::TrackPort>>& aTracks) {
65   for (auto& port : aTracks) {
66     if (port->GetTrack()->ReadyState() == MediaStreamTrackState::Live) {
67       return true;
68     }
69   }
70 
71   return false;
72 }
73 
TrackPort(MediaInputPort * aInputPort,MediaStreamTrack * aTrack,const InputPortOwnership aOwnership)74 DOMMediaStream::TrackPort::TrackPort(MediaInputPort* aInputPort,
75                                      MediaStreamTrack* aTrack,
76                                      const InputPortOwnership aOwnership)
77     : mInputPort(aInputPort), mTrack(aTrack), mOwnership(aOwnership) {
78   MOZ_ASSERT(mInputPort);
79   MOZ_ASSERT(mTrack);
80 
81   MOZ_COUNT_CTOR(TrackPort);
82 }
83 
~TrackPort()84 DOMMediaStream::TrackPort::~TrackPort() {
85   MOZ_COUNT_DTOR(TrackPort);
86 
87   if (mOwnership == InputPortOwnership::OWNED) {
88     DestroyInputPort();
89   }
90 }
91 
DestroyInputPort()92 void DOMMediaStream::TrackPort::DestroyInputPort() {
93   if (mInputPort) {
94     mInputPort->Destroy();
95     mInputPort = nullptr;
96   }
97 }
98 
GetSource() const99 MediaStream* DOMMediaStream::TrackPort::GetSource() const {
100   return mInputPort ? mInputPort->GetSource() : nullptr;
101 }
102 
GetSourceTrackId() const103 TrackID DOMMediaStream::TrackPort::GetSourceTrackId() const {
104   return mInputPort ? mInputPort->GetSourceTrackId() : TRACK_INVALID;
105 }
106 
BlockSourceTrackId(TrackID aTrackId,BlockingMode aBlockingMode)107 already_AddRefed<Pledge<bool>> DOMMediaStream::TrackPort::BlockSourceTrackId(
108     TrackID aTrackId, BlockingMode aBlockingMode) {
109   if (mInputPort) {
110     return mInputPort->BlockSourceTrackId(aTrackId, aBlockingMode);
111   }
112   auto rejected = MakeRefPtr<Pledge<bool>>();
113   rejected->Reject(NS_ERROR_FAILURE);
114   return rejected.forget();
115 }
116 
117 NS_IMPL_CYCLE_COLLECTION(DOMMediaStream::TrackPort, mTrack)
118 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMediaStream::TrackPort, AddRef)
119 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMMediaStream::TrackPort, Release)
120 
121 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaStreamTrackSourceGetter)
122 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaStreamTrackSourceGetter)
123 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamTrackSourceGetter)
124   NS_INTERFACE_MAP_ENTRY(nsISupports)
125 NS_INTERFACE_MAP_END
126 NS_IMPL_CYCLE_COLLECTION_0(MediaStreamTrackSourceGetter)
127 
128 /**
129  * Listener registered on the Owned stream to detect added and ended owned
130  * tracks for keeping the list of MediaStreamTracks in sync with the tracks
131  * added and ended directly at the source.
132  */
133 class DOMMediaStream::OwnedStreamListener : public MediaStreamListener {
134  public:
OwnedStreamListener(DOMMediaStream * aStream)135   explicit OwnedStreamListener(DOMMediaStream* aStream) : mStream(aStream) {}
136 
Forget()137   void Forget() { mStream = nullptr; }
138 
DoNotifyTrackCreated(MediaStreamGraph * aGraph,TrackID aTrackID,MediaSegment::Type aType,MediaStream * aInputStream,TrackID aInputTrackID)139   void DoNotifyTrackCreated(MediaStreamGraph* aGraph, TrackID aTrackID,
140                             MediaSegment::Type aType, MediaStream* aInputStream,
141                             TrackID aInputTrackID) {
142     MOZ_ASSERT(NS_IsMainThread());
143 
144     if (!mStream) {
145       return;
146     }
147 
148     MediaStreamTrack* track =
149         mStream->FindOwnedDOMTrack(aInputStream, aInputTrackID, aTrackID);
150 
151     if (track) {
152       LOG(LogLevel::Debug, ("DOMMediaStream %p Track %d from owned stream %p "
153                             "bound to MediaStreamTrack %p.",
154                             mStream, aTrackID, aInputStream, track));
155       return;
156     }
157 
158     // Track had not been created on main thread before, create it now.
159     NS_WARNING_ASSERTION(
160         !mStream->mTracks.IsEmpty(),
161         "A new track was detected on the input stream; creating a "
162         "corresponding "
163         "MediaStreamTrack. Initial tracks should be added manually to "
164         "immediately and synchronously be available to JS.");
165     RefPtr<MediaStreamTrackSource> source;
166     if (mStream->mTrackSourceGetter) {
167       source = mStream->mTrackSourceGetter->GetMediaStreamTrackSource(aTrackID);
168     }
169     if (!source) {
170       NS_ASSERTION(false,
171                    "Dynamic track created without an explicit TrackSource");
172       nsPIDOMWindowInner* window = mStream->GetParentObject();
173       nsIDocument* doc = window ? window->GetExtantDoc() : nullptr;
174       nsIPrincipal* principal = doc ? doc->NodePrincipal() : nullptr;
175       source = new BasicTrackSource(principal);
176     }
177 
178     RefPtr<MediaStreamTrack> newTrack =
179         mStream->CreateDOMTrack(aTrackID, aType, source);
180     aGraph->AbstractMainThread()->Dispatch(
181         NewRunnableMethod<RefPtr<MediaStreamTrack>>(
182             "DOMMediaStream::AddTrackInternal", mStream,
183             &DOMMediaStream::AddTrackInternal, newTrack));
184   }
185 
DoNotifyTrackEnded(MediaStreamGraph * aGraph,MediaStream * aInputStream,TrackID aInputTrackID,TrackID aTrackID)186   void DoNotifyTrackEnded(MediaStreamGraph* aGraph, MediaStream* aInputStream,
187                           TrackID aInputTrackID, TrackID aTrackID) {
188     MOZ_ASSERT(NS_IsMainThread());
189 
190     if (!mStream) {
191       return;
192     }
193 
194     RefPtr<MediaStreamTrack> track =
195         mStream->FindOwnedDOMTrack(aInputStream, aInputTrackID, aTrackID);
196     NS_ASSERTION(track,
197                  "Owned MediaStreamTracks must be known by the DOMMediaStream");
198     if (track) {
199       LOG(LogLevel::Debug, ("DOMMediaStream %p MediaStreamTrack %p ended at "
200                             "the source. Marking it ended.",
201                             mStream, track.get()));
202       aGraph->AbstractMainThread()->Dispatch(
203           NewRunnableMethod("dom::MediaStreamTrack::OverrideEnded", track,
204                             &MediaStreamTrack::OverrideEnded));
205     }
206   }
207 
NotifyQueuedTrackChanges(MediaStreamGraph * aGraph,TrackID aID,StreamTime aTrackOffset,TrackEventCommand aTrackEvents,const MediaSegment & aQueuedMedia,MediaStream * aInputStream,TrackID aInputTrackID)208   void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
209                                 StreamTime aTrackOffset,
210                                 TrackEventCommand aTrackEvents,
211                                 const MediaSegment& aQueuedMedia,
212                                 MediaStream* aInputStream,
213                                 TrackID aInputTrackID) override {
214     if (aTrackEvents & TrackEventCommand::TRACK_EVENT_CREATED) {
215       aGraph->DispatchToMainThreadAfterStreamStateUpdate(
216           NewRunnableMethod<MediaStreamGraph*, TrackID, MediaSegment::Type,
217                             RefPtr<MediaStream>, TrackID>(
218               "DOMMediaStream::OwnedStreamListener::DoNotifyTrackCreated", this,
219               &OwnedStreamListener::DoNotifyTrackCreated, aGraph, aID,
220               aQueuedMedia.GetType(), aInputStream, aInputTrackID));
221     } else if (aTrackEvents & TrackEventCommand::TRACK_EVENT_ENDED) {
222       aGraph->DispatchToMainThreadAfterStreamStateUpdate(
223           NewRunnableMethod<MediaStreamGraph*, RefPtr<MediaStream>, TrackID,
224                             TrackID>(
225               "DOMMediaStream::OwnedStreamListener::DoNotifyTrackEnded", this,
226               &OwnedStreamListener::DoNotifyTrackEnded, aGraph, aInputStream,
227               aInputTrackID, aID));
228     }
229   }
230 
231  private:
232   // These fields may only be accessed on the main thread
233   DOMMediaStream* mStream;
234 };
235 
236 /**
237  * Listener registered on the Playback stream to detect when tracks end and when
238  * all new tracks this iteration have been created - for when several tracks are
239  * queued by the source and committed all at once.
240  */
241 class DOMMediaStream::PlaybackStreamListener : public MediaStreamListener {
242  public:
PlaybackStreamListener(DOMMediaStream * aStream)243   explicit PlaybackStreamListener(DOMMediaStream* aStream) : mStream(aStream) {}
244 
Forget()245   void Forget() {
246     MOZ_ASSERT(NS_IsMainThread());
247     mStream = nullptr;
248   }
249 
DoNotifyFinishedTrackCreation()250   void DoNotifyFinishedTrackCreation() {
251     MOZ_ASSERT(NS_IsMainThread());
252 
253     if (!mStream) {
254       return;
255     }
256 
257     // The owned stream listener adds its tracks after another main thread
258     // dispatch. We have to do the same to notify of created tracks to stay
259     // in sync. (Or NotifyTracksCreated is called before tracks are added).
260     MOZ_ASSERT(mStream->GetPlaybackStream());
261     mStream->GetPlaybackStream()->Graph()->AbstractMainThread()->Dispatch(
262         NewRunnableMethod("DOMMediaStream::NotifyTracksCreated", mStream,
263                           &DOMMediaStream::NotifyTracksCreated));
264   }
265 
DoNotifyFinished()266   void DoNotifyFinished() {
267     MOZ_ASSERT(NS_IsMainThread());
268 
269     if (!mStream) {
270       return;
271     }
272 
273     mStream->GetPlaybackStream()->Graph()->AbstractMainThread()->Dispatch(
274         NewRunnableMethod("DOMMediaStream::NotifyFinished", mStream,
275                           &DOMMediaStream::NotifyFinished));
276   }
277 
278   // The methods below are called on the MediaStreamGraph thread.
279 
NotifyFinishedTrackCreation(MediaStreamGraph * aGraph)280   void NotifyFinishedTrackCreation(MediaStreamGraph* aGraph) override {
281     aGraph->DispatchToMainThreadAfterStreamStateUpdate(NewRunnableMethod(
282         "DOMMediaStream::PlaybackStreamListener::DoNotifyFinishedTrackCreation",
283         this, &PlaybackStreamListener::DoNotifyFinishedTrackCreation));
284   }
285 
NotifyEvent(MediaStreamGraph * aGraph,MediaStreamGraphEvent event)286   void NotifyEvent(MediaStreamGraph* aGraph,
287                    MediaStreamGraphEvent event) override {
288     if (event == MediaStreamGraphEvent::EVENT_FINISHED) {
289       aGraph->DispatchToMainThreadAfterStreamStateUpdate(NewRunnableMethod(
290           "DOMMediaStream::PlaybackStreamListener::DoNotifyFinished", this,
291           &PlaybackStreamListener::DoNotifyFinished));
292     }
293   }
294 
295  private:
296   // These fields may only be accessed on the main thread
297   DOMMediaStream* mStream;
298 };
299 
300 class DOMMediaStream::PlaybackTrackListener : public MediaStreamTrackConsumer {
301  public:
PlaybackTrackListener(DOMMediaStream * aStream)302   explicit PlaybackTrackListener(DOMMediaStream* aStream) : mStream(aStream) {}
303 
304   NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(PlaybackTrackListener)
NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(PlaybackTrackListener)305   NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(PlaybackTrackListener)
306 
307   void NotifyEnded(MediaStreamTrack* aTrack) override {
308     if (!mStream) {
309       MOZ_ASSERT(false);
310       return;
311     }
312 
313     if (!aTrack) {
314       MOZ_ASSERT(false);
315       return;
316     }
317 
318     MOZ_ASSERT(mStream->HasTrack(*aTrack));
319     mStream->NotifyTrackRemoved(aTrack);
320   }
321 
322  protected:
~PlaybackTrackListener()323   virtual ~PlaybackTrackListener() {}
324 
325   RefPtr<DOMMediaStream> mStream;
326 };
327 
328 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMMediaStream::PlaybackTrackListener,
329                                      AddRef)
330 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMMediaStream::PlaybackTrackListener,
331                                        Release)
332 NS_IMPL_CYCLE_COLLECTION(DOMMediaStream::PlaybackTrackListener, mStream)
333 
334 NS_IMPL_CYCLE_COLLECTION_CLASS(DOMMediaStream)
335 
336 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMMediaStream,
337                                                 DOMEventTargetHelper)
338   tmp->Destroy();
339   NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwnedTracks)340   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwnedTracks)
341   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks)
342   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive)
343   NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrackSourceGetter)
344   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlaybackTrackListener)
345   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
346   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoPrincipal)
347 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
348 
349 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMMediaStream,
350                                                   DOMEventTargetHelper)
351   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
352   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwnedTracks)
353   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks)
354   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive)
355   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrackSourceGetter)
356   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaybackTrackListener)
357   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
358   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVideoPrincipal)
359 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
360 
361 NS_IMPL_ADDREF_INHERITED(DOMMediaStream, DOMEventTargetHelper)
362 NS_IMPL_RELEASE_INHERITED(DOMMediaStream, DOMEventTargetHelper)
363 
364 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMMediaStream)
365   NS_INTERFACE_MAP_ENTRY(DOMMediaStream)
366 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
367 
368 NS_IMPL_ADDREF_INHERITED(DOMLocalMediaStream, DOMMediaStream)
369 NS_IMPL_RELEASE_INHERITED(DOMLocalMediaStream, DOMMediaStream)
370 
371 NS_INTERFACE_MAP_BEGIN(DOMLocalMediaStream)
372   NS_INTERFACE_MAP_ENTRY(DOMLocalMediaStream)
373 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
374 
375 NS_IMPL_CYCLE_COLLECTION_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream,
376                                    mStreamNode)
377 
378 NS_IMPL_ADDREF_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
379 NS_IMPL_RELEASE_INHERITED(DOMAudioNodeMediaStream, DOMMediaStream)
380 
381 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMAudioNodeMediaStream)
382 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream)
383 
384 DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner* aWindow,
385                                MediaStreamTrackSourceGetter* aTrackSourceGetter)
386     : mLogicalStreamStartTime(0),
387       mWindow(aWindow),
388       mInputStream(nullptr),
389       mOwnedStream(nullptr),
390       mPlaybackStream(nullptr),
391       mTracksPendingRemoval(0),
392       mTrackSourceGetter(aTrackSourceGetter),
393       mPlaybackTrackListener(MakeAndAddRef<PlaybackTrackListener>(this)),
394       mTracksCreated(false),
395       mNotifiedOfMediaStreamGraphShutdown(false),
396       mActive(false),
397       mSetInactiveOnFinish(false) {
398   nsresult rv;
399   nsCOMPtr<nsIUUIDGenerator> uuidgen =
400       do_GetService("@mozilla.org/uuid-generator;1", &rv);
401 
402   if (NS_SUCCEEDED(rv) && uuidgen) {
403     nsID uuid;
404     memset(&uuid, 0, sizeof(uuid));
405     rv = uuidgen->GenerateUUIDInPlace(&uuid);
406     if (NS_SUCCEEDED(rv)) {
407       char buffer[NSID_LENGTH];
408       uuid.ToProvidedString(buffer);
409       mID = NS_ConvertASCIItoUTF16(buffer);
410     }
411   }
412 }
413 
~DOMMediaStream()414 DOMMediaStream::~DOMMediaStream() { Destroy(); }
415 
Destroy()416 void DOMMediaStream::Destroy() {
417   LOG(LogLevel::Debug, ("DOMMediaStream %p Being destroyed.", this));
418   if (mOwnedListener) {
419     mOwnedListener->Forget();
420     mOwnedListener = nullptr;
421   }
422   if (mPlaybackListener) {
423     mPlaybackListener->Forget();
424     mPlaybackListener = nullptr;
425   }
426   for (const RefPtr<TrackPort>& info : mTracks) {
427     // We must remove ourselves from each track's principal change observer list
428     // before we die. CC may have cleared info->mTrack so guard against it.
429     MediaStreamTrack* track = info->GetTrack();
430     if (track) {
431       track->RemovePrincipalChangeObserver(this);
432       if (!track->Ended()) {
433         track->RemoveConsumer(mPlaybackTrackListener);
434       }
435     }
436   }
437   if (mPlaybackPort) {
438     mPlaybackPort->Destroy();
439     mPlaybackPort = nullptr;
440   }
441   if (mOwnedPort) {
442     mOwnedPort->Destroy();
443     mOwnedPort = nullptr;
444   }
445   if (mPlaybackStream) {
446     mPlaybackStream->UnregisterUser();
447     mPlaybackStream = nullptr;
448   }
449   if (mOwnedStream) {
450     mOwnedStream->UnregisterUser();
451     mOwnedStream = nullptr;
452   }
453   if (mInputStream) {
454     mInputStream->UnregisterUser();
455     mInputStream = nullptr;
456   }
457   mRunOnTracksAvailable.Clear();
458   mTrackListeners.Clear();
459 }
460 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)461 JSObject* DOMMediaStream::WrapObject(JSContext* aCx,
462                                      JS::Handle<JSObject*> aGivenProto) {
463   return dom::MediaStreamBinding::Wrap(aCx, this, aGivenProto);
464 }
465 
Constructor(const GlobalObject & aGlobal,ErrorResult & aRv)466 /* static */ already_AddRefed<DOMMediaStream> DOMMediaStream::Constructor(
467     const GlobalObject& aGlobal, ErrorResult& aRv) {
468   Sequence<OwningNonNull<MediaStreamTrack>> emptyTrackSeq;
469   return Constructor(aGlobal, emptyTrackSeq, aRv);
470 }
471 
Constructor(const GlobalObject & aGlobal,const DOMMediaStream & aStream,ErrorResult & aRv)472 /* static */ already_AddRefed<DOMMediaStream> DOMMediaStream::Constructor(
473     const GlobalObject& aGlobal, const DOMMediaStream& aStream,
474     ErrorResult& aRv) {
475   nsTArray<RefPtr<MediaStreamTrack>> tracks;
476   aStream.GetTracks(tracks);
477 
478   Sequence<OwningNonNull<MediaStreamTrack>> nonNullTrackSeq;
479   if (!nonNullTrackSeq.SetLength(tracks.Length(), fallible)) {
480     MOZ_ASSERT(false);
481     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
482     return nullptr;
483   }
484 
485   for (size_t i = 0; i < tracks.Length(); ++i) {
486     nonNullTrackSeq[i] = tracks[i];
487   }
488 
489   return Constructor(aGlobal, nonNullTrackSeq, aRv);
490 }
491 
Constructor(const GlobalObject & aGlobal,const Sequence<OwningNonNull<MediaStreamTrack>> & aTracks,ErrorResult & aRv)492 /* static */ already_AddRefed<DOMMediaStream> DOMMediaStream::Constructor(
493     const GlobalObject& aGlobal,
494     const Sequence<OwningNonNull<MediaStreamTrack>>& aTracks,
495     ErrorResult& aRv) {
496   nsCOMPtr<nsPIDOMWindowInner> ownerWindow =
497       do_QueryInterface(aGlobal.GetAsSupports());
498   if (!ownerWindow) {
499     aRv.Throw(NS_ERROR_FAILURE);
500     return nullptr;
501   }
502 
503   // Streams created from JS cannot have dynamically created tracks.
504   MediaStreamTrackSourceGetter* getter = nullptr;
505   RefPtr<DOMMediaStream> newStream = new DOMMediaStream(ownerWindow, getter);
506 
507   for (MediaStreamTrack& track : aTracks) {
508     if (!newStream->GetPlaybackStream()) {
509       MOZ_RELEASE_ASSERT(track.Graph());
510       newStream->InitPlaybackStreamCommon(track.Graph());
511     }
512     newStream->AddTrack(track);
513   }
514 
515   if (!newStream->GetPlaybackStream()) {
516     MOZ_ASSERT(aTracks.IsEmpty());
517     MediaStreamGraph* graph = MediaStreamGraph::GetInstance(
518         MediaStreamGraph::SYSTEM_THREAD_DRIVER, ownerWindow);
519     newStream->InitPlaybackStreamCommon(graph);
520   }
521 
522   return newStream.forget();
523 }
524 
CurrentTime()525 double DOMMediaStream::CurrentTime() {
526   if (!mPlaybackStream) {
527     return 0.0;
528   }
529   // The value of a MediaStream's CurrentTime will always advance forward; it
530   // will never reset (even if one rewinds a video.) Therefore we can use a
531   // single Random Seed initialized at the same time as the object.
532   return nsRFPService::ReduceTimePrecisionAsSecs(
533       mPlaybackStream->StreamTimeToSeconds(mPlaybackStream->GetCurrentTime() -
534                                            mLogicalStreamStartTime),
535       GetRandomTimelineSeed());
536 }
537 
CountUnderlyingStreams(const GlobalObject & aGlobal,ErrorResult & aRv)538 already_AddRefed<Promise> DOMMediaStream::CountUnderlyingStreams(
539     const GlobalObject& aGlobal, ErrorResult& aRv) {
540   nsCOMPtr<nsPIDOMWindowInner> window =
541       do_QueryInterface(aGlobal.GetAsSupports());
542   if (!window) {
543     aRv.Throw(NS_ERROR_UNEXPECTED);
544     return nullptr;
545   }
546 
547   nsCOMPtr<nsIGlobalObject> go = do_QueryInterface(aGlobal.GetAsSupports());
548   if (!go) {
549     aRv.Throw(NS_ERROR_UNEXPECTED);
550     return nullptr;
551   }
552 
553   RefPtr<Promise> p = Promise::Create(go, aRv);
554   if (aRv.Failed()) {
555     return nullptr;
556   }
557 
558   MediaStreamGraph* graph = MediaStreamGraph::GetInstanceIfExists(window);
559   if (!graph) {
560     p->MaybeResolve(0);
561     return p.forget();
562   }
563 
564   auto* graphImpl = static_cast<MediaStreamGraphImpl*>(graph);
565 
566   class Counter : public ControlMessage {
567    public:
568     Counter(MediaStreamGraphImpl* aGraph, const RefPtr<Promise>& aPromise)
569         : ControlMessage(nullptr),
570           mGraph(aGraph),
571           mPromise(MakeAndAddRef<nsMainThreadPtrHolder<Promise>>(
572               "DOMMediaStream::Counter::mPromise", aPromise)) {
573       MOZ_ASSERT(NS_IsMainThread());
574     }
575 
576     void Run() override {
577       nsMainThreadPtrHandle<Promise>& promise = mPromise;
578       uint32_t streams =
579           mGraph->mStreams.Length() + mGraph->mSuspendedStreams.Length();
580       mGraph->DispatchToMainThreadAfterStreamStateUpdate(
581           NewRunnableFrom([promise, streams]() mutable {
582             promise->MaybeResolve(streams);
583             return NS_OK;
584           }));
585     }
586 
587    private:
588     // mGraph owns this Counter instance and decides its lifetime.
589     MediaStreamGraphImpl* mGraph;
590     nsMainThreadPtrHandle<Promise> mPromise;
591   };
592   graphImpl->AppendMessage(MakeUnique<Counter>(graphImpl, p));
593 
594   return p.forget();
595 }
596 
GetId(nsAString & aID) const597 void DOMMediaStream::GetId(nsAString& aID) const { aID = mID; }
598 
GetAudioTracks(nsTArray<RefPtr<AudioStreamTrack>> & aTracks) const599 void DOMMediaStream::GetAudioTracks(
600     nsTArray<RefPtr<AudioStreamTrack>>& aTracks) const {
601   for (const RefPtr<TrackPort>& info : mTracks) {
602     AudioStreamTrack* t = info->GetTrack()->AsAudioStreamTrack();
603     if (t) {
604       aTracks.AppendElement(t);
605     }
606   }
607 }
608 
GetVideoTracks(nsTArray<RefPtr<VideoStreamTrack>> & aTracks) const609 void DOMMediaStream::GetVideoTracks(
610     nsTArray<RefPtr<VideoStreamTrack>>& aTracks) const {
611   for (const RefPtr<TrackPort>& info : mTracks) {
612     VideoStreamTrack* t = info->GetTrack()->AsVideoStreamTrack();
613     if (t) {
614       aTracks.AppendElement(t);
615     }
616   }
617 }
618 
GetTracks(nsTArray<RefPtr<MediaStreamTrack>> & aTracks) const619 void DOMMediaStream::GetTracks(
620     nsTArray<RefPtr<MediaStreamTrack>>& aTracks) const {
621   for (const RefPtr<TrackPort>& info : mTracks) {
622     aTracks.AppendElement(info->GetTrack());
623   }
624 }
625 
AddTrack(MediaStreamTrack & aTrack)626 void DOMMediaStream::AddTrack(MediaStreamTrack& aTrack) {
627   MOZ_RELEASE_ASSERT(mPlaybackStream);
628 
629   RefPtr<ProcessedMediaStream> dest = mPlaybackStream->AsProcessedStream();
630   MOZ_ASSERT(dest);
631   if (!dest) {
632     return;
633   }
634 
635   LOG(LogLevel::Info,
636       ("DOMMediaStream %p Adding track %p (from stream %p with ID %d)", this,
637        &aTrack, aTrack.mOwningStream.get(), aTrack.mTrackID));
638 
639   if (mPlaybackStream->Graph() != aTrack.Graph()) {
640     NS_ASSERTION(false,
641                  "Cannot combine tracks from different MediaStreamGraphs");
642     LOG(LogLevel::Error, ("DOMMediaStream %p Own MSG %p != aTrack's MSG %p",
643                           this, mPlaybackStream->Graph(), aTrack.Graph()));
644 
645     nsAutoString trackId;
646     aTrack.GetId(trackId);
647     const char16_t* params[] = {trackId.get()};
648     nsCOMPtr<nsPIDOMWindowInner> pWindow = GetParentObject();
649     nsIDocument* document = pWindow ? pWindow->GetExtantDoc() : nullptr;
650     nsContentUtils::ReportToConsole(nsIScriptError::errorFlag,
651                                     NS_LITERAL_CSTRING("Media"), document,
652                                     nsContentUtils::eDOM_PROPERTIES,
653                                     "MediaStreamAddTrackDifferentAudioChannel",
654                                     params, ArrayLength(params));
655     return;
656   }
657 
658   if (HasTrack(aTrack)) {
659     LOG(LogLevel::Debug,
660         ("DOMMediaStream %p already contains track %p", this, &aTrack));
661     return;
662   }
663 
664   // Hook up the underlying track with our underlying playback stream.
665   RefPtr<MediaInputPort> inputPort = GetPlaybackStream()->AllocateInputPort(
666       aTrack.GetOwnedStream(), aTrack.mTrackID);
667   RefPtr<TrackPort> trackPort =
668       new TrackPort(inputPort, &aTrack, TrackPort::InputPortOwnership::OWNED);
669   mTracks.AppendElement(trackPort.forget());
670   NotifyTrackAdded(&aTrack);
671 
672   LOG(LogLevel::Debug, ("DOMMediaStream %p Added track %p", this, &aTrack));
673 }
674 
RemoveTrack(MediaStreamTrack & aTrack)675 void DOMMediaStream::RemoveTrack(MediaStreamTrack& aTrack) {
676   LOG(LogLevel::Info,
677       ("DOMMediaStream %p Removing track %p (from stream %p with ID %d)", this,
678        &aTrack, aTrack.mOwningStream.get(), aTrack.mTrackID));
679 
680   RefPtr<TrackPort> toRemove = FindPlaybackTrackPort(aTrack);
681   if (!toRemove) {
682     LOG(LogLevel::Debug,
683         ("DOMMediaStream %p does not contain track %p", this, &aTrack));
684     return;
685   }
686 
687   DebugOnly<bool> removed = mTracks.RemoveElement(toRemove);
688   NS_ASSERTION(removed,
689                "If there's a track port we should be able to remove it");
690 
691   // If the track comes from a TRACK_ANY input port (i.e., mOwnedPort), we need
692   // to block it in the port. Doing this for a locked track is still OK as it
693   // will first block the track, then destroy the port. Both cause the track to
694   // end.
695   // If the track has already ended, it's input port might be gone, so in those
696   // cases blocking the underlying track should be avoided.
697   if (!aTrack.Ended()) {
698     BlockPlaybackTrack(toRemove);
699     NotifyTrackRemoved(&aTrack);
700   }
701 
702   LOG(LogLevel::Debug, ("DOMMediaStream %p Removed track %p", this, &aTrack));
703 }
704 
705 class ClonedStreamSourceGetter : public MediaStreamTrackSourceGetter {
706  public:
707   NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ClonedStreamSourceGetter,MediaStreamTrackSourceGetter)708   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ClonedStreamSourceGetter,
709                                            MediaStreamTrackSourceGetter)
710 
711   explicit ClonedStreamSourceGetter(DOMMediaStream* aStream)
712       : mStream(aStream) {}
713 
GetMediaStreamTrackSource(TrackID aInputTrackID)714   already_AddRefed<MediaStreamTrackSource> GetMediaStreamTrackSource(
715       TrackID aInputTrackID) override {
716     MediaStreamTrack* sourceTrack =
717         mStream->FindOwnedDOMTrack(mStream->GetOwnedStream(), aInputTrackID);
718     MOZ_RELEASE_ASSERT(sourceTrack);
719 
720     return do_AddRef(&sourceTrack->GetSource());
721   }
722 
723  protected:
~ClonedStreamSourceGetter()724   virtual ~ClonedStreamSourceGetter() {}
725 
726   RefPtr<DOMMediaStream> mStream;
727 };
728 
NS_IMPL_ADDREF_INHERITED(ClonedStreamSourceGetter,MediaStreamTrackSourceGetter)729 NS_IMPL_ADDREF_INHERITED(ClonedStreamSourceGetter, MediaStreamTrackSourceGetter)
730 NS_IMPL_RELEASE_INHERITED(ClonedStreamSourceGetter,
731                           MediaStreamTrackSourceGetter)
732 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ClonedStreamSourceGetter)
733 NS_INTERFACE_MAP_END_INHERITING(MediaStreamTrackSourceGetter)
734 NS_IMPL_CYCLE_COLLECTION_INHERITED(ClonedStreamSourceGetter,
735                                    MediaStreamTrackSourceGetter, mStream)
736 
737 already_AddRefed<DOMMediaStream> DOMMediaStream::Clone() {
738   return CloneInternal(TrackForwardingOption::CURRENT);
739 }
740 
CloneInternal(TrackForwardingOption aForwarding)741 already_AddRefed<DOMMediaStream> DOMMediaStream::CloneInternal(
742     TrackForwardingOption aForwarding) {
743   RefPtr<DOMMediaStream> newStream =
744       new DOMMediaStream(GetParentObject(), new ClonedStreamSourceGetter(this));
745 
746   LOG(LogLevel::Info,
747       ("DOMMediaStream %p created clone %p, forwarding %s tracks", this,
748        newStream.get(),
749        aForwarding == TrackForwardingOption::ALL ? "all" : "current"));
750 
751   MOZ_RELEASE_ASSERT(mPlaybackStream);
752   MOZ_RELEASE_ASSERT(mPlaybackStream->Graph());
753   MediaStreamGraph* graph = mPlaybackStream->Graph();
754 
755   // We initiate the owned and playback streams first, since we need to create
756   // all existing DOM tracks before we add the generic input port from
757   // mInputStream to mOwnedStream (see AllocateInputPort wrt. destination
758   // TrackID as to why).
759   newStream->InitOwnedStreamCommon(graph);
760   newStream->InitPlaybackStreamCommon(graph);
761 
762   // Set up existing DOM tracks.
763   TrackID allocatedTrackID = 1;
764   for (const RefPtr<TrackPort>& info : mTracks) {
765     MediaStreamTrack& track = *info->GetTrack();
766 
767     LOG(LogLevel::Debug,
768         ("DOMMediaStream %p forwarding external track %p to clone %p", this,
769          &track, newStream.get()));
770     RefPtr<MediaStreamTrack> trackClone =
771         newStream->CloneDOMTrack(track, allocatedTrackID++);
772   }
773 
774   if (aForwarding == TrackForwardingOption::ALL) {
775     // Set up an input port from our input stream to the new DOM stream's owned
776     // stream, to allow for dynamically added tracks at the source to appear in
777     // the clone. The clone may treat mInputStream as its own mInputStream but
778     // ownership remains with us.
779     newStream->mInputStream = mInputStream;
780     if (mInputStream) {
781       // We have already set up track-locked input ports for all existing DOM
782       // tracks, so now we need to block those in the generic input port to
783       // avoid ending up with double instances of them.
784       nsTArray<TrackID> tracksToBlock;
785       for (const RefPtr<TrackPort>& info : mOwnedTracks) {
786         tracksToBlock.AppendElement(info->GetTrack()->mTrackID);
787       }
788 
789       newStream->mInputStream->RegisterUser();
790       newStream->mOwnedPort = newStream->mOwnedStream->AllocateInputPort(
791           mInputStream, TRACK_ANY, TRACK_ANY, 0, 0, &tracksToBlock);
792     }
793   }
794 
795   return newStream.forget();
796 }
797 
Active() const798 bool DOMMediaStream::Active() const { return mActive; }
799 
GetTrackById(const nsAString & aId) const800 MediaStreamTrack* DOMMediaStream::GetTrackById(const nsAString& aId) const {
801   for (const RefPtr<TrackPort>& info : mTracks) {
802     nsString id;
803     info->GetTrack()->GetId(id);
804     if (id == aId) {
805       return info->GetTrack();
806     }
807   }
808   return nullptr;
809 }
810 
GetOwnedTrackById(const nsAString & aId)811 MediaStreamTrack* DOMMediaStream::GetOwnedTrackById(const nsAString& aId) {
812   for (const RefPtr<TrackPort>& info : mOwnedTracks) {
813     nsString id;
814     info->GetTrack()->GetId(id);
815     if (id == aId) {
816       return info->GetTrack();
817     }
818   }
819   return nullptr;
820 }
821 
HasTrack(const MediaStreamTrack & aTrack) const822 bool DOMMediaStream::HasTrack(const MediaStreamTrack& aTrack) const {
823   return !!FindPlaybackTrackPort(aTrack);
824 }
825 
OwnsTrack(const MediaStreamTrack & aTrack) const826 bool DOMMediaStream::OwnsTrack(const MediaStreamTrack& aTrack) const {
827   return !!FindOwnedTrackPort(aTrack);
828 }
829 
IsFinished() const830 bool DOMMediaStream::IsFinished() const {
831   return !mPlaybackStream || mPlaybackStream->IsFinished();
832 }
833 
GraphRate()834 TrackRate DOMMediaStream::GraphRate() {
835   if (mPlaybackStream) {
836     return mPlaybackStream->GraphRate();
837   }
838   if (mOwnedStream) {
839     return mOwnedStream->GraphRate();
840   }
841   if (mInputStream) {
842     return mInputStream->GraphRate();
843   }
844 
845   MOZ_ASSERT(false, "Not hooked up to a graph");
846   return 0;
847 }
848 
SetInactiveOnFinish()849 void DOMMediaStream::SetInactiveOnFinish() { mSetInactiveOnFinish = true; }
850 
InitSourceStream(MediaStreamGraph * aGraph)851 void DOMMediaStream::InitSourceStream(MediaStreamGraph* aGraph) {
852   InitInputStreamCommon(aGraph->CreateSourceStream(), aGraph);
853   InitOwnedStreamCommon(aGraph);
854   InitPlaybackStreamCommon(aGraph);
855 }
856 
InitTrackUnionStream(MediaStreamGraph * aGraph)857 void DOMMediaStream::InitTrackUnionStream(MediaStreamGraph* aGraph) {
858   InitInputStreamCommon(aGraph->CreateTrackUnionStream(), aGraph);
859   InitOwnedStreamCommon(aGraph);
860   InitPlaybackStreamCommon(aGraph);
861 }
862 
InitAudioCaptureStream(nsIPrincipal * aPrincipal,MediaStreamGraph * aGraph)863 void DOMMediaStream::InitAudioCaptureStream(nsIPrincipal* aPrincipal,
864                                             MediaStreamGraph* aGraph) {
865   const TrackID AUDIO_TRACK = 1;
866 
867   RefPtr<BasicTrackSource> audioCaptureSource =
868       new BasicTrackSource(aPrincipal, MediaSourceEnum::AudioCapture);
869 
870   AudioCaptureStream* audioCaptureStream = static_cast<AudioCaptureStream*>(
871       aGraph->CreateAudioCaptureStream(AUDIO_TRACK));
872   InitInputStreamCommon(audioCaptureStream, aGraph);
873   InitOwnedStreamCommon(aGraph);
874   InitPlaybackStreamCommon(aGraph);
875   RefPtr<MediaStreamTrack> track =
876       CreateDOMTrack(AUDIO_TRACK, MediaSegment::AUDIO, audioCaptureSource);
877   AddTrackInternal(track);
878 
879   audioCaptureStream->Start();
880 }
881 
InitInputStreamCommon(MediaStream * aStream,MediaStreamGraph * aGraph)882 void DOMMediaStream::InitInputStreamCommon(MediaStream* aStream,
883                                            MediaStreamGraph* aGraph) {
884   MOZ_ASSERT(!mOwnedStream,
885              "Input stream must be initialized before owned stream");
886 
887   mInputStream = aStream;
888   mInputStream->RegisterUser();
889 }
890 
InitOwnedStreamCommon(MediaStreamGraph * aGraph)891 void DOMMediaStream::InitOwnedStreamCommon(MediaStreamGraph* aGraph) {
892   MOZ_ASSERT(!mPlaybackStream,
893              "Owned stream must be initialized before playback stream");
894 
895   mOwnedStream = aGraph->CreateTrackUnionStream();
896   mOwnedStream->QueueSetAutofinish(true);
897   mOwnedStream->RegisterUser();
898   if (mInputStream) {
899     mOwnedPort = mOwnedStream->AllocateInputPort(mInputStream);
900   }
901 
902   // Setup track listeners
903   mOwnedListener = new OwnedStreamListener(this);
904   mOwnedStream->AddListener(mOwnedListener);
905 }
906 
InitPlaybackStreamCommon(MediaStreamGraph * aGraph)907 void DOMMediaStream::InitPlaybackStreamCommon(MediaStreamGraph* aGraph) {
908   mPlaybackStream = aGraph->CreateTrackUnionStream();
909   mPlaybackStream->QueueSetAutofinish(true);
910   mPlaybackStream->RegisterUser();
911   if (mOwnedStream) {
912     mPlaybackPort = mPlaybackStream->AllocateInputPort(mOwnedStream);
913   }
914 
915   mPlaybackListener = new PlaybackStreamListener(this);
916   mPlaybackStream->AddListener(mPlaybackListener);
917 
918   LOG(LogLevel::Debug, ("DOMMediaStream %p Initiated with mInputStream=%p, "
919                         "mOwnedStream=%p, mPlaybackStream=%p",
920                         this, mInputStream, mOwnedStream, mPlaybackStream));
921 }
922 
CreateSourceStreamAsInput(nsPIDOMWindowInner * aWindow,MediaStreamGraph * aGraph,MediaStreamTrackSourceGetter * aTrackSourceGetter)923 already_AddRefed<DOMMediaStream> DOMMediaStream::CreateSourceStreamAsInput(
924     nsPIDOMWindowInner* aWindow, MediaStreamGraph* aGraph,
925     MediaStreamTrackSourceGetter* aTrackSourceGetter) {
926   RefPtr<DOMMediaStream> stream =
927       new DOMMediaStream(aWindow, aTrackSourceGetter);
928   stream->InitSourceStream(aGraph);
929   return stream.forget();
930 }
931 
CreateTrackUnionStreamAsInput(nsPIDOMWindowInner * aWindow,MediaStreamGraph * aGraph,MediaStreamTrackSourceGetter * aTrackSourceGetter)932 already_AddRefed<DOMMediaStream> DOMMediaStream::CreateTrackUnionStreamAsInput(
933     nsPIDOMWindowInner* aWindow, MediaStreamGraph* aGraph,
934     MediaStreamTrackSourceGetter* aTrackSourceGetter) {
935   RefPtr<DOMMediaStream> stream =
936       new DOMMediaStream(aWindow, aTrackSourceGetter);
937   stream->InitTrackUnionStream(aGraph);
938   return stream.forget();
939 }
940 
941 already_AddRefed<DOMMediaStream>
CreateAudioCaptureStreamAsInput(nsPIDOMWindowInner * aWindow,nsIPrincipal * aPrincipal,MediaStreamGraph * aGraph)942 DOMMediaStream::CreateAudioCaptureStreamAsInput(nsPIDOMWindowInner* aWindow,
943                                                 nsIPrincipal* aPrincipal,
944                                                 MediaStreamGraph* aGraph) {
945   // Audio capture doesn't create tracks dynamically
946   MediaStreamTrackSourceGetter* getter = nullptr;
947   RefPtr<DOMMediaStream> stream = new DOMMediaStream(aWindow, getter);
948   stream->InitAudioCaptureStream(aPrincipal, aGraph);
949   return stream.forget();
950 }
951 
PrincipalChanged(MediaStreamTrack * aTrack)952 void DOMMediaStream::PrincipalChanged(MediaStreamTrack* aTrack) {
953   MOZ_ASSERT(aTrack);
954   NS_ASSERTION(HasTrack(*aTrack), "Principal changed for an unknown track");
955   LOG(LogLevel::Info,
956       ("DOMMediaStream %p Principal changed for track %p", this, aTrack));
957   RecomputePrincipal();
958 }
959 
RecomputePrincipal()960 void DOMMediaStream::RecomputePrincipal() {
961   nsCOMPtr<nsIPrincipal> previousPrincipal = mPrincipal.forget();
962   nsCOMPtr<nsIPrincipal> previousVideoPrincipal = mVideoPrincipal.forget();
963 
964   if (mTracksPendingRemoval > 0) {
965     LOG(LogLevel::Info, ("DOMMediaStream %p RecomputePrincipal() Cannot "
966                          "recompute stream principal with tracks pending "
967                          "removal.",
968                          this));
969     return;
970   }
971 
972   LOG(LogLevel::Debug, ("DOMMediaStream %p Recomputing principal. "
973                         "Old principal was %p.",
974                         this, previousPrincipal.get()));
975 
976   // mPrincipal is recomputed based on all current tracks, and tracks that have
977   // not ended in our playback stream.
978   for (const RefPtr<TrackPort>& info : mTracks) {
979     if (info->GetTrack()->Ended()) {
980       continue;
981     }
982     LOG(LogLevel::Debug,
983         ("DOMMediaStream %p Taking live track %p with "
984          "principal %p into account.",
985          this, info->GetTrack(), info->GetTrack()->GetPrincipal()));
986     nsContentUtils::CombineResourcePrincipals(&mPrincipal,
987                                               info->GetTrack()->GetPrincipal());
988     if (info->GetTrack()->AsVideoStreamTrack()) {
989       nsContentUtils::CombineResourcePrincipals(
990           &mVideoPrincipal, info->GetTrack()->GetPrincipal());
991     }
992   }
993 
994   LOG(LogLevel::Debug,
995       ("DOMMediaStream %p new principal is %p.", this, mPrincipal.get()));
996 
997   if (previousPrincipal != mPrincipal ||
998       previousVideoPrincipal != mVideoPrincipal) {
999     NotifyPrincipalChanged();
1000   }
1001 }
1002 
NotifyPrincipalChanged()1003 void DOMMediaStream::NotifyPrincipalChanged() {
1004   if (!mPrincipal) {
1005     // When all tracks are removed, mPrincipal will change to nullptr.
1006     LOG(LogLevel::Info,
1007         ("DOMMediaStream %p Principal changed to nothing.", this));
1008   } else {
1009     LOG(LogLevel::Info, ("DOMMediaStream %p Principal changed. Now: "
1010                          "null=%d, codebase=%d, expanded=%d, system=%d",
1011                          this, mPrincipal->GetIsNullPrincipal(),
1012                          mPrincipal->GetIsCodebasePrincipal(),
1013                          mPrincipal->GetIsExpandedPrincipal(),
1014                          mPrincipal->GetIsSystemPrincipal()));
1015   }
1016 
1017   for (uint32_t i = 0; i < mPrincipalChangeObservers.Length(); ++i) {
1018     mPrincipalChangeObservers[i]->PrincipalChanged(this);
1019   }
1020 }
1021 
AddPrincipalChangeObserver(PrincipalChangeObserver<DOMMediaStream> * aObserver)1022 bool DOMMediaStream::AddPrincipalChangeObserver(
1023     PrincipalChangeObserver<DOMMediaStream>* aObserver) {
1024   return mPrincipalChangeObservers.AppendElement(aObserver) != nullptr;
1025 }
1026 
RemovePrincipalChangeObserver(PrincipalChangeObserver<DOMMediaStream> * aObserver)1027 bool DOMMediaStream::RemovePrincipalChangeObserver(
1028     PrincipalChangeObserver<DOMMediaStream>* aObserver) {
1029   return mPrincipalChangeObservers.RemoveElement(aObserver);
1030 }
1031 
AddTrackInternal(MediaStreamTrack * aTrack)1032 void DOMMediaStream::AddTrackInternal(MediaStreamTrack* aTrack) {
1033   MOZ_ASSERT(aTrack->mOwningStream == this);
1034   MOZ_ASSERT(FindOwnedDOMTrack(aTrack->GetInputStream(), aTrack->mInputTrackID,
1035                                aTrack->mTrackID));
1036   MOZ_ASSERT(!FindPlaybackDOMTrack(aTrack->GetOwnedStream(), aTrack->mTrackID));
1037 
1038   LOG(LogLevel::Debug,
1039       ("DOMMediaStream %p Adding owned track %p", this, aTrack));
1040 
1041   mTracks.AppendElement(new TrackPort(mPlaybackPort, aTrack,
1042                                       TrackPort::InputPortOwnership::EXTERNAL));
1043 
1044   NotifyTrackAdded(aTrack);
1045 
1046   DispatchTrackEvent(NS_LITERAL_STRING("addtrack"), aTrack);
1047 }
1048 
CreateDOMTrack(TrackID aTrackID,MediaSegment::Type aType,MediaStreamTrackSource * aSource,const MediaTrackConstraints & aConstraints)1049 already_AddRefed<MediaStreamTrack> DOMMediaStream::CreateDOMTrack(
1050     TrackID aTrackID, MediaSegment::Type aType, MediaStreamTrackSource* aSource,
1051     const MediaTrackConstraints& aConstraints) {
1052   MOZ_RELEASE_ASSERT(mInputStream);
1053   MOZ_RELEASE_ASSERT(mOwnedStream);
1054 
1055   MOZ_ASSERT(FindOwnedDOMTrack(GetInputStream(), aTrackID) == nullptr);
1056 
1057   RefPtr<MediaStreamTrack> track;
1058   switch (aType) {
1059     case MediaSegment::AUDIO:
1060       track =
1061           new AudioStreamTrack(this, aTrackID, aTrackID, aSource, aConstraints);
1062       break;
1063     case MediaSegment::VIDEO:
1064       track =
1065           new VideoStreamTrack(this, aTrackID, aTrackID, aSource, aConstraints);
1066       break;
1067     default:
1068       MOZ_CRASH("Unhandled track type");
1069   }
1070 
1071   LOG(LogLevel::Debug, ("DOMMediaStream %p Created new track %p with ID %u",
1072                         this, track.get(), aTrackID));
1073 
1074   mOwnedTracks.AppendElement(new TrackPort(
1075       mOwnedPort, track, TrackPort::InputPortOwnership::EXTERNAL));
1076 
1077   return track.forget();
1078 }
1079 
CloneDOMTrack(MediaStreamTrack & aTrack,TrackID aCloneTrackID)1080 already_AddRefed<MediaStreamTrack> DOMMediaStream::CloneDOMTrack(
1081     MediaStreamTrack& aTrack, TrackID aCloneTrackID) {
1082   MOZ_RELEASE_ASSERT(mOwnedStream);
1083   MOZ_RELEASE_ASSERT(mPlaybackStream);
1084   MOZ_RELEASE_ASSERT(IsTrackIDExplicit(aCloneTrackID));
1085 
1086   TrackID inputTrackID = aTrack.mInputTrackID;
1087   MediaStream* inputStream = aTrack.GetInputStream();
1088 
1089   RefPtr<MediaStreamTrack> newTrack = aTrack.CloneInternal(this, aCloneTrackID);
1090 
1091   newTrack->mOriginalTrack =
1092       aTrack.mOriginalTrack ? aTrack.mOriginalTrack.get() : &aTrack;
1093 
1094   LOG(LogLevel::Debug,
1095       ("DOMMediaStream %p Created new track %p cloned from stream %p track %d",
1096        this, newTrack.get(), inputStream, inputTrackID));
1097 
1098   RefPtr<MediaInputPort> inputPort =
1099       mOwnedStream->AllocateInputPort(inputStream, inputTrackID, aCloneTrackID);
1100 
1101   mOwnedTracks.AppendElement(
1102       new TrackPort(inputPort, newTrack, TrackPort::InputPortOwnership::OWNED));
1103 
1104   mTracks.AppendElement(new TrackPort(mPlaybackPort, newTrack,
1105                                       TrackPort::InputPortOwnership::EXTERNAL));
1106 
1107   NotifyTrackAdded(newTrack);
1108 
1109   newTrack->SetEnabled(aTrack.Enabled());
1110   newTrack->SetMuted(aTrack.Muted());
1111   newTrack->SetReadyState(aTrack.ReadyState());
1112 
1113   if (aTrack.Ended()) {
1114     // For extra suspenders, make sure that we don't forward data by mistake
1115     // to the clone when the original has already ended.
1116     // We only block END_EXISTING to allow any pending clones to end.
1117     RefPtr<Pledge<bool, nsresult>> blockingPledge =
1118         inputPort->BlockSourceTrackId(inputTrackID, BlockingMode::END_EXISTING);
1119     Unused << blockingPledge;
1120   }
1121 
1122   return newTrack.forget();
1123 }
1124 
FindTrackPortAmongTracks(const MediaStreamTrack & aTrack,const nsTArray<RefPtr<DOMMediaStream::TrackPort>> & aTracks)1125 static DOMMediaStream::TrackPort* FindTrackPortAmongTracks(
1126     const MediaStreamTrack& aTrack,
1127     const nsTArray<RefPtr<DOMMediaStream::TrackPort>>& aTracks) {
1128   for (const RefPtr<DOMMediaStream::TrackPort>& info : aTracks) {
1129     if (info->GetTrack() == &aTrack) {
1130       return info;
1131     }
1132   }
1133   return nullptr;
1134 }
1135 
FindOwnedDOMTrack(MediaStream * aInputStream,TrackID aInputTrackID,TrackID aTrackID) const1136 MediaStreamTrack* DOMMediaStream::FindOwnedDOMTrack(MediaStream* aInputStream,
1137                                                     TrackID aInputTrackID,
1138                                                     TrackID aTrackID) const {
1139   MOZ_RELEASE_ASSERT(mOwnedStream);
1140 
1141   for (const RefPtr<TrackPort>& info : mOwnedTracks) {
1142     if (info->GetInputPort() &&
1143         info->GetInputPort()->GetSource() == aInputStream &&
1144         info->GetTrack()->mInputTrackID == aInputTrackID &&
1145         (aTrackID == TRACK_ANY || info->GetTrack()->mTrackID == aTrackID)) {
1146       // This track is owned externally but in our playback stream.
1147       return info->GetTrack();
1148     }
1149   }
1150   return nullptr;
1151 }
1152 
FindOwnedTrackPort(const MediaStreamTrack & aTrack) const1153 DOMMediaStream::TrackPort* DOMMediaStream::FindOwnedTrackPort(
1154     const MediaStreamTrack& aTrack) const {
1155   return FindTrackPortAmongTracks(aTrack, mOwnedTracks);
1156 }
1157 
FindPlaybackDOMTrack(MediaStream * aInputStream,TrackID aInputTrackID) const1158 MediaStreamTrack* DOMMediaStream::FindPlaybackDOMTrack(
1159     MediaStream* aInputStream, TrackID aInputTrackID) const {
1160   if (!mPlaybackStream) {
1161     // One would think we can assert mPlaybackStream here, but track clones have
1162     // a dummy DOMMediaStream that doesn't have a playback stream, so we can't.
1163     return nullptr;
1164   }
1165 
1166   for (const RefPtr<TrackPort>& info : mTracks) {
1167     if (info->GetInputPort() == mPlaybackPort && aInputStream == mOwnedStream &&
1168         info->GetTrack()->mInputTrackID == aInputTrackID) {
1169       // This track is in our owned and playback streams.
1170       return info->GetTrack();
1171     }
1172     if (info->GetInputPort() &&
1173         info->GetInputPort()->GetSource() == aInputStream &&
1174         info->GetSourceTrackId() == aInputTrackID) {
1175       // This track is owned externally but in our playback stream.
1176       MOZ_ASSERT(IsTrackIDExplicit(aInputTrackID));
1177       return info->GetTrack();
1178     }
1179   }
1180   return nullptr;
1181 }
1182 
FindPlaybackTrackPort(const MediaStreamTrack & aTrack) const1183 DOMMediaStream::TrackPort* DOMMediaStream::FindPlaybackTrackPort(
1184     const MediaStreamTrack& aTrack) const {
1185   return FindTrackPortAmongTracks(aTrack, mTracks);
1186 }
1187 
OnTracksAvailable(OnTracksAvailableCallback * aRunnable)1188 void DOMMediaStream::OnTracksAvailable(OnTracksAvailableCallback* aRunnable) {
1189   if (mNotifiedOfMediaStreamGraphShutdown) {
1190     // No more tracks will ever be added, so just delete the callback now.
1191     delete aRunnable;
1192     return;
1193   }
1194   mRunOnTracksAvailable.AppendElement(aRunnable);
1195   CheckTracksAvailable();
1196 }
1197 
NotifyTracksCreated()1198 void DOMMediaStream::NotifyTracksCreated() {
1199   mTracksCreated = true;
1200   CheckTracksAvailable();
1201 }
1202 
NotifyFinished()1203 void DOMMediaStream::NotifyFinished() {
1204   if (!mSetInactiveOnFinish) {
1205     return;
1206   }
1207 
1208   if (!mActive) {
1209     // This can happen if the stream never became active.
1210     return;
1211   }
1212 
1213   MOZ_ASSERT(!ContainsLiveTracks(mTracks));
1214   mActive = false;
1215   NotifyInactive();
1216 }
1217 
NotifyActive()1218 void DOMMediaStream::NotifyActive() {
1219   LOG(LogLevel::Info, ("DOMMediaStream %p NotifyActive(). ", this));
1220 
1221   MOZ_ASSERT(mActive);
1222   for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
1223     mTrackListeners[i]->NotifyActive();
1224   }
1225 }
1226 
NotifyInactive()1227 void DOMMediaStream::NotifyInactive() {
1228   LOG(LogLevel::Info, ("DOMMediaStream %p NotifyInactive(). ", this));
1229 
1230   MOZ_ASSERT(!mActive);
1231   for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
1232     mTrackListeners[i]->NotifyInactive();
1233   }
1234 }
1235 
CheckTracksAvailable()1236 void DOMMediaStream::CheckTracksAvailable() {
1237   if (!mTracksCreated) {
1238     return;
1239   }
1240   nsTArray<nsAutoPtr<OnTracksAvailableCallback>> callbacks;
1241   callbacks.SwapElements(mRunOnTracksAvailable);
1242 
1243   for (uint32_t i = 0; i < callbacks.Length(); ++i) {
1244     callbacks[i]->NotifyTracksAvailable(this);
1245   }
1246 }
1247 
RegisterTrackListener(TrackListener * aListener)1248 void DOMMediaStream::RegisterTrackListener(TrackListener* aListener) {
1249   MOZ_ASSERT(NS_IsMainThread());
1250 
1251   if (mNotifiedOfMediaStreamGraphShutdown) {
1252     // No more tracks will ever be added, so just do nothing.
1253     return;
1254   }
1255   mTrackListeners.AppendElement(aListener);
1256 }
1257 
UnregisterTrackListener(TrackListener * aListener)1258 void DOMMediaStream::UnregisterTrackListener(TrackListener* aListener) {
1259   MOZ_ASSERT(NS_IsMainThread());
1260   mTrackListeners.RemoveElement(aListener);
1261 }
1262 
NotifyTrackAdded(const RefPtr<MediaStreamTrack> & aTrack)1263 void DOMMediaStream::NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) {
1264   MOZ_ASSERT(NS_IsMainThread());
1265 
1266   if (mTracksPendingRemoval > 0) {
1267     // If there are tracks pending removal we may not degrade the current
1268     // principals until those tracks have been confirmed removed from the
1269     // playback stream. Instead combine with the new track and the (potentially)
1270     // degraded principal will be calculated when it's safe.
1271     nsContentUtils::CombineResourcePrincipals(&mPrincipal,
1272                                               aTrack->GetPrincipal());
1273     LOG(LogLevel::Debug, ("DOMMediaStream %p saw a track get added. Combining "
1274                           "its principal %p into our while waiting for pending "
1275                           "tracks to be removed. New principal is %p.",
1276                           this, aTrack->GetPrincipal(), mPrincipal.get()));
1277     if (aTrack->AsVideoStreamTrack()) {
1278       nsContentUtils::CombineResourcePrincipals(&mVideoPrincipal,
1279                                                 aTrack->GetPrincipal());
1280     }
1281   } else {
1282     LOG(LogLevel::Debug, ("DOMMediaStream %p saw a track get added. "
1283                           "Recomputing principal.",
1284                           this));
1285     RecomputePrincipal();
1286   }
1287 
1288   aTrack->AddPrincipalChangeObserver(this);
1289   aTrack->AddConsumer(mPlaybackTrackListener);
1290 
1291   for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
1292     mTrackListeners[i]->NotifyTrackAdded(aTrack);
1293   }
1294 
1295   if (mActive) {
1296     return;
1297   }
1298 
1299   // Check if we became active.
1300   if (ContainsLiveTracks(mTracks)) {
1301     mActive = true;
1302     NotifyActive();
1303   }
1304 }
1305 
NotifyTrackRemoved(const RefPtr<MediaStreamTrack> & aTrack)1306 void DOMMediaStream::NotifyTrackRemoved(
1307     const RefPtr<MediaStreamTrack>& aTrack) {
1308   MOZ_ASSERT(NS_IsMainThread());
1309 
1310   aTrack->RemoveConsumer(mPlaybackTrackListener);
1311   aTrack->RemovePrincipalChangeObserver(this);
1312 
1313   for (int32_t i = mTrackListeners.Length() - 1; i >= 0; --i) {
1314     mTrackListeners[i]->NotifyTrackRemoved(aTrack);
1315   }
1316 
1317   // Don't call RecomputePrincipal here as the track may still exist in the
1318   // playback stream in the MediaStreamGraph. It will instead be called when the
1319   // track has been confirmed removed by the graph. See BlockPlaybackTrack().
1320 
1321   if (!mActive) {
1322     NS_ASSERTION(false, "Shouldn't remove a live track if already inactive");
1323     return;
1324   }
1325 
1326   if (mSetInactiveOnFinish) {
1327     // For compatibility with mozCaptureStream we in some cases do not go
1328     // inactive until the playback stream finishes.
1329     return;
1330   }
1331 
1332   // Check if we became inactive.
1333   if (!ContainsLiveTracks(mTracks)) {
1334     mActive = false;
1335     NotifyInactive();
1336   }
1337 }
1338 
DispatchTrackEvent(const nsAString & aName,const RefPtr<MediaStreamTrack> & aTrack)1339 nsresult DOMMediaStream::DispatchTrackEvent(
1340     const nsAString& aName, const RefPtr<MediaStreamTrack>& aTrack) {
1341   MediaStreamTrackEventInit init;
1342   init.mTrack = aTrack;
1343 
1344   RefPtr<MediaStreamTrackEvent> event =
1345       MediaStreamTrackEvent::Constructor(this, aName, init);
1346 
1347   return DispatchTrustedEvent(event);
1348 }
1349 
BlockPlaybackTrack(TrackPort * aTrack)1350 void DOMMediaStream::BlockPlaybackTrack(TrackPort* aTrack) {
1351   MOZ_ASSERT(aTrack);
1352   ++mTracksPendingRemoval;
1353   RefPtr<Pledge<bool>> p = aTrack->BlockSourceTrackId(
1354       aTrack->GetTrack()->mTrackID, BlockingMode::CREATION);
1355   RefPtr<DOMMediaStream> self = this;
1356   p->Then([self](const bool& aIgnore) { self->NotifyPlaybackTrackBlocked(); },
1357           [](const nsresult& aIgnore) {
1358             NS_ERROR("Could not remove track from MSG");
1359           });
1360 }
1361 
NotifyPlaybackTrackBlocked()1362 void DOMMediaStream::NotifyPlaybackTrackBlocked() {
1363   MOZ_ASSERT(mTracksPendingRemoval > 0,
1364              "A track reported finished blocking more times than we asked for");
1365   if (--mTracksPendingRemoval == 0) {
1366     // The MediaStreamGraph has reported a track was blocked and we are not
1367     // waiting for any further tracks to get blocked. It is now safe to
1368     // recompute the principal based on our main thread track set state.
1369     LOG(LogLevel::Debug, ("DOMMediaStream %p saw all tracks pending removal "
1370                           "finish. Recomputing principal.",
1371                           this));
1372     RecomputePrincipal();
1373   }
1374 }
1375 
~DOMLocalMediaStream()1376 DOMLocalMediaStream::~DOMLocalMediaStream() {
1377   if (mInputStream) {
1378     // Make sure Listeners of this stream know it's going away
1379     StopImpl();
1380   }
1381 }
1382 
WrapObject(JSContext * aCx,JS::Handle<JSObject * > aGivenProto)1383 JSObject* DOMLocalMediaStream::WrapObject(JSContext* aCx,
1384                                           JS::Handle<JSObject*> aGivenProto) {
1385   return dom::LocalMediaStreamBinding::Wrap(aCx, this, aGivenProto);
1386 }
1387 
Stop()1388 void DOMLocalMediaStream::Stop() {
1389   LOG(LogLevel::Debug, ("DOMMediaStream %p Stop()", this));
1390   nsCOMPtr<nsPIDOMWindowInner> pWindow = GetParentObject();
1391   nsIDocument* document = pWindow ? pWindow->GetExtantDoc() : nullptr;
1392   nsContentUtils::ReportToConsole(
1393       nsIScriptError::warningFlag, NS_LITERAL_CSTRING("Media"), document,
1394       nsContentUtils::eDOM_PROPERTIES, "MediaStreamStopDeprecatedWarning");
1395 
1396   StopImpl();
1397 }
1398 
StopImpl()1399 void DOMLocalMediaStream::StopImpl() {
1400   if (mInputStream && mInputStream->AsSourceStream()) {
1401     mInputStream->AsSourceStream()->EndAllTrackAndFinish();
1402   }
1403 }
1404 
1405 already_AddRefed<DOMLocalMediaStream>
CreateSourceStreamAsInput(nsPIDOMWindowInner * aWindow,MediaStreamGraph * aGraph,MediaStreamTrackSourceGetter * aTrackSourceGetter)1406 DOMLocalMediaStream::CreateSourceStreamAsInput(
1407     nsPIDOMWindowInner* aWindow, MediaStreamGraph* aGraph,
1408     MediaStreamTrackSourceGetter* aTrackSourceGetter) {
1409   RefPtr<DOMLocalMediaStream> stream =
1410       new DOMLocalMediaStream(aWindow, aTrackSourceGetter);
1411   stream->InitSourceStream(aGraph);
1412   return stream.forget();
1413 }
1414 
1415 already_AddRefed<DOMLocalMediaStream>
CreateTrackUnionStreamAsInput(nsPIDOMWindowInner * aWindow,MediaStreamGraph * aGraph,MediaStreamTrackSourceGetter * aTrackSourceGetter)1416 DOMLocalMediaStream::CreateTrackUnionStreamAsInput(
1417     nsPIDOMWindowInner* aWindow, MediaStreamGraph* aGraph,
1418     MediaStreamTrackSourceGetter* aTrackSourceGetter) {
1419   RefPtr<DOMLocalMediaStream> stream =
1420       new DOMLocalMediaStream(aWindow, aTrackSourceGetter);
1421   stream->InitTrackUnionStream(aGraph);
1422   return stream.forget();
1423 }
1424 
DOMAudioNodeMediaStream(nsPIDOMWindowInner * aWindow,AudioNode * aNode)1425 DOMAudioNodeMediaStream::DOMAudioNodeMediaStream(nsPIDOMWindowInner* aWindow,
1426                                                  AudioNode* aNode)
1427     : DOMMediaStream(aWindow, nullptr), mStreamNode(aNode) {}
1428 
~DOMAudioNodeMediaStream()1429 DOMAudioNodeMediaStream::~DOMAudioNodeMediaStream() {}
1430 
1431 already_AddRefed<DOMAudioNodeMediaStream>
CreateTrackUnionStreamAsInput(nsPIDOMWindowInner * aWindow,AudioNode * aNode,MediaStreamGraph * aGraph)1432 DOMAudioNodeMediaStream::CreateTrackUnionStreamAsInput(
1433     nsPIDOMWindowInner* aWindow, AudioNode* aNode, MediaStreamGraph* aGraph) {
1434   RefPtr<DOMAudioNodeMediaStream> stream =
1435       new DOMAudioNodeMediaStream(aWindow, aNode);
1436   stream->InitTrackUnionStream(aGraph);
1437   return stream.forget();
1438 }
1439