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