mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 10:18:41 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			524 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			524 lines
		
	
	
	
		
			19 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
 | 
						|
 * You can obtain one at http://mozilla.org/MPL/2.0/. */
 | 
						|
 | 
						|
#include "MediaStatusManager.h"
 | 
						|
 | 
						|
#include "MediaControlService.h"
 | 
						|
#include "mozilla/dom/CanonicalBrowsingContext.h"
 | 
						|
#include "mozilla/dom/Element.h"
 | 
						|
#include "mozilla/dom/MediaControlUtils.h"
 | 
						|
#include "mozilla/dom/WindowGlobalParent.h"
 | 
						|
#include "mozilla/StaticPrefs_media.h"
 | 
						|
#include "nsContentUtils.h"
 | 
						|
#include "nsIChromeRegistry.h"
 | 
						|
#include "nsIObserverService.h"
 | 
						|
#include "nsIXULAppInfo.h"
 | 
						|
#include "nsNetUtil.h"
 | 
						|
 | 
						|
#ifdef MOZ_PLACES
 | 
						|
#  include "nsIFaviconService.h"
 | 
						|
#endif  // MOZ_PLACES
 | 
						|
 | 
						|
extern mozilla::LazyLogModule gMediaControlLog;
 | 
						|
 | 
						|
// avoid redefined macro in unified build
 | 
						|
#undef LOG
 | 
						|
