Bug 1818283: Make DOMMediaStream::TrackListener cycle-collected. r=pehrsons

This prevents HTMLMediaElements from being prematurely cycle-collected, and
also makes things a bit safer.

Differential Revision: https://phabricator.services.mozilla.com/D205734
This commit is contained in:
Byron Campen 2024-04-05 20:55:53 +00:00
parent 647fcd0a58
commit 7fc0ce825d
11 changed files with 150 additions and 31 deletions

View File

@ -565,6 +565,10 @@ class HTMLMediaElement::MediaStreamTrackListener
explicit MediaStreamTrackListener(HTMLMediaElement* aElement)
: mElement(aElement) {}
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamTrackListener,
DOMMediaStream::TrackListener)
void NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) override {
if (!mElement) {
return;
@ -678,16 +682,26 @@ class HTMLMediaElement::MediaStreamTrackListener
}
protected:
const WeakPtr<HTMLMediaElement> mElement;
virtual ~MediaStreamTrackListener() = default;
RefPtr<HTMLMediaElement> mElement;
};
NS_IMPL_CYCLE_COLLECTION_INHERITED(HTMLMediaElement::MediaStreamTrackListener,
DOMMediaStream::TrackListener, mElement)
NS_IMPL_ADDREF_INHERITED(HTMLMediaElement::MediaStreamTrackListener,
DOMMediaStream::TrackListener)
NS_IMPL_RELEASE_INHERITED(HTMLMediaElement::MediaStreamTrackListener,
DOMMediaStream::TrackListener)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
HTMLMediaElement::MediaStreamTrackListener)
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream::TrackListener)
/**
* Helper class that manages audio and video outputs for all enabled tracks in a
* media element. It also manages calculating the current time when playing a
* MediaStream.
*/
class HTMLMediaElement::MediaStreamRenderer
: public DOMMediaStream::TrackListener {
class HTMLMediaElement::MediaStreamRenderer {
public:
NS_INLINE_DECL_REFCOUNTING(MediaStreamRenderer)
@ -1973,6 +1987,7 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(HTMLMediaElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement,
nsGenericHTMLElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStreamWindowCapturer)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaSource)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcMediaSource)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSrcStream)
@ -1988,6 +2003,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(HTMLMediaElement,
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextTrackManager)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAudioTrackList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVideoTrackList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaStreamTrackListener)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaKeys)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIncomingMediaKeys)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelectedVideoStreamTrack)
@ -2022,6 +2038,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement,
tmp->mMediaStreamTrackListener.get());
}
}
NS_IMPL_CYCLE_COLLECTION_UNLINK(mStreamWindowCapturer)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSrcStream)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSrcAttrStream)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaSource)
@ -2040,6 +2057,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(HTMLMediaElement,
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextTrackManager)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mAudioTrackList)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mVideoTrackList)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaStreamTrackListener)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMediaKeys)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIncomingMediaKeys)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelectedVideoStreamTrack)
@ -5270,7 +5288,7 @@ void HTMLMediaElement::SetupSrcMediaStreamPlayback(DOMMediaStream* aStream) {
NotifyMediaStreamTrackAdded(track);
}
mMediaStreamTrackListener = MakeUnique<MediaStreamTrackListener>(this);
mMediaStreamTrackListener = new MediaStreamTrackListener(this);
mSrcStream->RegisterTrackListener(mMediaStreamTrackListener.get());
ChangeNetworkState(NETWORK_IDLE);
@ -7347,7 +7365,9 @@ void HTMLMediaElement::AudioCaptureTrackChange(bool aCapture) {
CaptureStreamInternal(StreamCaptureBehavior::CONTINUE_WHEN_ENDED,
StreamCaptureType::CAPTURE_AUDIO, mtg);
mStreamWindowCapturer =
MakeUnique<MediaStreamWindowCapturer>(stream, window->WindowID());
new MediaStreamWindowCapturer(stream, window->WindowID());
mStreamWindowCapturer->mStream->RegisterTrackListener(
mStreamWindowCapturer);
} else if (!aCapture && mStreamWindowCapturer) {
for (size_t i = 0; i < mOutputStreams.Length(); i++) {
if (mOutputStreams[i].mStream == mStreamWindowCapturer->mStream) {
@ -7361,6 +7381,9 @@ void HTMLMediaElement::AudioCaptureTrackChange(bool aCapture) {
break;
}
}
mStreamWindowCapturer->mStream->UnregisterTrackListener(
mStreamWindowCapturer);
mStreamWindowCapturer = nullptr;
if (mOutputStreams.IsEmpty()) {
mTracksCaptured = nullptr;

View File

@ -1455,7 +1455,7 @@ class HTMLMediaElement : public nsGenericHTMLElement,
// Holds a reference to the stream connecting this stream to the window
// capture sink.
UniquePtr<MediaStreamWindowCapturer> mStreamWindowCapturer;
RefPtr<MediaStreamWindowCapturer> mStreamWindowCapturer;
// Holds references to the DOM wrappers for the MediaStreams that we're
// writing to.
@ -1770,7 +1770,7 @@ class HTMLMediaElement : public nsGenericHTMLElement,
RefPtr<VideoTrackList> mVideoTrackList;
UniquePtr<MediaStreamTrackListener> mMediaStreamTrackListener;
RefPtr<MediaStreamTrackListener> mMediaStreamTrackListener;
// The principal guarding mVideoFrameContainer access when playing a
// MediaStream.

View File

@ -99,6 +99,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(DOMMediaStream,
tmp->Destroy();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTracks)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsumersToKeepAlive)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mTrackListeners)
NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@ -106,6 +107,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(DOMMediaStream,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTracks)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsumersToKeepAlive)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTrackListeners)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_ADDREF_INHERITED(DOMMediaStream, DOMEventTargetHelper)
@ -115,6 +117,13 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMMediaStream)
NS_INTERFACE_MAP_ENTRY_CONCRETE(DOMMediaStream)
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION(DOMMediaStream::TrackListener)
NS_IMPL_CYCLE_COLLECTING_ADDREF(DOMMediaStream::TrackListener)
NS_IMPL_CYCLE_COLLECTING_RELEASE(DOMMediaStream::TrackListener)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DOMMediaStream::TrackListener)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
DOMMediaStream::DOMMediaStream(nsPIDOMWindowInner* aWindow)
: DOMEventTargetHelper(aWindow),
mPlaybackTrackListener(MakeAndAddRef<PlaybackTrackListener>(this)) {

View File

@ -59,9 +59,10 @@ class DOMMediaStream : public DOMEventTargetHelper,
public:
typedef dom::MediaTrackConstraints MediaTrackConstraints;
class TrackListener {
class TrackListener : public nsISupports {
public:
virtual ~TrackListener() = default;
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(TrackListener)
/**
* Called when the DOMMediaStream has a live track added, either by
@ -94,6 +95,9 @@ class DOMMediaStream : public DOMEventTargetHelper,
* Called when the DOMMediaStream has become inaudible.
*/
virtual void NotifyInaudible(){};
protected:
virtual ~TrackListener() = default;
};
explicit DOMMediaStream(nsPIDOMWindowInner* aWindow);
@ -236,7 +240,7 @@ class DOMMediaStream : public DOMEventTargetHelper,
nsTArray<nsCOMPtr<nsISupports>> mConsumersToKeepAlive;
// The track listeners subscribe to changes in this stream's track set.
nsTArray<TrackListener*> mTrackListeners;
nsTArray<RefPtr<TrackListener>> mTrackListeners;
// True if this stream has live tracks.
bool mActive = false;

View File

@ -569,7 +569,9 @@ void SelectBitrates(uint32_t aBitsPerSecond, uint8_t aNumVideoTracks,
*/
class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
public DOMMediaStream::TrackListener {
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Session)
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(Session,
DOMMediaStream::TrackListener)
struct TrackTypeComparator {
enum Type {
@ -1170,6 +1172,14 @@ class MediaRecorder::Session : public PrincipalChangeObserver<MediaStreamTrack>,
RefPtr<ShutdownBlocker> mShutdownBlocker;
};
NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaRecorder::Session,
DOMMediaStream::TrackListener, mMediaStream,
mMediaStreamTracks)
NS_IMPL_ADDREF_INHERITED(MediaRecorder::Session, DOMMediaStream::TrackListener)
NS_IMPL_RELEASE_INHERITED(MediaRecorder::Session, DOMMediaStream::TrackListener)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaRecorder::Session)
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream::TrackListener)
MediaRecorder::~MediaRecorder() {
LOG(LogLevel::Debug, ("~MediaRecorder (%p)", this));
UnRegisterActivityObserver();

View File

@ -13,6 +13,15 @@ namespace mozilla {
using dom::AudioStreamTrack;
using dom::MediaStreamTrack;
NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaStreamWindowCapturer,
DOMMediaStream::TrackListener)
NS_IMPL_ADDREF_INHERITED(MediaStreamWindowCapturer,
DOMMediaStream::TrackListener)
NS_IMPL_RELEASE_INHERITED(MediaStreamWindowCapturer,
DOMMediaStream::TrackListener)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamWindowCapturer)
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream::TrackListener)
MediaStreamWindowCapturer::CapturedTrack::CapturedTrack(
MediaStreamTrack* aTrack, uint64_t aWindowID)
: mTrack(aTrack),
@ -28,7 +37,6 @@ MediaStreamWindowCapturer::CapturedTrack::~CapturedTrack() {
MediaStreamWindowCapturer::MediaStreamWindowCapturer(DOMMediaStream* aStream,
uint64_t aWindowId)
: mStream(aStream), mWindowId(aWindowId) {
mStream->RegisterTrackListener(this);
nsTArray<RefPtr<AudioStreamTrack>> tracks;
mStream->GetAudioTracks(tracks);
for (const auto& t : tracks) {
@ -39,11 +47,7 @@ MediaStreamWindowCapturer::MediaStreamWindowCapturer(DOMMediaStream* aStream,
}
}
MediaStreamWindowCapturer::~MediaStreamWindowCapturer() {
if (mStream) {
mStream->UnregisterTrackListener(this);
}
}
MediaStreamWindowCapturer::~MediaStreamWindowCapturer() = default;
void MediaStreamWindowCapturer::NotifyTrackAdded(
const RefPtr<MediaStreamTrack>& aTrack) {

View File

@ -24,7 +24,9 @@ class MediaInputPort;
class MediaStreamWindowCapturer : public DOMMediaStream::TrackListener {
public:
MediaStreamWindowCapturer(DOMMediaStream* aStream, uint64_t aWindowId);
~MediaStreamWindowCapturer();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MediaStreamWindowCapturer,
DOMMediaStream::TrackListener)
void NotifyTrackAdded(const RefPtr<dom::MediaStreamTrack>& aTrack) override;
void NotifyTrackRemoved(const RefPtr<dom::MediaStreamTrack>& aTrack) override;
@ -41,6 +43,7 @@ class MediaStreamWindowCapturer : public DOMMediaStream::TrackListener {
const uint64_t mWindowId;
protected:
~MediaStreamWindowCapturer();
void AddTrack(dom::AudioStreamTrack* aTrack);
void RemoveTrack(dom::AudioStreamTrack* aTrack);

View File

@ -23,12 +23,14 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaStreamAudioSourceNode)
tmp->Destroy();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStream)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputTrack)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamAudioSourceNode,
AudioNode)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStream)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputTrack)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamAudioSourceNode)
@ -65,12 +67,13 @@ already_AddRefed<MediaStreamAudioSourceNode> MediaStreamAudioSourceNode::Create(
void MediaStreamAudioSourceNode::Init(DOMMediaStream& aMediaStream,
ErrorResult& aRv) {
mListener = new TrackListener(this);
mInputStream = &aMediaStream;
AudioNodeEngine* engine = new MediaStreamAudioSourceNodeEngine(this);
mTrack = AudioNodeExternalInputTrack::Create(Context()->Graph(), engine);
mInputStream->AddConsumerToKeepAlive(ToSupports(this));
mInputStream->RegisterTrackListener(this);
mInputStream->RegisterTrackListener(mListener);
if (mInputStream->Audible()) {
NotifyAudible();
}
@ -79,8 +82,9 @@ void MediaStreamAudioSourceNode::Init(DOMMediaStream& aMediaStream,
void MediaStreamAudioSourceNode::Destroy() {
if (mInputStream) {
mInputStream->UnregisterTrackListener(this);
mInputStream->UnregisterTrackListener(mListener);
mInputStream = nullptr;
mListener = nullptr;
}
DetachFromTrack();
}
@ -275,4 +279,14 @@ JSObject* MediaStreamAudioSourceNode::WrapObject(
return MediaStreamAudioSourceNode_Binding::Wrap(aCx, this, aGivenProto);
}
NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaStreamAudioSourceNode::TrackListener,
DOMMediaStream::TrackListener, mNode)
NS_IMPL_ADDREF_INHERITED(MediaStreamAudioSourceNode::TrackListener,
DOMMediaStream::TrackListener)
NS_IMPL_RELEASE_INHERITED(MediaStreamAudioSourceNode::TrackListener,
DOMMediaStream::TrackListener)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
MediaStreamAudioSourceNode::TrackListener)
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream::TrackListener)
} // namespace mozilla::dom

