Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/media/mediacontrol/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 14 kB image not shown  

Quelle  ContentMediaController.cpp   Sprache: 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 "ContentMediaController.h"

#include "MediaControlUtils.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Telemetry.h"
#include "mozilla/ToString.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ContentChild.h"
#include "nsGlobalWindowInner.h"

namespace mozilla::dom {

#undef LOG
#define LOG(msg, ...)                        \
  MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
          ("ContentMediaController=%p, " msg, this##__VA_ARGS__))

static Maybe<bool> sXPCOMShutdown;

static void InitXPCOMShutdownMonitor() {
  if (sXPCOMShutdown) {
    return;
  }
  sXPCOMShutdown.emplace(false);
  RunOnShutdown([&] { sXPCOMShutdown = Some(true); });
}

static ContentMediaController* GetContentMediaControllerFromBrowsingContext(
    BrowsingContext* aBrowsingContext) {
  MOZ_ASSERT(NS_IsMainThread());
  InitXPCOMShutdownMonitor();
  if (!aBrowsingContext || aBrowsingContext->IsDiscarded()) {
    return nullptr;
  }

  nsPIDOMWindowOuter* outer = aBrowsingContext->GetDOMWindow();
  if (!outer) {
    return nullptr;
  }

  nsGlobalWindowInner* inner =
      nsGlobalWindowInner::Cast(outer->GetCurrentInnerWindow());
  return inner ? inner->GetContentMediaController() : nullptr;
}

static already_AddRefed<BrowsingContext> GetBrowsingContextForAgent(
    uint64_t aBrowsingContextId) {
  // If XPCOM has been shutdown, then we're not able to access browsing context.
  if (sXPCOMShutdown && *sXPCOMShutdown) {
    return nullptr;
  }
  return BrowsingContext::Get(aBrowsingContextId);
}

/* static */
ContentMediaControlKeyReceiver* ContentMediaControlKeyReceiver::Get(
    BrowsingContext* aBC) {
  MOZ_ASSERT(NS_IsMainThread());
  return GetContentMediaControllerFromBrowsingContext(aBC);
}

/* static */
ContentMediaAgent* ContentMediaAgent::Get(BrowsingContext* aBC) {
  MOZ_ASSERT(NS_IsMainThread());
  return GetContentMediaControllerFromBrowsingContext(aBC);
}

void ContentMediaAgent::NotifyMediaPlaybackChanged(uint64_t aBrowsingContextId,
                                                   MediaPlaybackState aState) {
  MOZ_ASSERT(NS_IsMainThread());
  RefPtr<BrowsingContext> bc = GetBrowsingContextForAgent(aBrowsingContextId);
  if (!bc || bc->IsDiscarded()) {
    return;
  }

  LOG("Notify media %s in BC %" PRId64, ToString(aState).c_str(), bc->Id());
  if (XRE_IsContentProcess()) {
    ContentChild* contentChild = ContentChild::GetSingleton();
    Unused << contentChild->SendNotifyMediaPlaybackChanged(bc, aState);
  } else {
    // Currently this only happen when we disable e10s, otherwise all controlled
    // media would be run in the content process.
    if (RefPtr<IMediaInfoUpdater> updater =
            bc->Canonical()->GetMediaController()) {
      updater->NotifyMediaPlaybackChanged(bc->Id(), aState);
    }
  }
}

void ContentMediaAgent::NotifyMediaAudibleChanged(uint64_t aBrowsingContextId,
                                                  MediaAudibleState aState) {
  MOZ_ASSERT(NS_IsMainThread());
  RefPtr<BrowsingContext> bc = GetBrowsingContextForAgent(aBrowsingContextId);
  if (!bc || bc->IsDiscarded()) {
    return;
  }

  LOG("Notify media became %s in BC %" PRId64,
      aState == MediaAudibleState::eAudible ? "audible" : "inaudible",
      bc->Id());
  if (XRE_IsContentProcess()) {
    ContentChild* contentChild = ContentChild::GetSingleton();
    Unused << contentChild->SendNotifyMediaAudibleChanged(bc, aState);
  } else {
    // Currently this only happen when we disable e10s, otherwise all controlled
    // media would be run in the content process.
    if (RefPtr<IMediaInfoUpdater> updater =
            bc->Canonical()->GetMediaController()) {
      updater->NotifyMediaAudibleChanged(bc->Id(), aState);
    }
  }
}

void ContentMediaAgent::SetIsInPictureInPictureMode(
    uint64_t aBrowsingContextId, bool aIsInPictureInPictureMode) {
  MOZ_ASSERT(NS_IsMainThread());
  RefPtr<BrowsingContext> bc = GetBrowsingContextForAgent(aBrowsingContextId);
  if (!bc || bc->IsDiscarded()) {
    return;
  }

  LOG("Notify media Picture-in-Picture mode '%s' in BC %" PRId64,
      aIsInPictureInPictureMode ? "enabled" : "disabled", bc->Id());
  if (XRE_IsContentProcess()) {
    ContentChild* contentChild = ContentChild::GetSingleton();
    Unused << contentChild->SendNotifyPictureInPictureModeChanged(
        bc, aIsInPictureInPictureMode);
  } else {
    // Currently this only happen when we disable e10s, otherwise all controlled
    // media would be run in the content process.
    if (RefPtr<IMediaInfoUpdater> updater =
            bc->Canonical()->GetMediaController()) {
      updater->SetIsInPictureInPictureMode(bc->Id(), aIsInPictureInPictureMode);
    }
  }
}

void ContentMediaAgent::SetDeclaredPlaybackState(
    uint64_t aBrowsingContextId, MediaSessionPlaybackState aState) {
  RefPtr<BrowsingContext> bc = GetBrowsingContextForAgent(aBrowsingContextId);
  if (!bc || bc->IsDiscarded()) {
    return;
  }

  LOG("Notify declared playback state '%s' in BC %" PRId64,
      ToMediaSessionPlaybackStateStr(aState), bc->Id());
  if (XRE_IsContentProcess()) {
    ContentChild* contentChild = ContentChild::GetSingleton();
    Unused << contentChild->SendNotifyMediaSessionPlaybackStateChanged(bc,
                                                                       aState);
    return;
  }
  // This would only happen when we disable e10s.
  if (RefPtr<IMediaInfoUpdater> updater =
          bc->Canonical()->GetMediaController()) {
    updater->SetDeclaredPlaybackState(bc->Id(), aState);
  }
}

void ContentMediaAgent::NotifySessionCreated(uint64_t aBrowsingContextId) {
  RefPtr<BrowsingContext> bc = GetBrowsingContextForAgent(aBrowsingContextId);
  if (!bc || bc->IsDiscarded()) {
    return;
  }

  LOG("Notify media session being created in BC %" PRId64, bc->Id());
  if (XRE_IsContentProcess()) {
    ContentChild* contentChild = ContentChild::GetSingleton();
    Unused << contentChild->SendNotifyMediaSessionUpdated(bc, true);
    return;
  }
  // This would only happen when we disable e10s.
  if (RefPtr<IMediaInfoUpdater> updater =
          bc->Canonical()->GetMediaController()) {
    updater->NotifySessionCreated(bc->Id());
  }
}

void ContentMediaAgent::NotifySessionDestroyed(uint64_t aBrowsingContextId) {
  RefPtr<BrowsingContext> bc = GetBrowsingContextForAgent(aBrowsingContextId);
  if (!bc || bc->IsDiscarded()) {
    return;
  }

  LOG("Notify media session being destroyed in BC %" PRId64, bc->Id());
  if (XRE_IsContentProcess()) {
    ContentChild* contentChild = ContentChild::GetSingleton();
    Unused << contentChild->SendNotifyMediaSessionUpdated(bc, false);
    return;
  }
  // This would only happen when we disable e10s.
  if (RefPtr<IMediaInfoUpdater> updater =
          bc->Canonical()->GetMediaController()) {
    updater->NotifySessionDestroyed(bc->Id());
  }
}

void ContentMediaAgent::UpdateMetadata(
    uint64_t aBrowsingContextId, const Maybe<MediaMetadataBase>& aMetadata) {
  RefPtr<BrowsingContext> bc = GetBrowsingContextForAgent(aBrowsingContextId);
  if (!bc || bc->IsDiscarded()) {
    return;
  }

  LOG("Notify media session metadata change in BC %" PRId64, bc->Id());
  if (XRE_IsContentProcess()) {
    ContentChild* contentChild = ContentChild::GetSingleton();
    Unused << contentChild->SendNotifyUpdateMediaMetadata(bc, aMetadata);
    return;
  }
  // This would only happen when we disable e10s.
  if (RefPtr<IMediaInfoUpdater> updater =
          bc->Canonical()->GetMediaController()) {
    updater->UpdateMetadata(bc->Id(), aMetadata);
  }
}

void ContentMediaAgent::EnableAction(uint64_t aBrowsingContextId,
                                     MediaSessionAction aAction) {
  RefPtr<BrowsingContext> bc = GetBrowsingContextForAgent(aBrowsingContextId);
  if (!bc || bc->IsDiscarded()) {
    return;
  }

  LOG("Notify to enable action '%s' in BC %" PRId64,
      GetEnumString(aAction).get(), bc->Id());
  if (XRE_IsContentProcess()) {
    ContentChild* contentChild = ContentChild::GetSingleton();
    Unused << contentChild->SendNotifyMediaSessionSupportedActionChanged(
        bc, aAction, true);
    return;
  }
  // This would only happen when we disable e10s.
  if (RefPtr<IMediaInfoUpdater> updater =
          bc->Canonical()->GetMediaController()) {
    updater->EnableAction(bc->Id(), aAction);
  }
}

void ContentMediaAgent::DisableAction(uint64_t aBrowsingContextId,
                                      MediaSessionAction aAction) {
  RefPtr<BrowsingContext> bc = GetBrowsingContextForAgent(aBrowsingContextId);
  if (!bc || bc->IsDiscarded()) {
    return;
  }

  LOG("Notify to disable action '%s' in BC %" PRId64,
      GetEnumString(aAction).get(), bc->Id());
  if (XRE_IsContentProcess()) {
    ContentChild* contentChild = ContentChild::GetSingleton();
    Unused << contentChild->SendNotifyMediaSessionSupportedActionChanged(
        bc, aAction, false);
    return;
  }
  // This would only happen when we disable e10s.
  if (RefPtr<IMediaInfoUpdater> updater =
          bc->Canonical()->GetMediaController()) {
    updater->DisableAction(bc->Id(), aAction);
  }
}

void ContentMediaAgent::NotifyMediaFullScreenState(uint64_t aBrowsingContextId,
                                                   bool aIsInFullScreen) {
  RefPtr<BrowsingContext> bc = GetBrowsingContextForAgent(aBrowsingContextId);
  if (!bc || bc->IsDiscarded()) {
    return;
  }

  LOG("Notify %s fullscreen in BC %" PRId64,
      aIsInFullScreen ? "entered" : "left", bc->Id());
  if (XRE_IsContentProcess()) {
    ContentChild* contentChild = ContentChild::GetSingleton();
    Unused << contentChild->SendNotifyMediaFullScreenState(bc, aIsInFullScreen);
    return;
  }
  // This would only happen when we disable e10s.
  if (RefPtr<IMediaInfoUpdater> updater =
          bc->Canonical()->GetMediaController()) {
    updater->NotifyMediaFullScreenState(bc->Id(), aIsInFullScreen);
  }
}

void ContentMediaAgent::UpdatePositionState(
    uint64_t aBrowsingContextId, const Maybe<PositionState>& aState) {
  RefPtr<BrowsingContext> bc = GetBrowsingContextForAgent(aBrowsingContextId);
  if (!bc || bc->IsDiscarded()) {
    return;
  }
  if (XRE_IsContentProcess()) {
    ContentChild* contentChild = ContentChild::GetSingleton();
    Unused << contentChild->SendNotifyPositionStateChanged(bc, aState);
    return;
  }
  // This would only happen when we disable e10s.
  if (RefPtr<IMediaInfoUpdater> updater =
          bc->Canonical()->GetMediaController()) {
    updater->UpdatePositionState(bc->Id(), aState);
  }
}

void ContentMediaAgent::UpdateGuessedPositionState(
    uint64_t aBrowsingContextId, const nsID& aMediaId,
    const Maybe<PositionState>& aState) {
  RefPtr<BrowsingContext> bc = GetBrowsingContextForAgent(aBrowsingContextId);
  if (!bc || bc->IsDiscarded()) {
    return;
  }

  if (aState) {
    LOG("Update guessed position state for BC %" PRId64
        " media id %s (duration=%f, playbackRate=%f, position=%f)",
        bc->Id(), aMediaId.ToString().get(), aState->mDuration,
        aState->mPlaybackRate, aState->mLastReportedPlaybackPosition);
  } else {
    LOG("Clear guessed position state for BC %" PRId64 " media id %s", bc->Id(),
        aMediaId.ToString().get());
  }

  if (XRE_IsContentProcess()) {
    ContentChild* contentChild = ContentChild::GetSingleton();
    Unused << contentChild->SendNotifyGuessedPositionStateChanged(bc, aMediaId,
                                                                  aState);
    return;
  }
  // This would only happen when we disable e10s.
  if (RefPtr<IMediaInfoUpdater> updater =
          bc->Canonical()->GetMediaController()) {
    updater->UpdateGuessedPositionState(bc->Id(), aMediaId, aState);
  }
}

ContentMediaController::ContentMediaController(uint64_t aId) {
  LOG("Create content media controller for BC %" PRId64, aId);
}

void ContentMediaController::AddReceiver(
    ContentMediaControlKeyReceiver* aListener) {
  MOZ_ASSERT(NS_IsMainThread());
  mReceivers.AppendElement(aListener);
}

void ContentMediaController::RemoveReceiver(
    ContentMediaControlKeyReceiver* aListener) {
  MOZ_ASSERT(NS_IsMainThread());
  mReceivers.RemoveElement(aListener);
}

void ContentMediaController::HandleMediaKey(MediaControlKey aKey,
                                            Maybe<SeekDetails> aDetails) {
  MOZ_ASSERT(NS_IsMainThread());
  if (mReceivers.IsEmpty()) {
    return;
  }
  LOG("Handle '%s' event, receiver num=%zu", GetEnumString(aKey).get(),
      mReceivers.Length());
  // We have default handlers for these actions
  // https://w3c.github.io/mediasession/#ref-for-dom-mediasessionaction-play%E2%91%A3
  switch (aKey) {
    case MediaControlKey::Pause:
      PauseOrStopMedia();
      return;
    case MediaControlKey::Play:
    case MediaControlKey::Stop:
    case MediaControlKey::Seekto:
    case MediaControlKey::Seekforward:
    case MediaControlKey::Seekbackward:
      // When receiving `Stop`, the amount of receiver would vary during the
      // iteration, so we use the backward iteration to avoid accessing the
      // index which is over the array length.
      for (auto& receiver : Reversed(mReceivers)) {
        receiver->HandleMediaKey(aKey, aDetails);
      }
      return;
    default:
      MOZ_ASSERT_UNREACHABLE("Not supported media key for default handler");
  }
}

void ContentMediaController::PauseOrStopMedia() {
  // When receiving `pause`, if a page contains playing media and paused media
  // at that moment, that means a user intends to pause those playing
  // media, not the already paused ones. Then, we're going to stop those already
  // paused media and keep those latest paused media in `mReceivers`.
  // The reason for doing that is, when resuming paused media, we only want to
  // resume latest paused media, not all media, in order to get a better user
  // experience, which matches Chrome's behavior.
  bool isAnyMediaPlaying = false;
  for (const auto& receiver : mReceivers) {
    if (receiver->IsPlaying()) {
      isAnyMediaPlaying = true;
      break;
    }
  }

  for (auto& receiver : Reversed(mReceivers)) {
    if (isAnyMediaPlaying && !receiver->IsPlaying()) {
      receiver->HandleMediaKey(MediaControlKey::Stop);
    } else {
      receiver->HandleMediaKey(MediaControlKey::Pause);
    }
  }
}

}  // namespace mozilla::dom

Messung V0.5
C=93 H=96 G=94

¤ Dauer der Verarbeitung: 0.12 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.