#define LOG(msg, ...)                        \
 | 
						|
  MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
 | 
						|
          ("MediaStatusManager=%p, " msg, this, ##__VA_ARGS__))
 | 
						|
 | 
						|
namespace mozilla::dom {
 | 
						|
 | 
						|
static bool IsMetadataEmpty(const Maybe<MediaMetadataBase>& aMetadata) {
 | 
						|
  // Media session's metadata is null.
 | 
						|
  if (!aMetadata) {
 | 
						|
    return true;
 | 
						|
  }
 | 
						|
 | 
						|
  // All attirbutes in metadata are empty.
 | 
						|
  // https://w3c.github.io/mediasession/#empty-metadata
 | 
						|
  const MediaMetadataBase& metadata = *aMetadata;
 | 
						|
  return metadata.mTitle.IsEmpty() && metadata.mArtist.IsEmpty() &&
 | 
						|
         metadata.mAlbum.IsEmpty() && metadata.mArtwork.IsEmpty();
 | 
						|
}
 | 
						|
 | 
						|
MediaStatusManager::MediaStatusManager(uint64_t aBrowsingContextId)
 | 
						|
    : mTopLevelBrowsingContextId(aBrowsingContextId) {
 | 
						|
  MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(),
 | 
						|
                        "MediaStatusManager only runs on Chrome process!");
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::NotifyMediaAudibleChanged(uint64_t aBrowsingContextId,
 | 
						|
                                                   MediaAudibleState aState) {
 | 
						|
  Maybe<uint64_t> oldAudioFocusOwnerId =
 | 
						|
      mPlaybackStatusDelegate.GetAudioFocusOwnerContextId();
 | 
						|
  mPlaybackStatusDelegate.UpdateMediaAudibleState(aBrowsingContextId, aState);
 | 
						|
  Maybe<uint64_t> newAudioFocusOwnerId =
 | 
						|
      mPlaybackStatusDelegate.GetAudioFocusOwnerContextId();
 | 
						|
  if (oldAudioFocusOwnerId != newAudioFocusOwnerId) {
 | 
						|
    HandleAudioFocusOwnerChanged(newAudioFocusOwnerId);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::NotifySessionCreated(uint64_t aBrowsingContextId) {
 | 
						|
  const bool created = mMediaSessionInfoMap.WithEntryHandle(
 | 
						|
      aBrowsingContextId, [&](auto&& entry) {
 | 
						|
        if (entry) return false;
 | 
						|
 | 
						|
        LOG("Session %" PRIu64 " has been created", aBrowsingContextId);
 | 
						|
        entry.Insert(MediaSessionInfo::EmptyInfo());
 | 
						|
        return true;
 | 
						|
      });
 | 
						|
 | 
						|
  if (created && IsSessionOwningAudioFocus(aBrowsingContextId)) {
 | 
						|
    // This can't be done from within the WithEntryHandle functor, since it
 | 
						|
    // accesses mMediaSessionInfoMap.
 | 
						|
    SetActiveMediaSessionContextId(aBrowsingContextId);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::NotifySessionDestroyed(uint64_t aBrowsingContextId) {
 | 
						|
  if (mMediaSessionInfoMap.Remove(aBrowsingContextId)) {
 | 
						|
    LOG("Session %" PRIu64 " has been destroyed", aBrowsingContextId);
 | 
						|
 | 
						|
    if (mActiveMediaSessionContextId &&
 | 
						|
        *mActiveMediaSessionContextId == aBrowsingContextId) {
 | 
						|
      ClearActiveMediaSessionContextIdIfNeeded();
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::UpdateMetadata(
 | 
						|
    uint64_t aBrowsingContextId, const Maybe<MediaMetadataBase>& aMetadata) {
 | 
						|
  auto info = mMediaSessionInfoMap.Lookup(aBrowsingContextId);
 | 
						|
  if (!info) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (IsMetadataEmpty(aMetadata)) {
 | 
						|
    LOG("Reset metadata for session %" PRIu64, aBrowsingContextId);
 | 
						|
    info->mMetadata.reset();
 | 
						|
  } else {
 | 
						|
    LOG("Update metadata for session %" PRIu64 " title=%s artist=%s album=%s",
 | 
						|
        aBrowsingContextId, NS_ConvertUTF16toUTF8((*aMetadata).mTitle).get(),
 | 
						|
        NS_ConvertUTF16toUTF8(aMetadata->mArtist).get(),
 | 
						|
        NS_ConvertUTF16toUTF8(aMetadata->mAlbum).get());
 | 
						|
    info->mMetadata = aMetadata;
 | 
						|
  }
 | 
						|
  // Only notify the event if the changed metadata belongs to the active media
 | 
						|
  // session.
 | 
						|
  if (mActiveMediaSessionContextId &&
 | 
						|
      *mActiveMediaSessionContextId == aBrowsingContextId) {
 | 
						|
    LOG("Notify metadata change for active session %" PRIu64,
 | 
						|
        aBrowsingContextId);
 | 
						|
    mMetadataChangedEvent.Notify(GetCurrentMediaMetadata());
 | 
						|
  }
 | 
						|
  if (StaticPrefs::media_mediacontrol_testingevents_enabled()) {
 | 
						|
    if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
 | 
						|
      obs->NotifyObservers(nullptr, "media-session-controller-metadata-changed",
 | 
						|
                           nullptr);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::HandleAudioFocusOwnerChanged(
 | 
						|
    Maybe<uint64_t>& aBrowsingContextId) {
 | 
						|
  // No one is holding the audio focus.
 | 
						|
  if (!aBrowsingContextId) {
 | 
						|
    LOG("No one is owning audio focus");
 | 
						|
    return ClearActiveMediaSessionContextIdIfNeeded();
 | 
						|
  }
 | 
						|
 | 
						|
  // This owner of audio focus doesn't have media session, so we should deactive
 | 
						|
  // the active session because the active session must own the audio focus.
 | 
						|
  if (!mMediaSessionInfoMap.Contains(*aBrowsingContextId)) {
 | 
						|
    LOG("The owner of audio focus doesn't have media session");
 | 
						|
    return ClearActiveMediaSessionContextIdIfNeeded();
 | 
						|
  }
 | 
						|
 | 
						|
  // This owner has media session so it should become an active session context.
 | 
						|
  SetActiveMediaSessionContextId(*aBrowsingContextId);
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::SetActiveMediaSessionContextId(
 | 
						|
    uint64_t aBrowsingContextId) {
 | 
						|
  if (mActiveMediaSessionContextId &&
 | 
						|
      *mActiveMediaSessionContextId == aBrowsingContextId) {
 | 
						|
    LOG("Active session context %" PRIu64 " keeps unchanged",
 | 
						|
        *mActiveMediaSessionContextId);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  mActiveMediaSessionContextId = Some(aBrowsingContextId);
 | 
						|
  StoreMediaSessionContextIdOnWindowContext();
 | 
						|
  LOG("context %" PRIu64 " becomes active session context",
 | 
						|
      *mActiveMediaSessionContextId);
 | 
						|
  mMetadataChangedEvent.Notify(GetCurrentMediaMetadata());
 | 
						|
  mSupportedActionsChangedEvent.Notify(GetSupportedActions());
 | 
						|
  mPositionStateChangedEvent.Notify(GetCurrentPositionState());
 | 
						|
  if (StaticPrefs::media_mediacontrol_testingevents_enabled()) {
 | 
						|
    if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
 | 
						|
      obs->NotifyObservers(nullptr, "active-media-session-changed", nullptr);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::ClearActiveMediaSessionContextIdIfNeeded() {
 | 
						|
  if (!mActiveMediaSessionContextId) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  LOG("Clear active session context");
 | 
						|
  mActiveMediaSessionContextId.reset();
 | 
						|
  StoreMediaSessionContextIdOnWindowContext();
 | 
						|
  mMetadataChangedEvent.Notify(GetCurrentMediaMetadata());
 | 
						|
  mSupportedActionsChangedEvent.Notify(GetSupportedActions());
 | 
						|
  mPositionStateChangedEvent.Notify(GetCurrentPositionState());
 | 
						|
  if (StaticPrefs::media_mediacontrol_testingevents_enabled()) {
 | 
						|
    if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
 | 
						|
      obs->NotifyObservers(nullptr, "active-media-session-changed", nullptr);
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::StoreMediaSessionContextIdOnWindowContext() {
 | 
						|
  RefPtr<CanonicalBrowsingContext> bc =
 | 
						|
      CanonicalBrowsingContext::Get(mTopLevelBrowsingContextId);
 | 
						|
  if (bc && bc->GetTopWindowContext()) {
 | 
						|
    Unused << bc->GetTopWindowContext()->SetActiveMediaSessionContextId(
 | 
						|
        mActiveMediaSessionContextId);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool MediaStatusManager::IsSessionOwningAudioFocus(
 | 
						|
    uint64_t aBrowsingContextId) const {
 | 
						|
  Maybe<uint64_t> audioFocusContextId =
 | 
						|
      mPlaybackStatusDelegate.GetAudioFocusOwnerContextId();
 | 
						|
  return audioFocusContextId ? *audioFocusContextId == aBrowsingContextId
 | 
						|
                             : false;
 | 
						|
}
 | 
						|
 | 
						|
MediaMetadataBase MediaStatusManager::CreateDefaultMetadata() const {
 | 
						|
  MediaMetadataBase metadata;
 | 
						|
  metadata.mTitle = GetDefaultTitle();
 | 
						|
  metadata.mArtwork.AppendElement()->mSrc = GetDefaultFaviconURL();
 | 
						|
 | 
						|
  LOG("Default media metadata, title=%s, album src=%s",
 | 
						|
      NS_ConvertUTF16toUTF8(metadata.mTitle).get(),
 | 
						|
      NS_ConvertUTF16toUTF8(metadata.mArtwork[0].mSrc).get());
 | 
						|
  return metadata;
 | 
						|
}
 | 
						|
 | 
						|
nsString MediaStatusManager::GetDefaultTitle() const {
 | 
						|
  RefPtr<MediaControlService> service = MediaControlService::GetService();
 | 
						|
  nsString defaultTitle = service->GetFallbackTitle();
 | 
						|
 | 
						|
  RefPtr<CanonicalBrowsingContext> bc =
 | 
						|
      CanonicalBrowsingContext::Get(mTopLevelBrowsingContextId);
 | 
						|
  if (!bc) {
 | 
						|
    return defaultTitle;
 | 
						|
  }
 | 
						|
 | 
						|
  RefPtr<WindowGlobalParent> globalParent = bc->GetCurrentWindowGlobal();
 | 
						|
  if (!globalParent) {
 | 
						|
    return defaultTitle;
 | 
						|
  }
 | 
						|
 | 
						|
  // The media metadata would be shown on the virtual controller interface. For
 | 
						|
  // example, on Android, the interface would be shown on both notification bar
 | 
						|
  // and lockscreen. Therefore, what information we provide via metadata is
 | 
						|
  // quite important, because if we're in private browsing, we don't want to
 | 
						|
  // expose details about what website the user is browsing on the lockscreen.
 | 
						|
  // Therefore, using the default title when in the private browsing or the
 | 
						|
  // document title is empty. Otherwise, use the document title.
 | 
						|
  nsString documentTitle;
 | 
						|
  if (!IsInPrivateBrowsing()) {
 | 
						|
    globalParent->GetDocumentTitle(documentTitle);
 | 
						|
  }
 | 
						|
  return documentTitle.IsEmpty() ? defaultTitle : documentTitle;
 | 
						|
}
 | 
						|
 | 
						|
nsString MediaStatusManager::GetDefaultFaviconURL() const {
 | 
						|
#ifdef MOZ_PLACES
 | 
						|
  nsCOMPtr<nsIURI> faviconURI;
 | 
						|
  nsresult rv = NS_NewURI(getter_AddRefs(faviconURI),
 | 
						|
                          nsLiteralCString(FAVICON_DEFAULT_URL));
 | 
						|
  NS_ENSURE_SUCCESS(rv, u""_ns);
 | 
						|
 | 
						|
  // Convert URI from `chrome://XXX` to `file://XXX` because we would like to
 | 
						|
  // let OS related frameworks, such as SMTC and MPRIS, handle this URL in order
 | 
						|
  // to show the icon on virtual controller interface.
 | 
						|
  nsCOMPtr<nsIChromeRegistry> regService = services::GetChromeRegistry();
 | 
						|
  if (!regService) {
 | 
						|
    return u""_ns;
 | 
						|
  }
 | 
						|
  nsCOMPtr<nsIURI> processedURI;
 | 
						|
  regService->ConvertChromeURL(faviconURI, getter_AddRefs(processedURI));
 | 
						|
 | 
						|
  nsAutoCString spec;
 | 
						|
  if (NS_FAILED(processedURI->GetSpec(spec))) {
 | 
						|
    return u""_ns;
 | 
						|
  }
 | 
						|
  return NS_ConvertUTF8toUTF16(spec);
 | 
						|
#else
 | 
						|
  return u""_ns;
 | 
						|
#endif
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::SetDeclaredPlaybackState(
 | 
						|
    uint64_t aBrowsingContextId, MediaSessionPlaybackState aState) {
 | 
						|
  auto info = mMediaSessionInfoMap.Lookup(aBrowsingContextId);
 | 
						|
  if (!info) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  LOG("SetDeclaredPlaybackState from %s to %s",
 | 
						|
      ToMediaSessionPlaybackStateStr(info->mDeclaredPlaybackState),
 | 
						|
      ToMediaSessionPlaybackStateStr(aState));
 | 
						|
  info->mDeclaredPlaybackState = aState;
 | 
						|
  UpdateActualPlaybackState();
 | 
						|
}
 | 
						|
 | 
						|
MediaSessionPlaybackState MediaStatusManager::GetCurrentDeclaredPlaybackState()
 | 
						|
    const {
 | 
						|
  if (!mActiveMediaSessionContextId) {
 | 
						|
    return MediaSessionPlaybackState::None;
 | 
						|
  }
 | 
						|
  return mMediaSessionInfoMap.Get(*mActiveMediaSessionContextId)
 | 
						|
      .mDeclaredPlaybackState;
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::NotifyMediaPlaybackChanged(uint64_t aBrowsingContextId,
 | 
						|
                                                    MediaPlaybackState aState) {
 | 
						|
  LOG("UpdateMediaPlaybackState %s for context %" PRIu64,
 | 
						|
      ToMediaPlaybackStateStr(aState), aBrowsingContextId);
 | 
						|
  const bool oldPlaying = mPlaybackStatusDelegate.IsPlaying();
 | 
						|
  mPlaybackStatusDelegate.UpdateMediaPlaybackState(aBrowsingContextId, aState);
 | 
						|
 | 
						|
  // Playback state doesn't change, we don't need to update the guessed playback
 | 
						|
  // state. This is used to prevent the state from changing from `none` to
 | 
						|
  // `paused` when receiving `MediaPlaybackState::eStarted`.
 | 
						|
  if (mPlaybackStatusDelegate.IsPlaying() == oldPlaying) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (mPlaybackStatusDelegate.IsPlaying()) {
 | 
						|
    SetGuessedPlayState(MediaSessionPlaybackState::Playing);
 | 
						|
  } else {
 | 
						|
    SetGuessedPlayState(MediaSessionPlaybackState::Paused);
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::SetGuessedPlayState(MediaSessionPlaybackState aState) {
 | 
						|
  if (aState == mGuessedPlaybackState) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  LOG("SetGuessedPlayState : '%s'", ToMediaSessionPlaybackStateStr(aState));
 | 
						|
  mGuessedPlaybackState = aState;
 | 
						|
  UpdateActualPlaybackState();
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::UpdateActualPlaybackState() {
 | 
						|
  // The way to compute the actual playback state is based on the spec.
 | 
						|
  // https://w3c.github.io/mediasession/#actual-playback-state
 | 
						|
  MediaSessionPlaybackState newState =
 | 
						|
      GetCurrentDeclaredPlaybackState() == MediaSessionPlaybackState::Playing
 | 
						|
          ? MediaSessionPlaybackState::Playing
 | 
						|
          : mGuessedPlaybackState;
 | 
						|
  if (mActualPlaybackState == newState) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  mActualPlaybackState = newState;
 | 
						|
  LOG("UpdateActualPlaybackState : '%s'",
 | 
						|
      ToMediaSessionPlaybackStateStr(mActualPlaybackState));
 | 
						|
  mPlaybackStateChangedEvent.Notify(mActualPlaybackState);
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::EnableAction(uint64_t aBrowsingContextId,
 | 
						|
                                      MediaSessionAction aAction) {
 | 
						|
  auto info = mMediaSessionInfoMap.Lookup(aBrowsingContextId);
 | 
						|
  if (!info) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (info->IsActionSupported(aAction)) {
 | 
						|
    LOG("Action '%s' has already been enabled for context %" PRIu64,
 | 
						|
        GetEnumString(aAction).get(), aBrowsingContextId);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  LOG("Enable action %s for context %" PRIu64, GetEnumString(aAction).get(),
 | 
						|
      aBrowsingContextId);
 | 
						|
  info->EnableAction(aAction);
 | 
						|
  NotifySupportedKeysChangedIfNeeded(aBrowsingContextId);
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::DisableAction(uint64_t aBrowsingContextId,
 | 
						|
                                       MediaSessionAction aAction) {
 | 
						|
  auto info = mMediaSessionInfoMap.Lookup(aBrowsingContextId);
 | 
						|
  if (!info) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  if (!info->IsActionSupported(aAction)) {
 | 
						|
    LOG("Action '%s' hasn't been enabled yet for context %" PRIu64,
 | 
						|
        GetEnumString(aAction).get(), aBrowsingContextId);
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  LOG("Disable action %s for context %" PRIu64, GetEnumString(aAction).get(),
 | 
						|
      aBrowsingContextId);
 | 
						|
  info->DisableAction(aAction);
 | 
						|
  NotifySupportedKeysChangedIfNeeded(aBrowsingContextId);
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::UpdatePositionState(
 | 
						|
    uint64_t aBrowsingContextId, const Maybe<PositionState>& aState) {
 | 
						|
  auto info = mMediaSessionInfoMap.Lookup(aBrowsingContextId);
 | 
						|
  if (info) {
 | 
						|
    LOG("Update position state for context %" PRIu64, aBrowsingContextId);
 | 
						|
    info->mPositionState = aState;
 | 
						|
  }
 | 
						|
 | 
						|
  // The position state comes from non-active media session which we don't care.
 | 
						|
  if (!mActiveMediaSessionContextId ||
 | 
						|
      *mActiveMediaSessionContextId != aBrowsingContextId) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  mPositionStateChangedEvent.Notify(aState);
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::UpdateGuessedPositionState(
 | 
						|
    uint64_t aBrowsingContextId, const nsID& aMediaId,
 | 
						|
    const Maybe<PositionState>& aGuessedState) {
 | 
						|
  mPlaybackStatusDelegate.UpdateGuessedPositionState(aBrowsingContextId,
 | 
						|
                                                     aMediaId, aGuessedState);
 | 
						|
 | 
						|
  // The position state comes from a non-active media session and
 | 
						|
  // there is another one active (with some metadata).
 | 
						|
  if (mActiveMediaSessionContextId &&
 | 
						|
      *mActiveMediaSessionContextId != aBrowsingContextId) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // media session is declared for the updated session, but there's no active
 | 
						|
  // session - it will get emitted once the session becomes active
 | 
						|
  if (mMediaSessionInfoMap.Contains(aBrowsingContextId) &&
 | 
						|
      !mActiveMediaSessionContextId) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  mPositionStateChangedEvent.Notify(GetCurrentPositionState());
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::NotifySupportedKeysChangedIfNeeded(
 | 
						|
    uint64_t aBrowsingContextId) {
 | 
						|
  // Only the active media session's supported actions would be shown in virtual
 | 
						|
  // control interface, so we only notify the event when supported actions
 | 
						|
  // change happens on the active media session.
 | 
						|
  if (!mActiveMediaSessionContextId ||
 | 
						|
      *mActiveMediaSessionContextId != aBrowsingContextId) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  mSupportedActionsChangedEvent.Notify(GetSupportedActions());
 | 
						|
}
 | 
						|
 | 
						|
CopyableTArray<MediaSessionAction> MediaStatusManager::GetSupportedActions()
 | 
						|
    const {
 | 
						|
  CopyableTArray<MediaSessionAction> supportedActions;
 | 
						|
  if (!mActiveMediaSessionContextId) {
 | 
						|
    return supportedActions;
 | 
						|
  }
 | 
						|
 | 
						|
  MediaSessionInfo info =
 | 
						|
      mMediaSessionInfoMap.Get(*mActiveMediaSessionContextId);
 | 
						|
  for (MediaSessionAction action :
 | 
						|
       MakeWebIDLEnumeratedRange<MediaSessionAction>()) {
 | 
						|
    if (info.IsActionSupported(action)) {
 | 
						|
      supportedActions.AppendElement(action);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  return supportedActions;
 | 
						|
}
 | 
						|
 | 
						|
MediaMetadataBase MediaStatusManager::GetCurrentMediaMetadata() const {
 | 
						|
  // If we don't have active media session, active media session doesn't have
 | 
						|
  // media metadata, or we're in private browsing mode, then we should create a
 | 
						|
  // default metadata which is using website's title and favicon as title and
 | 
						|
  // artwork.
 | 
						|
  if (mActiveMediaSessionContextId && !IsInPrivateBrowsing()) {
 | 
						|
    MediaSessionInfo info =
 | 
						|
        mMediaSessionInfoMap.Get(*mActiveMediaSessionContextId);
 | 
						|
    if (!info.mMetadata) {
 | 
						|
      return CreateDefaultMetadata();
 | 
						|
    }
 | 
						|
    MediaMetadataBase& metadata = *(info.mMetadata);
 | 
						|
    FillMissingTitleAndArtworkIfNeeded(metadata);
 | 
						|
    return metadata;
 | 
						|
  }
 | 
						|
  return CreateDefaultMetadata();
 | 
						|
}
 | 
						|
 | 
						|
Maybe<PositionState> MediaStatusManager::GetCurrentPositionState() const {
 | 
						|
  if (mActiveMediaSessionContextId) {
 | 
						|
    auto info = mMediaSessionInfoMap.Lookup(*mActiveMediaSessionContextId);
 | 
						|
    if (info && info->mPositionState) {
 | 
						|
      return info->mPositionState;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  return mPlaybackStatusDelegate.GuessedMediaPositionState(
 | 
						|
      mActiveMediaSessionContextId);
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::FillMissingTitleAndArtworkIfNeeded(
 | 
						|
    MediaMetadataBase& aMetadata) const {
 | 
						|
  // If the metadata doesn't set its title and artwork properly, we would like
 | 
						|
  // to use default title and favicon instead in order to prevent showing
 | 
						|
  // nothing on the virtual control interface.
 | 
						|
  if (aMetadata.mTitle.IsEmpty()) {
 | 
						|
    aMetadata.mTitle = GetDefaultTitle();
 | 
						|
  }
 | 
						|
  if (aMetadata.mArtwork.IsEmpty()) {
 | 
						|
    aMetadata.mArtwork.AppendElement()->mSrc = GetDefaultFaviconURL();
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
bool MediaStatusManager::IsInPrivateBrowsing() const {
 | 
						|
  RefPtr<CanonicalBrowsingContext> bc =
 | 
						|
      CanonicalBrowsingContext::Get(mTopLevelBrowsingContextId);
 | 
						|
  if (!bc) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  RefPtr<Element> element = bc->GetEmbedderElement();
 | 
						|
  if (!element) {
 | 
						|
    return false;
 | 
						|
  }
 | 
						|
  return nsContentUtils::IsInPrivateBrowsing(element->OwnerDoc());
 | 
						|
}
 | 
						|
 | 
						|
MediaSessionPlaybackState MediaStatusManager::PlaybackState() const {
 | 
						|
  return mActualPlaybackState;
 | 
						|
}
 | 
						|
 | 
						|
bool MediaStatusManager::IsMediaAudible() const {
 | 
						|
  return mPlaybackStatusDelegate.IsAudible();
 | 
						|
}
 | 
						|
 | 
						|
bool MediaStatusManager::IsMediaPlaying() const {
 | 
						|
  return mActualPlaybackState == MediaSessionPlaybackState::Playing;
 | 
						|
}
 | 
						|
 | 
						|
bool MediaStatusManager::IsAnyMediaBeingControlled() const {
 | 
						|
  return mPlaybackStatusDelegate.IsAnyMediaBeingControlled();
 | 
						|
}
 | 
						|
 | 
						|
void MediaStatusManager::NotifyPageTitleChanged() {
 | 
						|
  // If active media session has set non-empty metadata, then we would use that
 | 
						|
  // instead of using default metadata.
 | 
						|
  if (mActiveMediaSessionContextId &&
 | 
						|
      mMediaSessionInfoMap.Lookup(*mActiveMediaSessionContextId)->mMetadata) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  // In private browsing mode, we won't show page title on default metadata so
 | 
						|
  // we don't need to update that.
 | 
						|
  if (IsInPrivateBrowsing()) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  LOG("page title changed, update default metadata");
 | 
						|
  mMetadataChangedEvent.Notify(GetCurrentMediaMetadata());
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace mozilla::dom
 |