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/. */
4 
5 #include "ContentPlaybackController.h"
6 
7 #include "MediaControlUtils.h"
8 #include "mozilla/dom/MediaSession.h"
9 #include "mozilla/dom/Navigator.h"
10 #include "nsFocusManager.h"
11 
12 // avoid redefined macro in unified build
13 #undef LOG
14 #define LOG(msg, ...)                        \
15   MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
16           ("ContentPlaybackController=%p, " msg, this, ##__VA_ARGS__))
17 
18 namespace mozilla {
19 namespace dom {
20 
ContentPlaybackController(BrowsingContext * aContext)21 ContentPlaybackController::ContentPlaybackController(
22     BrowsingContext* aContext) {
23   MOZ_ASSERT(aContext);
24   mBC = aContext;
25 }
26 
GetMediaSession() const27 MediaSession* ContentPlaybackController::GetMediaSession() const {
28   RefPtr<nsPIDOMWindowOuter> window = mBC->GetDOMWindow();
29   if (!window) {
30     return nullptr;
31   }
32 
33   RefPtr<Navigator> navigator = window->GetNavigator();
34   return navigator->HasCreatedMediaSession() ? navigator->MediaSession()
35                                              : nullptr;
36 }
37 
NotifyContentControlKeyEventReceiver(MediaControlKeysEvent aEvent)38 void ContentPlaybackController::NotifyContentControlKeyEventReceiver(
39     MediaControlKeysEvent aEvent) {
40   if (RefPtr<ContentControlKeyEventReceiver> receiver =
41           ContentControlKeyEventReceiver::Get(mBC)) {
42     LOG("Handle '%s' in default behavior", ToMediaControlKeysEventStr(aEvent));
43     receiver->HandleEvent(aEvent);
44   }
45 }
46 
NotifyMediaSession(MediaSessionAction aAction)47 void ContentPlaybackController::NotifyMediaSession(MediaSessionAction aAction) {
48   if (RefPtr<MediaSession> session = GetMediaSession()) {
49     LOG("Handle '%s' in media session behavior",
50         ToMediaSessionActionStr(aAction));
51     session->NotifyHandler(aAction);
52   }
53 }
54 
NotifyMediaSessionWhenActionIsSupported(MediaSessionAction aAction)55 void ContentPlaybackController::NotifyMediaSessionWhenActionIsSupported(
56     MediaSessionAction aAction) {
57   if (IsMediaSessionActionSupported(aAction)) {
58     NotifyMediaSession(aAction);
59   }
60 }
61 
IsMediaSessionActionSupported(MediaSessionAction aAction) const62 bool ContentPlaybackController::IsMediaSessionActionSupported(
63     MediaSessionAction aAction) const {
64   RefPtr<MediaSession> session = GetMediaSession();
65   return session ? session->IsSupportedAction(aAction) : false;
66 }
67 
Focus()68 void ContentPlaybackController::Focus() {
69   // Focus is not part of the MediaSession standard, so always use the
70   // default behavior and focus the window currently playing media.
71   if (RefPtr<nsPIDOMWindowOuter> win = mBC->GetDOMWindow()) {
72     nsFocusManager::FocusWindow(win, CallerType::System);
73   }
74 }
75 
Play()76 void ContentPlaybackController::Play() {
77   const MediaSessionAction action = MediaSessionAction::Play;
78   if (IsMediaSessionActionSupported(action)) {
79     NotifyMediaSession(action);
80   } else {
81     NotifyContentControlKeyEventReceiver(MediaControlKeysEvent::ePlay);
82   }
83 }
84 
Pause()85 void ContentPlaybackController::Pause() {
86   const MediaSessionAction action = MediaSessionAction::Pause;
87   if (IsMediaSessionActionSupported(action)) {
88     NotifyMediaSession(action);
89   } else {
90     NotifyContentControlKeyEventReceiver(MediaControlKeysEvent::ePause);
91   }
92 }
93 
SeekBackward()94 void ContentPlaybackController::SeekBackward() {
95   NotifyMediaSessionWhenActionIsSupported(MediaSessionAction::Seekbackward);
96 }
97 
SeekForward()98 void ContentPlaybackController::SeekForward() {
99   NotifyMediaSessionWhenActionIsSupported(MediaSessionAction::Seekforward);
100 }
101 
PreviousTrack()102 void ContentPlaybackController::PreviousTrack() {
103   NotifyMediaSessionWhenActionIsSupported(MediaSessionAction::Previoustrack);
104 }
105 
NextTrack()106 void ContentPlaybackController::NextTrack() {
107   NotifyMediaSessionWhenActionIsSupported(MediaSessionAction::Nexttrack);
108 }
109 
SkipAd()110 void ContentPlaybackController::SkipAd() {
111   // TODO : use media session's action handler if it exists. MediaSessionAction
112   // doesn't support `skipad` yet.
113   return;
114 }
115 
Stop()116 void ContentPlaybackController::Stop() {
117   const MediaSessionAction action = MediaSessionAction::Stop;
118   if (IsMediaSessionActionSupported(action)) {
119     NotifyMediaSession(action);
120   } else {
121     NotifyContentControlKeyEventReceiver(MediaControlKeysEvent::eStop);
122   }
123 }
124 
SeekTo()125 void ContentPlaybackController::SeekTo() {
126   // TODO : use media session's action handler if it exists. MediaSessionAction
127   // doesn't support `seekto` yet.
128   return;
129 }
130 
HandleMediaControlKeysEvent(BrowsingContext * aContext,MediaControlKeysEvent aEvent)131 void ContentMediaActionHandler::HandleMediaControlKeysEvent(
132     BrowsingContext* aContext, MediaControlKeysEvent aEvent) {
133   ContentPlaybackController controller(aContext);
134   switch (aEvent) {
135     case MediaControlKeysEvent::eFocus:
136       controller.Focus();
137       return;
138     case MediaControlKeysEvent::ePlay:
139       controller.Play();
140       return;
141     case MediaControlKeysEvent::ePause:
142       controller.Pause();
143       return;
144     case MediaControlKeysEvent::eStop:
145       controller.Stop();
146       return;
147     case MediaControlKeysEvent::ePrevTrack:
148       controller.PreviousTrack();
149       return;
150     case MediaControlKeysEvent::eNextTrack:
151       controller.NextTrack();
152       return;
153     case MediaControlKeysEvent::eSeekBackward:
154       controller.SeekBackward();
155       return;
156     case MediaControlKeysEvent::eSeekForward:
157       controller.SeekForward();
158       return;
159     default:
160       MOZ_ASSERT_UNREACHABLE("Invalid event.");
161   };
162 }
163 
164 }  // namespace dom
165 }  // namespace mozilla
166