tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit b2791ba9793fd31eb0b6b35188b1edc4a8e7899e
parent 4a7f84c7fec85366a346a70db41d7fb6df51dbfd
Author: Andreas Pehrson <apehrson@mozilla.com>
Date:   Tue, 11 Nov 2025 08:20:26 +0000

Bug 1771789 - Disregard failure to send Stop in MERVS state machine. r=jib

Failure to send stop does not mean the capturer is still running. Treat it as
stopped.

Differential Revision: https://phabricator.services.mozilla.com/D268858

Diffstat:
Mdom/media/systemservices/CamerasChild.cpp | 66+++++++++++++++++++++++++++++++++++++++++-------------------------
Mdom/media/systemservices/CamerasChild.h | 13+++++++++++--
Mdom/media/webrtc/MediaEngineRemoteVideoSource.cpp | 38+++++++++++++++++++++++++++++---------
3 files changed, 81 insertions(+), 36 deletions(-)

diff --git a/dom/media/systemservices/CamerasChild.cpp b/dom/media/systemservices/CamerasChild.cpp @@ -141,35 +141,42 @@ mozilla::ipc::IPCResult CamerasChild::RecvReplyNumberOfCapabilities( template <class T = int> class LockAndDispatch { public: + using Result = CamerasChild::DispatchToParentResult; + LockAndDispatch(CamerasChild* aCamerasChild, const char* aRequestingFunc, - nsIRunnable* aRunnable, T aFailureValue, + nsIRunnable* aRunnable, T aFailureValue, T aIPCFailureValue, const T& aSuccessValue) : mCamerasChild(aCamerasChild), mRequestingFunc(aRequestingFunc), mRunnable(aRunnable), mReplyLock(aCamerasChild->mReplyMonitor), mRequestLock(aCamerasChild->mRequestMutex), - mSuccess(true), + mStatus(Result::SUCCESS), mFailureValue(aFailureValue), + mIPCFailureValue(aIPCFailureValue), mSuccessValue(aSuccessValue) { Dispatch(); } T ReturnValue() const { - if (mSuccess) { + if (mStatus == Result::SUCCESS) { return mSuccessValue; - } else { + } + if (mStatus == Result::FAILURE) { return mFailureValue; } + MOZ_ASSERT(mStatus == Result::DISCONNECTED); + return mIPCFailureValue; } - const bool& Success() const { return mSuccess; } + bool Success() const { return mStatus == Result::SUCCESS; } + bool Disconnected() const { return mStatus == Result::DISCONNECTED; } private: void Dispatch() { - if (!mCamerasChild->DispatchToParent(mRunnable, mReplyLock)) { + mStatus = mCamerasChild->DispatchToParent(mRunnable, mReplyLock); + if (mStatus != Result::SUCCESS) { LOG(("Cameras dispatch for IPC failed in %s", mRequestingFunc)); - mSuccess = false; } } @@ -181,13 +188,15 @@ class LockAndDispatch { // the reply to be filled in, necessitating the additional mRequestLock/Mutex; MonitorAutoLock mReplyLock; MutexAutoLock mRequestLock; - bool mSuccess; + CamerasChild::DispatchToParentResult mStatus; const T mFailureValue; + const T mIPCFailureValue; const T& mSuccessValue; }; -bool CamerasChild::DispatchToParent(nsIRunnable* aRunnable, - MonitorAutoLock& aMonitor) { +auto CamerasChild::DispatchToParent(nsIRunnable* aRunnable, + MonitorAutoLock& aMonitor) + -> DispatchToParentResult { CamerasSingleton::Mutex().AssertCurrentThreadOwns(); mReplyMonitor.AssertCurrentThreadOwns(); CamerasSingleton::Thread()->Dispatch(aRunnable, NS_DISPATCH_NORMAL); @@ -197,11 +206,12 @@ bool CamerasChild::DispatchToParent(nsIRunnable* aRunnable, do { // If the parent has been shut down, then we won't receive a reply. if (!mIPCIsAlive) { - return false; + return DispatchToParentResult::DISCONNECTED; } aMonitor.Wait(); } while (!mReceivedReply); - return mReplySuccess; + return mReplySuccess ? DispatchToParentResult::SUCCESS + : DispatchToParentResult::FAILURE; } int CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine, @@ -213,7 +223,7 @@ int CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine, mozilla::NewRunnableMethod<CaptureEngine, nsCString>( "camera::PCamerasChild::SendNumberOfCapabilities", this, &CamerasChild::SendNumberOfCapabilities, aCapEngine, unique_id); - LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger); + LockAndDispatch<> dispatcher(this, __func__, runnable, 0, 0, mReplyInteger); LOG(("Capture capability count: %d", dispatcher.ReturnValue())); return dispatcher.ReturnValue(); } @@ -223,7 +233,7 @@ int CamerasChild::NumberOfCaptureDevices(CaptureEngine aCapEngine) { nsCOMPtr<nsIRunnable> runnable = mozilla::NewRunnableMethod<CaptureEngine>( "camera::PCamerasChild::SendNumberOfCaptureDevices", this, &CamerasChild::SendNumberOfCaptureDevices, aCapEngine); - LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger); + LockAndDispatch<> dispatcher(this, __func__, runnable, 0, 0, mReplyInteger); LOG(("Capture Devices: %d", dispatcher.ReturnValue())); return dispatcher.ReturnValue(); } @@ -244,8 +254,8 @@ int CamerasChild::EnsureInitialized(CaptureEngine aCapEngine) { nsCOMPtr<nsIRunnable> runnable = mozilla::NewRunnableMethod<CaptureEngine>( "camera::PCamerasChild::SendEnsureInitialized", this, &CamerasChild::SendEnsureInitialized, aCapEngine); - LockAndDispatch<> dispatcher(this, __func__, runnable, 0, mReplyInteger); - LOG(("Capture Devices: %d", dispatcher.ReturnValue())); + LockAndDispatch<> dispatcher(this, __func__, runnable, 0, 0, mReplyInteger); + LOG(("Initialized: %d", dispatcher.ReturnValue())); return dispatcher.ReturnValue(); } @@ -262,7 +272,8 @@ int CamerasChild::GetCaptureCapability( &CamerasChild::SendGetCaptureCapability, aCapEngine, unique_id, capability_number); mReplyCapability = capability; - LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero); + LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError, + kSuccess); mReplyCapability = nullptr; return dispatcher.ReturnValue(); } @@ -292,7 +303,8 @@ int CamerasChild::GetCaptureDevice( mozilla::NewRunnableMethod<CaptureEngine, unsigned int>( "camera::PCamerasChild::SendGetCaptureDevice", this, &CamerasChild::SendGetCaptureDevice, aCapEngine, list_number); - LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero); + LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError, + kSuccess); if (dispatcher.Success()) { base::strlcpy(device_nameUTF8, mReplyDeviceName.get(), device_nameUTF8Length); @@ -328,7 +340,8 @@ int CamerasChild::AllocateCapture(CaptureEngine aCapEngine, mozilla::NewRunnableMethod<CaptureEngine, nsCString, uint64_t>( "camera::PCamerasChild::SendAllocateCapture", this, &CamerasChild::SendAllocateCapture, aCapEngine, unique_id, aWindowID); - LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mReplyInteger); + LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError, + mReplyInteger); if (dispatcher.Success()) { LOG(("Capture Device allocated: %d", mReplyInteger)); } @@ -353,7 +366,8 @@ int CamerasChild::ReleaseCapture(CaptureEngine aCapEngine, mozilla::NewRunnableMethod<CaptureEngine, int>( "camera::PCamerasChild::SendReleaseCapture", this, &CamerasChild::SendReleaseCapture, aCapEngine, capture_id); - LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero); + LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError, + kSuccess); return dispatcher.ReturnValue(); } @@ -401,7 +415,8 @@ int CamerasChild::StartCapture(CaptureEngine aCapEngine, const int capture_id, "camera::PCamerasChild::SendStartCapture", this, &CamerasChild::SendStartCapture, aCapEngine, capture_id, capCap, constraints, resize_mode); - LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero); + LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError, + kSuccess); return dispatcher.ReturnValue(); } @@ -412,7 +427,8 @@ int CamerasChild::FocusOnSelectedSource(CaptureEngine aCapEngine, mozilla::NewRunnableMethod<CaptureEngine, int>( "camera::PCamerasChild::SendFocusOnSelectedSource", this, &CamerasChild::SendFocusOnSelectedSource, aCapEngine, aCaptureId); - LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero); + LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError, + kSuccess); return dispatcher.ReturnValue(); } @@ -422,8 +438,9 @@ int CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id) { mozilla::NewRunnableMethod<CaptureEngine, int>( "camera::PCamerasChild::SendStopCapture", this, &CamerasChild::SendStopCapture, aCapEngine, capture_id); - LockAndDispatch<> dispatcher(this, __func__, runnable, -1, mZero); - if (dispatcher.Success()) { + LockAndDispatch<> dispatcher(this, __func__, runnable, kError, kIpcError, + kSuccess); + if (dispatcher.Success() || dispatcher.Disconnected()) { RemoveCallback(capture_id); } return dispatcher.ReturnValue(); @@ -526,7 +543,6 @@ CamerasChild::CamerasChild() mReplyMonitor("mozilla::cameras::CamerasChild::mReplyMonitor"), mReceivedReply(false), mReplySuccess(false), - mZero(0), mReplyInteger(0), mReplyScary(false) { LOG(("CamerasChild: %p", this)); diff --git a/dom/media/systemservices/CamerasChild.h b/dom/media/systemservices/CamerasChild.h @@ -45,6 +45,10 @@ class CamerasChild; template <class T> class LockAndDispatch; +static constexpr int kSuccess = 0; +static constexpr int kError = -1; +static constexpr int kIpcError = -2; + // We emulate the sync webrtc.org API with the help of singleton // CamerasSingleton, which manages a pointer to an IPC object, a thread // where IPC operations should run on, and a mutex. @@ -223,7 +227,13 @@ class CamerasChild final : public PCamerasChild { ~CamerasChild(); // Dispatch a Runnable to the PCamerasParent, by executing it on the // decidecated Cameras IPC/PBackground thread. - bool DispatchToParent(nsIRunnable* aRunnable, MonitorAutoLock& aMonitor); + enum class DispatchToParentResult : int8_t { + SUCCESS = 0, + FAILURE = -1, + DISCONNECTED = -2, + }; + DispatchToParentResult DispatchToParent(nsIRunnable* aRunnable, + MonitorAutoLock& aMonitor); void AddCallback(int capture_id, FrameRelay* render); void RemoveCallback(int capture_id); @@ -250,7 +260,6 @@ class CamerasChild final : public PCamerasChild { bool mReceivedReply; // Async responses data contents; bool mReplySuccess; - const int mZero; int mReplyInteger; webrtc::VideoCaptureCapability* mReplyCapability = nullptr; nsCString mReplyDeviceName; diff --git a/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp b/dom/media/webrtc/MediaEngineRemoteVideoSource.cpp @@ -369,12 +369,22 @@ nsresult MediaEngineRemoteVideoSource::Deallocate() { LOG("Video device %d deallocated", mCaptureId); - if (camera::GetChildAndCall(&camera::CamerasChild::ReleaseCapture, mCapEngine, - mCaptureId)) { - // Failure can occur when the parent process is shutting down. - return NS_ERROR_FAILURE; + int error = camera::GetChildAndCall(&camera::CamerasChild::ReleaseCapture, + mCapEngine, mCaptureId); + + if (error == camera::kSuccess) { + return NS_OK; } - return NS_OK; + + if (error == camera::kIpcError) { + // Failure can occur when the parent process is shutting down, and the IPC + // channel is down. We still consider the capturer deallocated in this + // case, since it cannot deliver frames without the IPC channel open. + return NS_OK; + } + + MOZ_ASSERT(error == camera::kError); + return NS_ERROR_FAILURE; } void MediaEngineRemoteVideoSource::SetTrack(const RefPtr<MediaTrack>& aTrack, @@ -468,9 +478,11 @@ nsresult MediaEngineRemoteVideoSource::Stop() { MOZ_ASSERT(mState == kStarted); - if (camera::GetChildAndCall(&camera::CamerasChild::StopCapture, mCapEngine, - mCaptureId)) { - // Failure can occur when the parent process is shutting down. + int error = camera::GetChildAndCall(&camera::CamerasChild::StopCapture, + mCapEngine, mCaptureId); + + if (error == camera::kError) { + // CamerasParent replied with error. The capturer is still running. return NS_ERROR_FAILURE; } @@ -479,7 +491,15 @@ nsresult MediaEngineRemoteVideoSource::Stop() { mState = kStopped; } - return NS_OK; + if (error == camera::kSuccess) { + return NS_OK; + } + + MOZ_ASSERT(error == camera::kIpcError); + // Failure can occur when the parent process is shutting down, and the IPC + // channel is down. We still consider the capturer stopped in this case, + // since it cannot deliver frames without the IPC channel open. + return NS_ERROR_FAILURE; } nsresult MediaEngineRemoteVideoSource::Reconfigure(