Commit 79de0d4b authored by Chris Cunningham's avatar Chris Cunningham Committed by Chromium LUCI CQ

Defensive fix for crash in MediaWebContentsobserver

This defensive fix until MediaSession owners return from vacation.
Reverting is challenging given the long string of CLs.

GetMediaPlayerRemote(player_id) is crashing for failing to find the
remote with that ID. The stack shows the call coming from
MediaSessionController, which should have the same lifetime as its
associated mojo::Remote<media::mojom::MediaPlayer>. So far, I have not
found how those lifetimes are getting out of sync.

Ownership looks like this (read -> as "owns", MWCO as MediaWebContentsObserver):
MWCO -> MediaSessionControllersManager -> map<MediaPlayerId, MediaSessionController(s)>
MWCO -> map<MediaPlayerId, mojo::Remote<media::mojom::MediaPlayer>>

I've combed through all the places where MWCO removes from its map of
MediaPlayer remotes, and looked for a failure to call
MediaSessionControllersManger::OnEnd(Id) to remove the associated
MediaSessionController. There are 3 cases where we remove from the
map...
1. MediaWebContentsObserver::RenderFrameDeleted()
2. MediaWebContentsObserver::WebContentsDestroyed()
3. media_player_remotes_[player_id].set_disconnect_handler(base::BindOnce(...

The only case where I don't see a call to
MediaSessionControllersManager is #2. But that shouldn't matter since
MWCO's destructor is called shortly (synchronously) after
WebContentsDestroyed(). Its destructor will tear down the
MediaSessionControllersManager.

The defensive fix does not address the root lifetime issue. It is
probably the case that the MediaSessionControllers are being leaked.

Bug: 1161551, 1039252
Change-Id: I85337eae106d8b56b7d48fc9759702dc579855ca
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2607579
Commit-Queue: Chrome Cunningham <chcunningham@chromium.org>
Commit-Queue: Frank Liberato <liberato@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Auto-Submit: Chrome Cunningham <chcunningham@chromium.org>
Cr-Commit-Position: refs/heads/master@{#839818}
parent e944e2ee
...@@ -525,11 +525,14 @@ void MediaWebContentsObserver::OnReceivedTranslatedDeviceId( ...@@ -525,11 +525,14 @@ void MediaWebContentsObserver::OnReceivedTranslatedDeviceId(
MediaPlayerId(render_frame_host, delegate_id), raw_device_id); MediaPlayerId(render_frame_host, delegate_id), raw_device_id);
} }
mojo::Remote<media::mojom::MediaPlayer>& media::mojom::MediaPlayer* MediaWebContentsObserver::GetMediaPlayerRemote(
MediaWebContentsObserver::GetMediaPlayerRemote(const MediaPlayerId& player_id) { const MediaPlayerId& player_id) {
DCHECK(media_player_remotes_.contains(player_id)); if (media_player_remotes_.contains(player_id)) {
DCHECK(media_player_remotes_[player_id].is_bound()); DCHECK(media_player_remotes_[player_id].is_bound());
return media_player_remotes_.at(player_id); return media_player_remotes_.at(player_id).get();
}
return nullptr;
} }
void MediaWebContentsObserver::OnMediaPlayerHostDisconnected( void MediaWebContentsObserver::OnMediaPlayerHostDisconnected(
......
...@@ -110,9 +110,11 @@ class CONTENT_EXPORT MediaWebContentsObserver : public WebContentsObserver { ...@@ -110,9 +110,11 @@ class CONTENT_EXPORT MediaWebContentsObserver : public WebContentsObserver {
int delegate_id, int delegate_id,
const std::string& raw_device_id); const std::string& raw_device_id);
// Return an already bound mojo Remote for the MediaPlayer mojo interface. It // Return an already bound mojo Remote for the MediaPlayer mojo interface.
// is an error to call this method if no MediaPlayer with |player_id| exists. // Return null if no player with |player_id| exists.
mojo::Remote<media::mojom::MediaPlayer>& GetMediaPlayerRemote( // TODO(https://crbug.com/1161551): Revert to returning a reference and make
// it an error to call this method if no MediaPlayer with |player_id| exists.
media::mojom::MediaPlayer* GetMediaPlayerRemote(
const MediaPlayerId& player_id); const MediaPlayerId& player_id);
// Creates a new MediaPlayerObserverHostImpl associated to |player_id| if // Creates a new MediaPlayerObserverHostImpl associated to |player_id| if
......
...@@ -50,34 +50,62 @@ bool MediaSessionController::OnPlaybackStarted() { ...@@ -50,34 +50,62 @@ bool MediaSessionController::OnPlaybackStarted() {
void MediaSessionController::OnSuspend(int player_id) { void MediaSessionController::OnSuspend(int player_id) {
DCHECK_EQ(player_id_, player_id); DCHECK_EQ(player_id_, player_id);
media::mojom::MediaPlayer* remote =
web_contents_->media_web_contents_observer()->GetMediaPlayerRemote(id_);
if (!remote) {
// TODO(https://crbug.com/1161551): Remove this when lifetime bug is fixed.
NOTREACHED() << "Controller should not outlive remote MediaPlayer";
return;
}
// TODO(crbug.com/953645): Set triggered_by_user to true ONLY if that action // TODO(crbug.com/953645): Set triggered_by_user to true ONLY if that action
// was actually triggered by user as this will activate the frame. // was actually triggered by user as this will activate the frame.
web_contents_->media_web_contents_observer() remote->RequestPause(/*triggered_by_user=*/true);
->GetMediaPlayerRemote(id_)
->RequestPause(/*triggered_by_user=*/true);
} }
void MediaSessionController::OnResume(int player_id) { void MediaSessionController::OnResume(int player_id) {
DCHECK_EQ(player_id_, player_id); DCHECK_EQ(player_id_, player_id);
web_contents_->media_web_contents_observer()
->GetMediaPlayerRemote(id_) media::mojom::MediaPlayer* remote =
->RequestPlay(); web_contents_->media_web_contents_observer()->GetMediaPlayerRemote(id_);
if (!remote) {
// TODO(https://crbug.com/1161551): Remove this when lifetime bug is fixed.
NOTREACHED() << "Controller should not outlive remote MediaPlayer";
return;
}
remote->RequestPlay();
} }
void MediaSessionController::OnSeekForward(int player_id, void MediaSessionController::OnSeekForward(int player_id,
base::TimeDelta seek_time) { base::TimeDelta seek_time) {
DCHECK_EQ(player_id_, player_id); DCHECK_EQ(player_id_, player_id);
web_contents_->media_web_contents_observer()
->GetMediaPlayerRemote(id_) media::mojom::MediaPlayer* remote =
->RequestSeekForward(seek_time); web_contents_->media_web_contents_observer()->GetMediaPlayerRemote(id_);
if (!remote) {
// TODO(https://crbug.com/1161551): Remove this when lifetime bug is fixed.
NOTREACHED() << "Controller should not outlive remote MediaPlayer";
return;
}
remote->RequestSeekForward(seek_time);
} }
void MediaSessionController::OnSeekBackward(int player_id, void MediaSessionController::OnSeekBackward(int player_id,
base::TimeDelta seek_time) { base::TimeDelta seek_time) {
DCHECK_EQ(player_id_, player_id); DCHECK_EQ(player_id_, player_id);
web_contents_->media_web_contents_observer()
->GetMediaPlayerRemote(id_) media::mojom::MediaPlayer* remote =
->RequestSeekBackward(seek_time); web_contents_->media_web_contents_observer()->GetMediaPlayerRemote(id_);
if (!remote) {
// TODO(https://crbug.com/1161551): Remove this when lifetime bug is fixed.
NOTREACHED() << "Controller should not outlive remote MediaPlayer";
return;
}
remote->RequestSeekBackward(seek_time);
} }
void MediaSessionController::OnSetVolumeMultiplier(int player_id, void MediaSessionController::OnSetVolumeMultiplier(int player_id,
...@@ -91,17 +119,29 @@ void MediaSessionController::OnSetVolumeMultiplier(int player_id, ...@@ -91,17 +119,29 @@ void MediaSessionController::OnSetVolumeMultiplier(int player_id,
void MediaSessionController::OnEnterPictureInPicture(int player_id) { void MediaSessionController::OnEnterPictureInPicture(int player_id) {
DCHECK_EQ(player_id_, player_id); DCHECK_EQ(player_id_, player_id);
web_contents_->media_web_contents_observer() media::mojom::MediaPlayer* remote =
->GetMediaPlayerRemote(id_) web_contents_->media_web_contents_observer()->GetMediaPlayerRemote(id_);
->RequestEnterPictureInPicture(); if (!remote) {
// TODO(https://crbug.com/1161551): Remove this when lifetime bug is fixed.
NOTREACHED() << "Controller should not outlive remote MediaPlayer";
return;
}
remote->RequestEnterPictureInPicture();
} }
void MediaSessionController::OnExitPictureInPicture(int player_id) { void MediaSessionController::OnExitPictureInPicture(int player_id) {
DCHECK_EQ(player_id_, player_id); DCHECK_EQ(player_id_, player_id);
web_contents_->media_web_contents_observer() media::mojom::MediaPlayer* remote =
->GetMediaPlayerRemote(id_) web_contents_->media_web_contents_observer()->GetMediaPlayerRemote(id_);
->RequestExitPictureInPicture(); if (!remote) {
// TODO(https://crbug.com/1161551): Remove this when lifetime bug is fixed.
NOTREACHED() << "Controller should not outlive remote MediaPlayer";
return;
}
remote->RequestExitPictureInPicture();
} }
void MediaSessionController::OnSetAudioSinkId( void MediaSessionController::OnSetAudioSinkId(
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment