Backed out 7 changesets (bug 1942424) for causing frequent crashes. a=backout
[gecko.git] / dom / media / mediacontrol / ContentPlaybackController.cpp
blob894ad6c992a328a3d72bedddbc83c2ef17c8bf5e
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #include "ContentPlaybackController.h"
7 #include "MediaControlUtils.h"
8 #include "mozilla/dom/ContentMediaController.h"
9 #include "mozilla/dom/MediaSession.h"
10 #include "mozilla/dom/Navigator.h"
11 #include "mozilla/dom/WindowContext.h"
12 #include "nsFocusManager.h"
14 // avoid redefined macro in unified build
15 #undef LOG
16 #define LOG(msg, ...) \
17 MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
18 ("ContentPlaybackController=%p, " msg, this, ##__VA_ARGS__))
20 namespace mozilla::dom {
22 ContentPlaybackController::ContentPlaybackController(
23 BrowsingContext* aContext) {
24 MOZ_ASSERT(aContext);
25 mBC = aContext;
28 MediaSession* ContentPlaybackController::GetMediaSession() const {
29 RefPtr<nsPIDOMWindowOuter> window = mBC->GetDOMWindow();
30 if (!window) {
31 return nullptr;
34 RefPtr<Navigator> navigator = window->GetNavigator();
35 if (!navigator) {
36 return nullptr;
39 return navigator->HasCreatedMediaSession() ? navigator->MediaSession()
40 : nullptr;
43 void ContentPlaybackController::NotifyContentMediaControlKeyReceiver(
44 MediaControlKey aKey, Maybe<SeekDetails> aDetails) {
45 if (RefPtr<ContentMediaControlKeyReceiver> receiver =
46 ContentMediaControlKeyReceiver::Get(mBC)) {
47 LOG("Handle '%s' in default behavior for BC %" PRIu64,
48 GetEnumString(aKey).get(), mBC->Id());
49 receiver->HandleMediaKey(aKey, aDetails);
53 void ContentPlaybackController::NotifyMediaSession(MediaSessionAction aAction) {
54 MediaSessionActionDetails details;
55 details.mAction = aAction;
56 NotifyMediaSession(details);
59 void ContentPlaybackController::NotifyMediaSession(
60 const MediaSessionActionDetails& aDetails) {
61 if (RefPtr<MediaSession> session = GetMediaSession()) {
62 LOG("Handle '%s' in media session behavior for BC %" PRIu64,
63 GetEnumString(aDetails.mAction).get(), mBC->Id());
64 MOZ_ASSERT(session->IsActive(), "Notify inactive media session!");
65 session->NotifyHandler(aDetails);
69 void ContentPlaybackController::NotifyMediaSessionWhenActionIsSupported(
70 MediaSessionAction aAction) {
71 if (IsMediaSessionActionSupported(aAction)) {
72 NotifyMediaSession(aAction);
76 bool ContentPlaybackController::IsMediaSessionActionSupported(
77 MediaSessionAction aAction) const {
78 RefPtr<MediaSession> session = GetMediaSession();
79 return session ? session->IsActive() && session->IsSupportedAction(aAction)
80 : false;
83 Maybe<uint64_t> ContentPlaybackController::GetActiveMediaSessionId() const {
84 RefPtr<WindowContext> wc = mBC->GetTopWindowContext();
85 return wc ? wc->GetActiveMediaSessionContextId() : Nothing();
88 void ContentPlaybackController::Focus() {
89 // Focus is not part of the MediaSession standard, so always use the
90 // default behavior and focus the window currently playing media.
91 if (nsCOMPtr<nsPIDOMWindowOuter> win = mBC->GetDOMWindow()) {
92 nsFocusManager::FocusWindow(win, CallerType::System);
96 void ContentPlaybackController::Play() {
97 const MediaSessionAction action = MediaSessionAction::Play;
98 RefPtr<MediaSession> session = GetMediaSession();
99 if (IsMediaSessionActionSupported(action)) {
100 NotifyMediaSession(action);
102 // We don't want to arbitrarily call play default handler, because we want to
103 // resume the frame which a user really gets interest in, not all media in the
104 // same page. Therefore, we would only call default handler for `play` when
105 // (1) We don't have an active media session (If we have one, the play action
106 // handler should only be triggered on that session)
107 // (2) Active media session without setting action handler for `play`
108 else if (!GetActiveMediaSessionId() || (session && session->IsActive())) {
109 NotifyContentMediaControlKeyReceiver(MediaControlKey::Play);
113 void ContentPlaybackController::Pause() {
114 const MediaSessionAction action = MediaSessionAction::Pause;
115 if (IsMediaSessionActionSupported(action)) {
116 NotifyMediaSession(action);
117 } else {
118 NotifyContentMediaControlKeyReceiver(MediaControlKey::Pause);
122 void ContentPlaybackController::SeekBackward(double aSeekOffset) {
123 MediaSessionActionDetails details;
124 details.mAction = MediaSessionAction::Seekbackward;
125 details.mSeekOffset.Construct(aSeekOffset);
126 RefPtr<MediaSession> session = GetMediaSession();
127 if (IsMediaSessionActionSupported(details.mAction)) {
128 NotifyMediaSession(details);
129 } else if (!GetActiveMediaSessionId() || (session && session->IsActive())) {
130 NotifyContentMediaControlKeyReceiver(MediaControlKey::Seekbackward,
131 Some(SeekDetails(aSeekOffset)));
135 void ContentPlaybackController::SeekForward(double aSeekOffset) {
136 MediaSessionActionDetails details;
137 details.mAction = MediaSessionAction::Seekforward;
138 details.mSeekOffset.Construct(aSeekOffset);
139 RefPtr<MediaSession> session = GetMediaSession();
140 if (IsMediaSessionActionSupported(details.mAction)) {
141 NotifyMediaSession(details);
142 } else if (!GetActiveMediaSessionId() || (session && session->IsActive())) {
143 NotifyContentMediaControlKeyReceiver(MediaControlKey::Seekforward,
144 Some(SeekDetails(aSeekOffset)));
148 void ContentPlaybackController::PreviousTrack() {
149 NotifyMediaSessionWhenActionIsSupported(MediaSessionAction::Previoustrack);
152 void ContentPlaybackController::NextTrack() {
153 NotifyMediaSessionWhenActionIsSupported(MediaSessionAction::Nexttrack);
156 void ContentPlaybackController::SkipAd() {
157 NotifyMediaSessionWhenActionIsSupported(MediaSessionAction::Skipad);
160 void ContentPlaybackController::Stop() {
161 const MediaSessionAction action = MediaSessionAction::Stop;
162 if (IsMediaSessionActionSupported(action)) {
163 NotifyMediaSession(action);
164 } else {
165 NotifyContentMediaControlKeyReceiver(MediaControlKey::Stop);
169 void ContentPlaybackController::SeekTo(double aSeekTime, bool aFastSeek) {
170 MediaSessionActionDetails details;
171 details.mAction = MediaSessionAction::Seekto;
172 details.mSeekTime.Construct(aSeekTime);
173 RefPtr<MediaSession> session = GetMediaSession();
174 if (aFastSeek) {
175 details.mFastSeek.Construct(aFastSeek);
177 if (IsMediaSessionActionSupported(details.mAction)) {
178 NotifyMediaSession(details);
179 } else if (!GetActiveMediaSessionId() || (session && session->IsActive())) {
180 NotifyContentMediaControlKeyReceiver(
181 MediaControlKey::Seekto, Some(SeekDetails(aSeekTime, aFastSeek)));
185 void ContentMediaControlKeyHandler::HandleMediaControlAction(
186 BrowsingContext* aContext, const MediaControlAction& aAction) {
187 MOZ_ASSERT(aContext);
188 // The web content doesn't exist in this browsing context.
189 if (!aContext->GetDocShell()) {
190 return;
192 if (aAction.mKey.isNothing()) {
193 MOZ_ASSERT_UNREACHABLE("Invalid media control key.");
194 return;
196 ContentPlaybackController controller(aContext);
197 switch (aAction.mKey.value()) {
198 case MediaControlKey::Focus:
199 controller.Focus();
200 return;
201 case MediaControlKey::Play:
202 controller.Play();
203 return;
204 case MediaControlKey::Pause:
205 controller.Pause();
206 return;
207 case MediaControlKey::Playpause:
208 MOZ_ASSERT_UNREACHABLE("Invalid media control key.");
209 return;
210 case MediaControlKey::Stop:
211 controller.Stop();
212 return;
213 case MediaControlKey::Previoustrack:
214 controller.PreviousTrack();
215 return;
216 case MediaControlKey::Nexttrack:
217 controller.NextTrack();
218 return;
219 case MediaControlKey::Seekbackward: {
220 const SeekDetails& details = *aAction.mDetails;
221 MOZ_ASSERT(details.mRelativeSeekOffset);
222 controller.SeekBackward(details.mRelativeSeekOffset.value());
223 return;
225 case MediaControlKey::Seekforward: {
226 const SeekDetails& details = *aAction.mDetails;
227 MOZ_ASSERT(details.mRelativeSeekOffset);
228 controller.SeekForward(details.mRelativeSeekOffset.value());
229 return;
231 case MediaControlKey::Skipad:
232 controller.SkipAd();
233 return;
234 case MediaControlKey::Seekto: {
235 const SeekDetails& details = *aAction.mDetails;
236 MOZ_ASSERT(details.mAbsolute);
237 controller.SeekTo(details.mAbsolute->mSeekTime,
238 details.mAbsolute->mFastSeek);
239 return;
241 default:
242 MOZ_ASSERT_UNREACHABLE("Invalid media control key.");
246 } // namespace mozilla::dom