Bug 1772053 - Enable dynamic code disable mitigations only on Windows 10 1703+ r...
[gecko.git] / dom / media / MediaStreamTrack.cpp
blobd0550dfd5c5df01f66337460c953c03d337c362f
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/. */
6 #include "MediaStreamTrack.h"
8 #include "DOMMediaStream.h"
9 #include "MediaSegment.h"
10 #include "MediaStreamError.h"
11 #include "MediaTrackGraphImpl.h"
12 #include "MediaTrackListener.h"
13 #include "mozilla/BasePrincipal.h"
14 #include "mozilla/dom/Promise.h"
15 #include "nsContentUtils.h"
16 #include "nsGlobalWindowInner.h"
17 #include "nsIUUIDGenerator.h"
18 #include "nsServiceManagerUtils.h"
19 #include "systemservices/MediaUtils.h"
21 #ifdef LOG
22 # undef LOG
23 #endif
25 static mozilla::LazyLogModule gMediaStreamTrackLog("MediaStreamTrack");
26 #define LOG(type, msg) MOZ_LOG(gMediaStreamTrackLog, type, msg)
28 using namespace mozilla::media;
30 namespace mozilla::dom {
32 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaStreamTrackSource)
33 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaStreamTrackSource)
34 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamTrackSource)
35 NS_INTERFACE_MAP_ENTRY(nsISupports)
36 NS_INTERFACE_MAP_END
38 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamTrackSource)
40 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaStreamTrackSource)
41 tmp->Destroy();
42 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
43 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
45 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(MediaStreamTrackSource)
46 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
47 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
49 auto MediaStreamTrackSource::ApplyConstraints(
50 const dom::MediaTrackConstraints& aConstraints, CallerType aCallerType)
51 -> RefPtr<ApplyConstraintsPromise> {
52 return ApplyConstraintsPromise::CreateAndReject(
53 MakeRefPtr<MediaMgrError>(MediaMgrError::Name::OverconstrainedError, ""),
54 __func__);
57 /**
58 * MTGListener monitors state changes of the media flowing through the
59 * MediaTrackGraph.
62 * For changes to PrincipalHandle the following applies:
64 * When the main thread principal for a MediaStreamTrack changes, its principal
65 * will be set to the combination of the previous principal and the new one.
67 * As a PrincipalHandle change later happens on the MediaTrackGraph thread, we
68 * will be notified. If the latest principal on main thread matches the
69 * PrincipalHandle we just saw on MTG thread, we will set the track's principal
70 * to the new one.
72 * We know at this point that the old principal has been flushed out and data
73 * under it cannot leak to consumers.
75 * In case of multiple changes to the main thread state, the track's principal
76 * will be a combination of its old principal and all the new ones until the
77 * latest main thread principal matches the PrincipalHandle on the MTG thread.
79 class MediaStreamTrack::MTGListener : public MediaTrackListener {
80 public:
81 explicit MTGListener(MediaStreamTrack* aTrack) : mTrack(aTrack) {}
83 void DoNotifyPrincipalHandleChanged(
84 const PrincipalHandle& aNewPrincipalHandle) {
85 MOZ_ASSERT(NS_IsMainThread());
87 if (!mTrack) {
88 return;
91 mTrack->NotifyPrincipalHandleChanged(aNewPrincipalHandle);
94 void NotifyPrincipalHandleChanged(
95 MediaTrackGraph* aGraph,
96 const PrincipalHandle& aNewPrincipalHandle) override {
97 aGraph->DispatchToMainThreadStableState(
98 NewRunnableMethod<StoreCopyPassByConstLRef<PrincipalHandle>>(
99 "dom::MediaStreamTrack::MTGListener::"
100 "DoNotifyPrincipalHandleChanged",
101 this, &MTGListener::DoNotifyPrincipalHandleChanged,
102 aNewPrincipalHandle));
105 void NotifyRemoved(MediaTrackGraph* aGraph) override {
106 // `mTrack` is a WeakPtr and must be destroyed on main thread.
107 // We dispatch ourselves to main thread here in case the MediaTrackGraph
108 // is holding the last reference to us.
109 aGraph->DispatchToMainThreadStableState(
110 NS_NewRunnableFunction("MediaStreamTrack::MTGListener::mTrackReleaser",
111 [self = RefPtr<MTGListener>(this)]() {}));
114 void DoNotifyEnded() {
115 MOZ_ASSERT(NS_IsMainThread());
117 if (!mTrack) {
118 return;
121 if (!mTrack->GetParentObject()) {
122 return;
125 AbstractThread* mainThread =
126 nsGlobalWindowInner::Cast(mTrack->GetParentObject())
127 ->AbstractMainThreadFor(TaskCategory::Other);
128 mainThread->Dispatch(NewRunnableMethod("MediaStreamTrack::OverrideEnded",
129 mTrack.get(),
130 &MediaStreamTrack::OverrideEnded));
133 void NotifyEnded(MediaTrackGraph* aGraph) override {
134 aGraph->DispatchToMainThreadStableState(
135 NewRunnableMethod("MediaStreamTrack::MTGListener::DoNotifyEnded", this,
136 &MTGListener::DoNotifyEnded));
139 protected:
140 // Main thread only.
141 WeakPtr<MediaStreamTrack> mTrack;
144 class MediaStreamTrack::TrackSink : public MediaStreamTrackSource::Sink {
145 public:
146 explicit TrackSink(MediaStreamTrack* aTrack) : mTrack(aTrack) {}
149 * Keep the track source alive. This track and any clones are controlling the
150 * lifetime of the source by being registered as its sinks.
152 bool KeepsSourceAlive() const override { return true; }
154 bool Enabled() const override {
155 if (!mTrack) {
156 return false;
158 return mTrack->Enabled();
161 void PrincipalChanged() override {
162 if (mTrack) {
163 mTrack->PrincipalChanged();
167 void MutedChanged(bool aNewState) override {
168 if (mTrack) {
169 mTrack->MutedChanged(aNewState);
173 void OverrideEnded() override {
174 if (mTrack) {
175 mTrack->OverrideEnded();
179 private:
180 WeakPtr<MediaStreamTrack> mTrack;
183 MediaStreamTrack::MediaStreamTrack(nsPIDOMWindowInner* aWindow,
184 mozilla::MediaTrack* aInputTrack,
185 MediaStreamTrackSource* aSource,
186 MediaStreamTrackState aReadyState,
187 bool aMuted,
188 const MediaTrackConstraints& aConstraints)
189 : mWindow(aWindow),
190 mInputTrack(aInputTrack),
191 mSource(aSource),
192 mSink(MakeUnique<TrackSink>(this)),
193 mPrincipal(aSource->GetPrincipal()),
194 mReadyState(aReadyState),
195 mEnabled(true),
196 mMuted(aMuted),
197 mConstraints(aConstraints) {
198 if (!Ended()) {
199 GetSource().RegisterSink(mSink.get());
201 // Even if the input track is destroyed we need mTrack so that methods
202 // like AddListener still work. Keeping the number of paths to a minimum
203 // also helps prevent bugs elsewhere. We'll be ended through the
204 // MediaStreamTrackSource soon enough.
205 auto graph = mInputTrack->IsDestroyed()
206 ? MediaTrackGraph::GetInstanceIfExists(
207 mWindow, mInputTrack->mSampleRate,
208 MediaTrackGraph::DEFAULT_OUTPUT_DEVICE)
209 : mInputTrack->Graph();
210 MOZ_DIAGNOSTIC_ASSERT(graph,
211 "A destroyed input track is only expected when "
212 "cloning, but since we're live there must be another "
213 "live track that is keeping the graph alive");
215 mTrack = graph->CreateForwardedInputTrack(mInputTrack->mType);
216 mPort = mTrack->AllocateInputPort(mInputTrack);
217 mMTGListener = new MTGListener(this);
218 AddListener(mMTGListener);
221 nsresult rv;
222 nsCOMPtr<nsIUUIDGenerator> uuidgen =
223 do_GetService("@mozilla.org/uuid-generator;1", &rv);
225 nsID uuid;
226 memset(&uuid, 0, sizeof(uuid));
227 if (uuidgen) {
228 uuidgen->GenerateUUIDInPlace(&uuid);
231 char chars[NSID_LENGTH];
232 uuid.ToProvidedString(chars);
233 mID = NS_ConvertASCIItoUTF16(chars);
236 MediaStreamTrack::~MediaStreamTrack() { Destroy(); }
238 void MediaStreamTrack::Destroy() {
239 SetReadyState(MediaStreamTrackState::Ended);
240 // Remove all listeners -- avoid iterating over the list we're removing from
241 for (const auto& listener : mTrackListeners.Clone()) {
242 RemoveListener(listener);
244 // Do the same as above for direct listeners
245 for (const auto& listener : mDirectTrackListeners.Clone()) {
246 RemoveDirectListener(listener);
250 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamTrack)
252 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(MediaStreamTrack,
253 DOMEventTargetHelper)
254 tmp->Destroy();
255 NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
256 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSource)
257 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrincipal)
258 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPendingPrincipal)
259 NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
260 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
262 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamTrack,
263 DOMEventTargetHelper)
264 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
265 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSource)
266 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrincipal)
267 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPendingPrincipal)
268 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
270 NS_IMPL_ADDREF_INHERITED(MediaStreamTrack, DOMEventTargetHelper)
271 NS_IMPL_RELEASE_INHERITED(MediaStreamTrack, DOMEventTargetHelper)
272 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamTrack)
273 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
275 JSObject* MediaStreamTrack::WrapObject(JSContext* aCx,
276 JS::Handle<JSObject*> aGivenProto) {
277 return MediaStreamTrack_Binding::Wrap(aCx, this, aGivenProto);
280 void MediaStreamTrack::GetId(nsAString& aID) const { aID = mID; }
282 void MediaStreamTrack::SetEnabled(bool aEnabled) {
283 LOG(LogLevel::Info,
284 ("MediaStreamTrack %p %s", this, aEnabled ? "Enabled" : "Disabled"));
286 if (mEnabled == aEnabled) {
287 return;
290 mEnabled = aEnabled;
292 if (Ended()) {
293 return;
296 mTrack->SetDisabledTrackMode(mEnabled ? DisabledTrackMode::ENABLED
297 : DisabledTrackMode::SILENCE_BLACK);
298 NotifyEnabledChanged();
301 void MediaStreamTrack::Stop() {
302 LOG(LogLevel::Info, ("MediaStreamTrack %p Stop()", this));
304 if (Ended()) {
305 LOG(LogLevel::Warning, ("MediaStreamTrack %p Already ended", this));
306 return;
309 SetReadyState(MediaStreamTrackState::Ended);
311 NotifyEnded();
314 void MediaStreamTrack::GetConstraints(dom::MediaTrackConstraints& aResult) {
315 aResult = mConstraints;
318 void MediaStreamTrack::GetSettings(dom::MediaTrackSettings& aResult,
319 CallerType aCallerType) {
320 GetSource().GetSettings(aResult);
322 // Spoof values when privacy.resistFingerprinting is true.
323 if (!nsContentUtils::ResistFingerprinting(aCallerType)) {
324 return;
326 if (aResult.mFacingMode.WasPassed()) {
327 aResult.mFacingMode.Value().AssignASCII(
328 VideoFacingModeEnumValues::GetString(VideoFacingModeEnum::User));
332 already_AddRefed<Promise> MediaStreamTrack::ApplyConstraints(
333 const MediaTrackConstraints& aConstraints, CallerType aCallerType,
334 ErrorResult& aRv) {
335 if (MOZ_LOG_TEST(gMediaStreamTrackLog, LogLevel::Info)) {
336 nsString str;
337 aConstraints.ToJSON(str);
339 LOG(LogLevel::Info, ("MediaStreamTrack %p ApplyConstraints() with "
340 "constraints %s",
341 this, NS_ConvertUTF16toUTF8(str).get()));
344 nsIGlobalObject* go = mWindow ? mWindow->AsGlobal() : nullptr;
346 RefPtr<Promise> promise = Promise::Create(go, aRv);
347 if (aRv.Failed()) {
348 return nullptr;
351 // Forward constraints to the source.
353 // After GetSource().ApplyConstraints succeeds (after it's been to
354 // media-thread and back), and no sooner, do we set mConstraints to the newly
355 // applied values.
357 // Keep a reference to this, to make sure it's still here when we get back.
358 RefPtr<MediaStreamTrack> self(this);
359 GetSource()
360 .ApplyConstraints(aConstraints, aCallerType)
361 ->Then(
362 GetCurrentSerialEventTarget(), __func__,
363 [this, self, promise, aConstraints](bool aDummy) {
364 if (!mWindow || !mWindow->IsCurrentInnerWindow()) {
365 return; // Leave Promise pending after navigation by design.
367 mConstraints = aConstraints;
368 promise->MaybeResolve(false);
370 [this, self, promise](const RefPtr<MediaMgrError>& aError) {
371 if (!mWindow || !mWindow->IsCurrentInnerWindow()) {
372 return; // Leave Promise pending after navigation by design.
374 promise->MaybeReject(
375 MakeRefPtr<MediaStreamError>(mWindow, *aError));
377 return promise.forget();
380 ProcessedMediaTrack* MediaStreamTrack::GetTrack() const {
381 MOZ_DIAGNOSTIC_ASSERT(!Ended());
382 return mTrack;
385 MediaTrackGraph* MediaStreamTrack::Graph() const {
386 MOZ_DIAGNOSTIC_ASSERT(!Ended());
387 return mTrack->Graph();
390 MediaTrackGraphImpl* MediaStreamTrack::GraphImpl() const {
391 MOZ_DIAGNOSTIC_ASSERT(!Ended());
392 return mTrack->GraphImpl();
395 void MediaStreamTrack::SetPrincipal(nsIPrincipal* aPrincipal) {
396 if (aPrincipal == mPrincipal) {
397 return;
399 mPrincipal = aPrincipal;
401 LOG(LogLevel::Info,
402 ("MediaStreamTrack %p principal changed to %p. Now: "
403 "null=%d, codebase=%d, expanded=%d, system=%d",
404 this, mPrincipal.get(), mPrincipal->GetIsNullPrincipal(),
405 mPrincipal->GetIsContentPrincipal(),
406 mPrincipal->GetIsExpandedPrincipal(), mPrincipal->IsSystemPrincipal()));
407 for (PrincipalChangeObserver<MediaStreamTrack>* observer :
408 mPrincipalChangeObservers) {
409 observer->PrincipalChanged(this);
413 void MediaStreamTrack::PrincipalChanged() {
414 mPendingPrincipal = GetSource().GetPrincipal();
415 nsCOMPtr<nsIPrincipal> newPrincipal = mPrincipal;
416 LOG(LogLevel::Info, ("MediaStreamTrack %p Principal changed on main thread "
417 "to %p (pending). Combining with existing principal %p.",
418 this, mPendingPrincipal.get(), mPrincipal.get()));
419 if (nsContentUtils::CombineResourcePrincipals(&newPrincipal,
420 mPendingPrincipal)) {
421 SetPrincipal(newPrincipal);
425 void MediaStreamTrack::NotifyPrincipalHandleChanged(
426 const PrincipalHandle& aNewPrincipalHandle) {
427 PrincipalHandle handle(aNewPrincipalHandle);
428 LOG(LogLevel::Info, ("MediaStreamTrack %p principalHandle changed on "
429 "MediaTrackGraph thread to %p. Current principal: %p, "
430 "pending: %p",
431 this, GetPrincipalFromHandle(handle), mPrincipal.get(),
432 mPendingPrincipal.get()));
433 if (PrincipalHandleMatches(handle, mPendingPrincipal)) {
434 SetPrincipal(mPendingPrincipal);
435 mPendingPrincipal = nullptr;
439 void MediaStreamTrack::MutedChanged(bool aNewState) {
440 MOZ_ASSERT(NS_IsMainThread());
443 * 4.3.1 Life-cycle and Media flow - Media flow
444 * To set a track's muted state to newState, the User Agent MUST run the
445 * following steps:
446 * 1. Let track be the MediaStreamTrack in question.
447 * 2. Set track's muted attribute to newState.
448 * 3. If newState is true let eventName be mute, otherwise unmute.
449 * 4. Fire a simple event named eventName on track.
452 if (mMuted == aNewState) {
453 return;
456 LOG(LogLevel::Info,
457 ("MediaStreamTrack %p became %s", this, aNewState ? "muted" : "unmuted"));
459 mMuted = aNewState;
461 if (Ended()) {
462 return;
465 nsString eventName = aNewState ? u"mute"_ns : u"unmute"_ns;
466 DispatchTrustedEvent(eventName);
469 void MediaStreamTrack::NotifyEnded() {
470 MOZ_ASSERT(mReadyState == MediaStreamTrackState::Ended);
472 for (const auto& consumer : mConsumers.Clone()) {
473 if (consumer) {
474 consumer->NotifyEnded(this);
475 } else {
476 MOZ_ASSERT_UNREACHABLE("A consumer was not explicitly removed");
477 mConsumers.RemoveElement(consumer);
482 void MediaStreamTrack::NotifyEnabledChanged() {
483 GetSource().SinkEnabledStateChanged();
485 for (const auto& consumer : mConsumers.Clone()) {
486 if (consumer) {
487 consumer->NotifyEnabledChanged(this, Enabled());
488 } else {
489 MOZ_ASSERT_UNREACHABLE("A consumer was not explicitly removed");
490 mConsumers.RemoveElement(consumer);
495 bool MediaStreamTrack::AddPrincipalChangeObserver(
496 PrincipalChangeObserver<MediaStreamTrack>* aObserver) {
497 // XXX(Bug 1631371) Check if this should use a fallible operation as it
498 // pretended earlier.
499 mPrincipalChangeObservers.AppendElement(aObserver);
500 return true;
503 bool MediaStreamTrack::RemovePrincipalChangeObserver(
504 PrincipalChangeObserver<MediaStreamTrack>* aObserver) {
505 return mPrincipalChangeObservers.RemoveElement(aObserver);
508 void MediaStreamTrack::AddConsumer(MediaStreamTrackConsumer* aConsumer) {
509 MOZ_ASSERT(!mConsumers.Contains(aConsumer));
510 mConsumers.AppendElement(aConsumer);
512 // Remove destroyed consumers for cleanliness
513 while (mConsumers.RemoveElement(nullptr)) {
514 MOZ_ASSERT_UNREACHABLE("A consumer was not explicitly removed");
518 void MediaStreamTrack::RemoveConsumer(MediaStreamTrackConsumer* aConsumer) {
519 mConsumers.RemoveElement(aConsumer);
521 // Remove destroyed consumers for cleanliness
522 while (mConsumers.RemoveElement(nullptr)) {
523 MOZ_ASSERT_UNREACHABLE("A consumer was not explicitly removed");
527 already_AddRefed<MediaStreamTrack> MediaStreamTrack::Clone() {
528 RefPtr<MediaStreamTrack> newTrack = CloneInternal();
529 newTrack->SetEnabled(Enabled());
530 newTrack->SetMuted(Muted());
531 return newTrack.forget();
534 void MediaStreamTrack::SetReadyState(MediaStreamTrackState aState) {
535 MOZ_ASSERT(!(mReadyState == MediaStreamTrackState::Ended &&
536 aState == MediaStreamTrackState::Live),
537 "We don't support overriding the ready state from ended to live");
539 if (Ended()) {
540 return;
543 if (mReadyState == MediaStreamTrackState::Live &&
544 aState == MediaStreamTrackState::Ended) {
545 if (mSource) {
546 mSource->UnregisterSink(mSink.get());
548 if (mMTGListener) {
549 RemoveListener(mMTGListener);
551 if (mPort) {
552 mPort->Destroy();
554 if (mTrack) {
555 mTrack->Destroy();
557 mPort = nullptr;
558 mTrack = nullptr;
559 mMTGListener = nullptr;
562 mReadyState = aState;
565 void MediaStreamTrack::OverrideEnded() {
566 MOZ_ASSERT(NS_IsMainThread());
568 if (Ended()) {
569 return;
572 LOG(LogLevel::Info, ("MediaStreamTrack %p ended", this));
574 SetReadyState(MediaStreamTrackState::Ended);
576 NotifyEnded();
578 DispatchTrustedEvent(u"ended"_ns);
581 void MediaStreamTrack::AddListener(MediaTrackListener* aListener) {
582 LOG(LogLevel::Debug,
583 ("MediaStreamTrack %p adding listener %p", this, aListener));
584 mTrackListeners.AppendElement(aListener);
586 if (Ended()) {
587 return;
589 mTrack->AddListener(aListener);
592 void MediaStreamTrack::RemoveListener(MediaTrackListener* aListener) {
593 LOG(LogLevel::Debug,
594 ("MediaStreamTrack %p removing listener %p", this, aListener));
595 mTrackListeners.RemoveElement(aListener);
597 if (Ended()) {
598 return;
600 mTrack->RemoveListener(aListener);
603 void MediaStreamTrack::AddDirectListener(DirectMediaTrackListener* aListener) {
604 LOG(LogLevel::Debug, ("MediaStreamTrack %p (%s) adding direct listener %p to "
605 "track %p",
606 this, AsAudioStreamTrack() ? "audio" : "video",
607 aListener, mTrack.get()));
608 mDirectTrackListeners.AppendElement(aListener);
610 if (Ended()) {
611 return;
613 mTrack->AddDirectListener(aListener);
616 void MediaStreamTrack::RemoveDirectListener(
617 DirectMediaTrackListener* aListener) {
618 LOG(LogLevel::Debug,
619 ("MediaStreamTrack %p removing direct listener %p from track %p", this,
620 aListener, mTrack.get()));
621 mDirectTrackListeners.RemoveElement(aListener);
623 if (Ended()) {
624 return;
626 mTrack->RemoveDirectListener(aListener);
629 already_AddRefed<MediaInputPort> MediaStreamTrack::ForwardTrackContentsTo(
630 ProcessedMediaTrack* aTrack) {
631 MOZ_ASSERT(NS_IsMainThread());
632 MOZ_RELEASE_ASSERT(aTrack);
633 return aTrack->AllocateInputPort(mTrack);
636 } // namespace mozilla::dom