tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ContentPlaybackController.cpp (8616B)


      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/ContentMediaController.h"
      9 #include "mozilla/dom/MediaSession.h"
     10 #include "mozilla/dom/Navigator.h"
     11 #include "mozilla/dom/WindowContext.h"
     12 #include "nsFocusManager.h"
     13 
     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__))
     19 
     20 namespace mozilla::dom {
     21 
     22 ContentPlaybackController::ContentPlaybackController(
     23    BrowsingContext* aContext) {
     24  MOZ_ASSERT(aContext);
     25  mBC = aContext;
     26 }
     27 
     28 MediaSession* ContentPlaybackController::GetMediaSession() const {
     29  RefPtr<nsPIDOMWindowOuter> window = mBC->GetDOMWindow();
     30  if (!window) {
     31    return nullptr;
     32  }
     33 
     34  RefPtr<Navigator> navigator = window->GetNavigator();
     35  if (!navigator) {
     36    return nullptr;
     37  }
     38 
     39  return navigator->HasCreatedMediaSession() ? navigator->MediaSession()
     40                                             : nullptr;
     41 }
     42 
     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);
     50  }
     51 }
     52 
     53 void ContentPlaybackController::NotifyMediaSession(MediaSessionAction aAction) {
     54  MediaSessionActionDetails details;
     55  details.mAction = aAction;
     56  NotifyMediaSession(details);
     57 }
     58 
     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);
     66  }
     67 }
     68 
     69 void ContentPlaybackController::NotifyMediaSessionWhenActionIsSupported(
     70    MediaSessionAction aAction) {
     71  if (IsMediaSessionActionSupported(aAction)) {
     72    NotifyMediaSession(aAction);
     73  }
     74 }
     75 
     76 bool ContentPlaybackController::IsMediaSessionActionSupported(
     77    MediaSessionAction aAction) const {
     78  RefPtr<MediaSession> session = GetMediaSession();
     79  return session ? session->IsActive() && session->IsSupportedAction(aAction)
     80                 : false;
     81 }
     82 
     83 Maybe<uint64_t> ContentPlaybackController::GetActiveMediaSessionId() const {
     84  RefPtr<WindowContext> wc = mBC->GetTopWindowContext();
     85  return wc ? wc->GetActiveMediaSessionContextId() : Nothing();
     86 }
     87 
     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);
     93  }
     94 }
     95 
     96 void ContentPlaybackController::Play() {
     97  const MediaSessionAction action = MediaSessionAction::Play;
     98  RefPtr<MediaSession> session = GetMediaSession();
     99  if (IsMediaSessionActionSupported(action)) {
    100    NotifyMediaSession(action);
    101  }
    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);
    110  }
    111 }
    112 
    113 void ContentPlaybackController::Pause() {
    114  const MediaSessionAction action = MediaSessionAction::Pause;
    115  if (IsMediaSessionActionSupported(action)) {
    116    NotifyMediaSession(action);
    117  } else {
    118    NotifyContentMediaControlKeyReceiver(MediaControlKey::Pause);
    119  }
    120 }
    121 
    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)));
    132  }
    133 }
    134 
    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)));
    145  }
    146 }
    147 
    148 void ContentPlaybackController::PreviousTrack() {
    149  NotifyMediaSessionWhenActionIsSupported(MediaSessionAction::Previoustrack);
    150 }
    151 
    152 void ContentPlaybackController::NextTrack() {
    153  NotifyMediaSessionWhenActionIsSupported(MediaSessionAction::Nexttrack);
    154 }
    155 
    156 void ContentPlaybackController::SkipAd() {
    157  NotifyMediaSessionWhenActionIsSupported(MediaSessionAction::Skipad);
    158 }
    159 
    160 void ContentPlaybackController::Stop() {
    161  const MediaSessionAction action = MediaSessionAction::Stop;
    162  if (IsMediaSessionActionSupported(action)) {
    163    NotifyMediaSession(action);
    164  } else {
    165    NotifyContentMediaControlKeyReceiver(MediaControlKey::Stop);
    166  }
    167 }
    168 
    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);
    176  }
    177  if (IsMediaSessionActionSupported(details.mAction)) {
    178    NotifyMediaSession(details);
    179  } else if (!GetActiveMediaSessionId() || (session && session->IsActive())) {
    180    NotifyContentMediaControlKeyReceiver(
    181        MediaControlKey::Seekto, Some(SeekDetails(aSeekTime, aFastSeek)));
    182  }
    183 }
    184 
    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;
    191  }
    192  if (aAction.mKey.isNothing()) {
    193    MOZ_ASSERT_UNREACHABLE("Invalid media control key.");
    194    return;
    195  }
    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;
    224    }
    225    case MediaControlKey::Seekforward: {
    226      const SeekDetails& details = *aAction.mDetails;
    227      MOZ_ASSERT(details.mRelativeSeekOffset);
    228      controller.SeekForward(details.mRelativeSeekOffset.value());
    229      return;
    230    }
    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;
    240    }
    241    default:
    242      MOZ_ASSERT_UNREACHABLE("Invalid media control key.");
    243  };
    244 }
    245 
    246 }  // namespace mozilla::dom