View File

@ -40,7 +40,6 @@ class MediaStreamAudioSourceNodeEngine final : public AudioNodeEngine {
class MediaStreamAudioSourceNode
: public AudioNode,
public DOMMediaStream::TrackListener,
public PrincipalChangeObserver<MediaStreamTrack> {
public:
static already_AddRefed<MediaStreamAudioSourceNode> Create(
@ -87,9 +86,28 @@ class MediaStreamAudioSourceNode
ErrorResult& aRv);
// From DOMMediaStream::TrackListener.
void NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) override;
void NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack) override;
void NotifyAudible() override;
void NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack);
void NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack);
void NotifyAudible();
class TrackListener final : public DOMMediaStream::TrackListener {
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TrackListener,
DOMMediaStream::TrackListener)
explicit TrackListener(MediaStreamAudioSourceNode* aNode) : mNode(aNode) {}
void NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) override {
mNode->NotifyTrackAdded(aTrack);
}
void NotifyTrackRemoved(const RefPtr<MediaStreamTrack>& aTrack) override {
mNode->NotifyTrackRemoved(aTrack);
}
void NotifyAudible() override { mNode->NotifyAudible(); }
private:
virtual ~TrackListener() = default;
RefPtr<MediaStreamAudioSourceNode> mNode;
};
// From PrincipalChangeObserver<MediaStreamTrack>.
void PrincipalChanged(MediaStreamTrack* aMediaStreamTrack) override;
@ -120,6 +138,7 @@ class MediaStreamAudioSourceNode
// On construction we set this to the first audio track of mInputStream.
RefPtr<MediaStreamTrack> mInputTrack;
RefPtr<TrackListener> mListener;
};
} // namespace mozilla::dom

