tor-browser

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

commit 58a0249b524d6fd95bd30e7da62f153d1949bf19
parent 4e31e678ec71b60061ae44ab1615b53cd85e6ca1
Author: Jan-Ivar Bruaroey <jib@mozilla.com>
Date:   Wed,  8 Oct 2025 17:05:54 +0000

Bug 1868223 - Implement custom RTCEncodedAudioFrame::Read/WriteStructuredClone for same process. r=bwc

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

Diffstat:
Mdom/base/StructuredCloneHolder.cpp | 25+++++++++++++++++++++++++
Mdom/base/StructuredCloneHolder.h | 9+++++++++
Mdom/media/webrtc/jsapi/RTCEncodedAudioFrame.cpp | 83++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Mdom/media/webrtc/jsapi/RTCEncodedAudioFrame.h | 29++++++++++++++++++++---------
4 files changed, 126 insertions(+), 20 deletions(-)

diff --git a/dom/base/StructuredCloneHolder.cpp b/dom/base/StructuredCloneHolder.cpp @@ -56,6 +56,8 @@ #include "mozilla/dom/OffscreenCanvas.h" #include "mozilla/dom/OffscreenCanvasBinding.h" #ifdef MOZ_WEBRTC +# include "mozilla/dom/RTCEncodedAudioFrame.h" +# include "mozilla/dom/RTCEncodedAudioFrameBinding.h" # include "mozilla/dom/RTCEncodedVideoFrame.h" # include "mozilla/dom/RTCEncodedVideoFrameBinding.h" #endif @@ -1173,7 +1175,19 @@ JSObject* StructuredCloneHolder::CustomReadHandler( aCx, mGlobal, aReader, RtcEncodedVideoFrames()[aIndex]); } } + + if (StaticPrefs::media_peerconnection_enabled() && + aTag == SCTAG_DOM_RTCENCODEDAUDIOFRAME && + CloneScope() == StructuredCloneScope::SameProcess && + aCloneDataPolicy.areIntraClusterClonableSharedObjectsAllowed()) { + JS::Rooted<JSObject*> global(aCx, mGlobal->GetGlobalJSObject()); + if (RTCEncodedAudioFrame_Binding::ConstructorEnabled(aCx, global)) { + return RTCEncodedAudioFrame::ReadStructuredClone( + aCx, mGlobal, aReader, RtcEncodedAudioFrames()[aIndex]); + } + } #endif + return ReadFullySerializableObjects(aCx, aReader, aTag, false); } @@ -1327,6 +1341,17 @@ bool StructuredCloneHolder::CustomWriteHandler( : false; } } + + // See if this is an RTCEncodedAudioFrame object. + if (StaticPrefs::media_peerconnection_enabled()) { + RTCEncodedAudioFrame* rtcFrame = nullptr; + if (NS_SUCCEEDED(UNWRAP_OBJECT(RTCEncodedAudioFrame, &obj, rtcFrame))) { + SameProcessScopeRequired(aSameProcessScopeRequired); + return CloneScope() == StructuredCloneScope::SameProcess + ? rtcFrame->WriteStructuredClone(aWriter, this) + : false; + } + } #endif { // We only care about streams, so ReflectorToISupportsStatic is fine. diff --git a/dom/base/StructuredCloneHolder.h b/dom/base/StructuredCloneHolder.h @@ -174,6 +174,7 @@ struct VideoFrameSerializedData; struct AudioDataSerializedData; #ifdef MOZ_WEBRTC struct RTCEncodedVideoFrameData; +struct RTCEncodedAudioFrameData; #endif class StructuredCloneHolder : public StructuredCloneHolderBase { @@ -233,6 +234,7 @@ class StructuredCloneHolder : public StructuredCloneHolderBase { STMT(mEncodedVideoChunks); \ STMT(mEncodedAudioChunks); \ IF_WEBRTC(STMT(mRtcEncodedVideoFrames);) \ + IF_WEBRTC(STMT(mRtcEncodedAudioFrames);) \ STMT(mPortIdentifiers); // Call this method to know if this object is keeping some DOM object alive. @@ -316,6 +318,10 @@ class StructuredCloneHolder : public StructuredCloneHolderBase { nsTArray<RTCEncodedVideoFrameData>& RtcEncodedVideoFrames() { return mRtcEncodedVideoFrames; } + + nsTArray<RTCEncodedAudioFrameData>& RtcEncodedAudioFrames() { + return mRtcEncodedAudioFrames; + } #endif // Implementations of the virtual methods to allow cloning of objects which @@ -435,6 +441,9 @@ class StructuredCloneHolder : public StructuredCloneHolderBase { #ifdef MOZ_WEBRTC // Used for cloning RTCEncodedVideoFrame in the structured cloning algorithm. nsTArray<RTCEncodedVideoFrameData> mRtcEncodedVideoFrames; + + // Used for cloning RTCEncodedAudioFrame in the structured cloning algorithm. + nsTArray<RTCEncodedAudioFrameData> mRtcEncodedAudioFrames; #endif // This raw pointer is only set within ::Read() and is unset by the end. diff --git a/dom/media/webrtc/jsapi/RTCEncodedAudioFrame.cpp b/dom/media/webrtc/jsapi/RTCEncodedAudioFrame.cpp @@ -11,6 +11,7 @@ #include <memory> #include <utility> +#include "api/frame_transformer_factory.h" #include "api/frame_transformer_interface.h" #include "js/RootingAPI.h" #include "jsapi/RTCEncodedFrameBase.h" @@ -20,6 +21,8 @@ #include "mozilla/Unused.h" #include "mozilla/dom/RTCEncodedAudioFrameBinding.h" #include "mozilla/dom/RTCRtpScriptTransformer.h" +#include "mozilla/dom/StructuredCloneHolder.h" +#include "mozilla/dom/StructuredCloneTags.h" #include "mozilla/fallible.h" #include "nsContentUtils.h" #include "nsCycleCollectionParticipant.h" @@ -29,8 +32,20 @@ namespace mozilla::dom { -NS_IMPL_CYCLE_COLLECTION_INHERITED(RTCEncodedAudioFrame, RTCEncodedFrameBase, - mOwner) +NS_IMPL_CYCLE_COLLECTION_CLASS(RTCEncodedAudioFrame) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(RTCEncodedAudioFrame, + RTCEncodedFrameBase) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner) + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(RTCEncodedAudioFrame, + RTCEncodedFrameBase) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(RTCEncodedAudioFrame, + RTCEncodedFrameBase) + NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_IMPL_ADDREF_INHERITED(RTCEncodedAudioFrame, RTCEncodedFrameBase) NS_IMPL_RELEASE_INHERITED(RTCEncodedAudioFrame, RTCEncodedFrameBase) @@ -48,7 +63,7 @@ RTCEncodedAudioFrame::RTCEncodedAudioFrame( mMetadata.mSynchronizationSource.Construct(mFrame->GetSsrc()); mMetadata.mPayloadType.Construct(mFrame->GetPayloadType()); const auto& audioFrame( - static_cast<webrtc::TransformableAudioFrameInterface&>(*mState.mFrame)); + static_cast<webrtc::TransformableAudioFrameInterface&>(*mFrame)); mMetadata.mContributingSources.Construct(); for (const auto csrc : audioFrame.GetContributingSources()) { Unused << mMetadata.mContributingSources.Value().AppendElement(csrc, @@ -63,7 +78,22 @@ RTCEncodedAudioFrame::RTCEncodedAudioFrame( mozilla::HoldJSObjects(this); } +RTCEncodedAudioFrame::RTCEncodedAudioFrame(nsIGlobalObject* aGlobal, + RTCEncodedAudioFrameData&& aData) + : RTCEncodedAudioFrameData{{std::move(aData.mFrame), aData.mCounter, + aData.mTimestamp}, + std::move(aData.mMetadata)}, + RTCEncodedFrameBase(aGlobal, static_cast<RTCEncodedFrameState&>(*this)), + mOwner(nullptr) { + // Base class needs this, but can't do it itself because of an assertion in + // the cycle-collector. + mozilla::HoldJSObjects(this); +} + RTCEncodedAudioFrame::~RTCEncodedAudioFrame() { + // Clear JS::Heap<> members before unregistering as a script holder, + // so their destructors don't barrier against a finalized JS object. + mData = nullptr; // from RTCEncodedFrameBase (protected) // Base class needs this, but can't do it itself because of an assertion in // the cycle-collector. mozilla::DropJSObjects(this); @@ -74,6 +104,14 @@ JSObject* RTCEncodedAudioFrame::WrapObject(JSContext* aCx, return RTCEncodedAudioFrame_Binding::Wrap(aCx, this, aGivenProto); } +RTCEncodedAudioFrameData RTCEncodedAudioFrameData::Clone() const { + return RTCEncodedAudioFrameData{ + {webrtc::CloneAudioFrame( + static_cast<webrtc::TransformableAudioFrameInterface*>( + mFrame.get()))}, + RTCEncodedAudioFrameMetadata(mMetadata)}; +} + nsIGlobalObject* RTCEncodedAudioFrame::GetParentObject() const { return mGlobal; } @@ -89,17 +127,40 @@ bool RTCEncodedAudioFrame::CheckOwner(RTCRtpScriptTransformer* aOwner) const { // https://www.w3.org/TR/webrtc-encoded-transform/#RTCEncodedAudioFrame-serialization /* static */ -already_AddRefed<RTCEncodedAudioFrame> -RTCEncodedAudioFrame::ReadStructuredClone(JSContext* aCx, - nsIGlobalObject* aGlobal, - JSStructuredCloneReader* aReader) { - // TBD implementation to follow in followup patch - return nullptr; +JSObject* RTCEncodedAudioFrame::ReadStructuredClone( + JSContext* aCx, nsIGlobalObject* aGlobal, JSStructuredCloneReader* aReader, + RTCEncodedAudioFrameData& aData) { + JS::Rooted<JS::Value> value(aCx, JS::NullValue()); + // To avoid a rooting hazard error from returning a raw JSObject* before + // running the RefPtr destructor, RefPtr needs to be destructed before + // returning the raw JSObject*, which is why the RefPtr<RTCEncodedAudioFrame> + // is created in the scope below. Otherwise, the static analysis infers the + // RefPtr cannot be safely destructed while the unrooted return JSObject* is + // on the stack. + { + auto frame = MakeRefPtr<RTCEncodedAudioFrame>(aGlobal, std::move(aData)); + if (!GetOrCreateDOMReflector(aCx, frame, &value) || !value.isObject()) { + return nullptr; + } + } + return value.toObjectOrNull(); } bool RTCEncodedAudioFrame::WriteStructuredClone( - JSContext* aCx, JSStructuredCloneWriter* aWriter) const { - return false; + JSStructuredCloneWriter* aWriter, StructuredCloneHolder* aHolder) const { + AssertIsOnOwningThread(); + + // Indexing the chunk and send the index to the receiver. + const uint32_t index = + static_cast<uint32_t>(aHolder->RtcEncodedAudioFrames().Length()); + // The serialization is limited to the same process scope so it's ok to + // hand over a (copy of a) webrtc internal object here. + // + // TODO: optimize later once encoded source API materializes + // .AppendElement(aHolder->IsTransferred(mData) ? Take() : Clone()) + aHolder->RtcEncodedAudioFrames().AppendElement(Clone()); + return !NS_WARN_IF( + !JS_WriteUint32Pair(aWriter, SCTAG_DOM_RTCENCODEDAUDIOFRAME, index)); } } // namespace mozilla::dom diff --git a/dom/media/webrtc/jsapi/RTCEncodedAudioFrame.h b/dom/media/webrtc/jsapi/RTCEncodedAudioFrame.h @@ -7,15 +7,19 @@ #ifndef MOZILLA_DOM_MEDIA_WEBRTC_JSAPI_RTCENCODEDAUDIOFRAME_H_ #define MOZILLA_DOM_MEDIA_WEBRTC_JSAPI_RTCENCODEDAUDIOFRAME_H_ -#include "jsapi/RTCEncodedFrameBase.h" #include "mozilla/RefPtr.h" #include "mozilla/dom/RTCEncodedAudioFrameBinding.h" +#include "mozilla/dom/RTCEncodedFrameBase.h" #include "nsIGlobalObject.h" namespace mozilla::dom { +class StructuredCloneHolder; + struct RTCEncodedAudioFrameData : RTCEncodedFrameState { RTCEncodedAudioFrameMetadata mMetadata; + + [[nodiscard]] RTCEncodedAudioFrameData Clone() const; }; // Wraps a libwebrtc frame, allowing the frame buffer to be modified, and @@ -30,10 +34,13 @@ class RTCEncodedAudioFrame final : public RTCEncodedAudioFrameData, std::unique_ptr<webrtc::TransformableFrameInterface> aFrame, uint64_t aCounter, RTCRtpScriptTransformer* aOwner); + explicit RTCEncodedAudioFrame(nsIGlobalObject* aGlobal, + RTCEncodedAudioFrameData&& aData); + // nsISupports NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(RTCEncodedAudioFrame, - RTCEncodedFrameBase) + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(RTCEncodedAudioFrame, + RTCEncodedFrameBase) // webidl (timestamp and data accessors live in base class) JSObject* WrapObject(JSContext* aCx, @@ -47,12 +54,11 @@ class RTCEncodedAudioFrame final : public RTCEncodedAudioFrameData, bool IsVideo() const override { return false; } - // [Serializable] implementations: {Read, Write}StructuredClone - static already_AddRefed<RTCEncodedAudioFrame> ReadStructuredClone( - JSContext* aCx, nsIGlobalObject* aGlobal, - JSStructuredCloneReader* aReader); - bool WriteStructuredClone(JSContext* aCx, - JSStructuredCloneWriter* aWriter) const; + static JSObject* ReadStructuredClone(JSContext* aCx, nsIGlobalObject* aGlobal, + JSStructuredCloneReader* aReader, + RTCEncodedAudioFrameData& aData); + bool WriteStructuredClone(JSStructuredCloneWriter* aWriter, + StructuredCloneHolder* aHolder) const; private: virtual ~RTCEncodedAudioFrame(); @@ -63,6 +69,11 @@ class RTCEncodedAudioFrame final : public RTCEncodedAudioFrameData, RTCEncodedAudioFrame(RTCEncodedAudioFrame&&) = delete; RTCEncodedAudioFrame& operator=(RTCEncodedAudioFrame&&) = delete; + // RTCEncodedAudioFrame can run on either main thread or worker thread. + void AssertIsOnOwningThread() const { + NS_ASSERT_OWNINGTHREAD(RTCEncodedAudioFrame); + } + RefPtr<RTCRtpScriptTransformer> mOwner; };