View File

@ -136,7 +136,7 @@ CreateSpeechRecognitionService(nsPIDOMWindowInner* aWindow,
NS_IMPL_CYCLE_COLLECTION_WEAK_PTR_INHERITED(SpeechRecognition,
DOMEventTargetHelper, mStream,
mTrack, mRecognitionService,
mSpeechGrammarList)
mSpeechGrammarList, mListener)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SpeechRecognition)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
@ -145,6 +145,16 @@ NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
NS_IMPL_ADDREF_INHERITED(SpeechRecognition, DOMEventTargetHelper)
NS_IMPL_RELEASE_INHERITED(SpeechRecognition, DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_INHERITED(SpeechRecognition::TrackListener,
DOMMediaStream::TrackListener,
mSpeechRecognition)
NS_IMPL_ADDREF_INHERITED(SpeechRecognition::TrackListener,
DOMMediaStream::TrackListener)
NS_IMPL_RELEASE_INHERITED(SpeechRecognition::TrackListener,
DOMMediaStream::TrackListener)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SpeechRecognition::TrackListener)
NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream::TrackListener)
SpeechRecognition::SpeechRecognition(nsPIDOMWindowInner* aOwnerWindow)
: DOMEventTargetHelper(aOwnerWindow),
mEndpointer(kSAMPLE_RATE),
@ -472,8 +482,9 @@ void SpeechRecognition::Reset() {
++mStreamGeneration;
if (mStream) {
mStream->UnregisterTrackListener(this);
mStream->UnregisterTrackListener(mListener);
mStream = nullptr;
mListener = nullptr;
}
mTrack = nullptr;
mTrackIsOwned = false;
@ -642,7 +653,8 @@ RefPtr<GenericNonExclusivePromise> SpeechRecognition::StopRecording() {
if (mStream) {
// Ensure we don't start recording because a track became available
// before we get reset.
mStream->UnregisterTrackListener(this);
mStream->UnregisterTrackListener(mListener);
mListener = nullptr;
}
return GenericNonExclusivePromise::CreateAndResolve(true, __func__);
}
@ -801,10 +813,13 @@ void SpeechRecognition::Start(const Optional<NonNull<DOMMediaStream>>& aStream,
MediaStreamConstraints constraints;
constraints.mAudio.SetAsBoolean() = true;
MOZ_ASSERT(!mListener);
mListener = new TrackListener(this);
if (aStream.WasPassed()) {
mStream = &aStream.Value();
mTrackIsOwned = false;
mStream->RegisterTrackListener(this);
mStream->RegisterTrackListener(mListener);
nsTArray<RefPtr<AudioStreamTrack>> tracks;
mStream->GetAudioTracks(tracks);
for (const RefPtr<AudioStreamTrack>& track : tracks) {
@ -839,7 +854,7 @@ void SpeechRecognition::Start(const Optional<NonNull<DOMMediaStream>>& aStream,
return;
}
mStream = std::move(aStream);
mStream->RegisterTrackListener(this);
mStream->RegisterTrackListener(mListener);
for (const RefPtr<AudioStreamTrack>& track : tracks) {
if (!track->Ended()) {
NotifyTrackAdded(track);

View File

@ -52,7 +52,6 @@ LogModule* GetSpeechRecognitionLog();
class SpeechRecognition final : public DOMEventTargetHelper,
public nsIObserver,
public DOMMediaStream::TrackListener,
public SupportsWeakPtr {
public:
explicit SpeechRecognition(nsPIDOMWindowInner* aOwnerWindow);
@ -133,7 +132,24 @@ class SpeechRecognition final : public DOMEventTargetHelper,
EVENT_COUNT
};
void NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) override;
void NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack);
class TrackListener final : public DOMMediaStream::TrackListener {
public:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TrackListener,
DOMMediaStream::TrackListener)
explicit TrackListener(SpeechRecognition* aSpeechRecognition)
: mSpeechRecognition(aSpeechRecognition) {}
void NotifyTrackAdded(const RefPtr<MediaStreamTrack>& aTrack) override {
mSpeechRecognition->NotifyTrackAdded(aTrack);
}
private:
virtual ~TrackListener() = default;
RefPtr<SpeechRecognition> mSpeechRecognition;
};
// aMessage should be valid UTF-8, but invalid UTF-8 byte sequences are
// replaced with the REPLACEMENT CHARACTER on conversion to UTF-16.
void DispatchError(EventType aErrorType,
@ -266,6 +282,8 @@ class SpeechRecognition final : public DOMEventTargetHelper,
// a conforming implementation.
uint32_t mMaxAlternatives;
RefPtr<TrackListener> mListener;
void ProcessTestEventRequest(nsISupports* aSubject,
const nsAString& aEventName);