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

Quelle  AutoplayPolicy.cpp   Sprache: C

 
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "AutoplayPolicy.h"

#include "mozilla/dom/AudioContext.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/FeaturePolicyUtils.h"
#include "mozilla/dom/HTMLMediaElement.h"
#include "mozilla/dom/HTMLMediaElementBinding.h"
#include "mozilla/dom/NavigatorBinding.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/WindowContext.h"
#include "mozilla/Logging.h"
#include "mozilla/MediaManager.h"
#include "mozilla/Components.h"
#include "mozilla/StaticPrefs_media.h"
#include "nsContentUtils.h"
#include "nsGlobalWindowInner.h"
#include "nsIAutoplay.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeItem.h"
#include "nsIPermissionManager.h"
#include "nsIPrincipal.h"
#include "nsPIDOMWindow.h"

mozilla::LazyLogModule gAutoplayPermissionLog("Autoplay");

#define AUTOPLAY_LOG(msg, ...) \
  MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))

using namespace mozilla::dom;

namespace mozilla::media {

static const uint32_t sPOLICY_STICKY_ACTIVATION = 0;
// static const uint32_t sPOLICY_TRANSIENT_ACTIVATION = 1;
static const uint32_t sPOLICY_USER_INPUT_DEPTH = 2;

static bool IsActivelyCapturingOrHasAPermission(nsPIDOMWindowInner* aWindow) {
  // Pages which have been granted permission to capture WebRTC camera or
  // microphone or screen are assumed to be trusted, and are allowed to
  // autoplay.
  if (MediaManager::GetIfExists()) {
    return MediaManager::GetIfExists()->IsActivelyCapturingOrHasAPermission(
        aWindow->WindowID());
  }

  auto principal = nsGlobalWindowInner::Cast(aWindow)->GetPrincipal();
  return (nsContentUtils::IsExactSitePermAllow(principal, "camera"_ns) ||
          nsContentUtils::IsExactSitePermAllow(principal, "microphone"_ns) ||
          nsContentUtils::IsExactSitePermAllow(principal, "screen"_ns));
}

static uint32_t SiteAutoplayPerm(nsPIDOMWindowInner* aWindow) {
  if (!aWindow || !aWindow->GetBrowsingContext()) {
    return nsIPermissionManager::UNKNOWN_ACTION;
  }

  WindowContext* topContext =
      aWindow->GetBrowsingContext()->GetTopWindowContext();
  if (!topContext) {
    return nsIPermissionManager::UNKNOWN_ACTION;
  }
  return topContext->GetAutoplayPermission();
}

static bool IsWindowAllowedToPlayByUserGesture(nsPIDOMWindowInner* aWindow) {
  if (!aWindow) {
    return false;
  }

  WindowContext* topContext =
      aWindow->GetBrowsingContext()->GetTopWindowContext();
  if (topContext && topContext->HasBeenUserGestureActivated()) {
    AUTOPLAY_LOG(
        "Allow autoplay as top-level context has been activated by user "
        "gesture.");
    return true;
  }
  return false;
}

static bool IsWindowAllowedToPlayByTraits(nsPIDOMWindowInner* aWindow) {
  if (!aWindow) {
    return false;
  }

  if (IsActivelyCapturingOrHasAPermission(aWindow)) {
    AUTOPLAY_LOG(
        "Allow autoplay as document has camera or microphone or screen"
        " permission.");
    return true;
  }

  Document* currentDoc = aWindow->GetExtantDoc();
  if (!currentDoc) {
    return false;
  }

  bool isTopLevelContent = !aWindow->GetBrowsingContext()->GetParent();
  if (currentDoc->MediaDocumentKind() == Document::MediaDocumentKind::Video &&
      isTopLevelContent) {
    AUTOPLAY_LOG("Allow top-level video document to autoplay.");
    return true;
  }

  if (StaticPrefs::media_autoplay_allow_extension_background_pages() &&
      currentDoc->IsExtensionPage()) {
    AUTOPLAY_LOG("Allow autoplay as in extension document.");
    return true;
  }

  if (currentDoc->GetPrincipal()->Equals(
          nsContentUtils::GetFingerprintingProtectionPrincipal())) {
    AUTOPLAY_LOG("Allow autoplay as in fingerprinting protection document.");
    return true;
  }

  return false;
}

static bool IsWindowAllowedToPlayOverall(nsPIDOMWindowInner* aWindow) {
  return IsWindowAllowedToPlayByUserGesture(aWindow) ||
         IsWindowAllowedToPlayByTraits(aWindow);
}

static uint32_t DefaultAutoplayBehaviour() {
  int32_t prefValue = StaticPrefs::media_autoplay_default();
  if (prefValue == nsIAutoplay::ALLOWED) {
    return nsIAutoplay::ALLOWED;
  }
  if (prefValue == nsIAutoplay::BLOCKED_ALL) {
    return nsIAutoplay::BLOCKED_ALL;
  }
  return nsIAutoplay::BLOCKED;
}

static bool IsMediaElementInaudible(const HTMLMediaElement& aElement) {
  if (aElement.Volume() == 0.0 || aElement.Muted()) {
    AUTOPLAY_LOG("Media %p is muted.", &aElement);
    return true;
  }

  if (!aElement.HasAudio() &&
      aElement.ReadyState() >= HTMLMediaElement_Binding::HAVE_METADATA) {
    AUTOPLAY_LOG("Media %p has no audio track", &aElement);
    return true;
  }

  return false;
}

static bool IsEnableBlockingWebAudioByUserGesturePolicy() {
  return StaticPrefs::media_autoplay_blocking_policy() ==
         sPOLICY_STICKY_ACTIVATION;
}

static bool IsAllowedToPlayByBlockingModel(const HTMLMediaElement& aElement) {
  const uint32_t policy = StaticPrefs::media_autoplay_blocking_policy();
  if (policy == sPOLICY_STICKY_ACTIVATION) {
    const bool isAllowed =
        IsWindowAllowedToPlayOverall(aElement.OwnerDoc()->GetInnerWindow());
    AUTOPLAY_LOG("Use 'sticky-activation', isAllowed=%d", isAllowed);
    return isAllowed;
  }
  // If element is blessed, it would always be allowed to play().
  const bool isElementBlessed = aElement.IsBlessed();
  if (policy == sPOLICY_USER_INPUT_DEPTH) {
    const bool isUserInput = UserActivation::IsHandlingUserInput();
    AUTOPLAY_LOG("Use 'User-Input-Depth', isBlessed=%d, isUserInput=%d",
                 isElementBlessed, isUserInput);
    return isElementBlessed || isUserInput;
  }
  const bool hasTransientActivation =
      aElement.OwnerDoc()->HasValidTransientUserGestureActivation();
  AUTOPLAY_LOG(
      "Use 'transient-activation', isBlessed=%d, "
      "hasValidTransientActivation=%d",
      isElementBlessed, hasTransientActivation);
  return isElementBlessed || hasTransientActivation;
}

// On GeckoView, we don't store any site's permission in permission manager, we
// would check the GV request status to know if the site can be allowed to play.
// But on other platforms, we would store the site's permission in permission
// manager.
#if defined(MOZ_WIDGET_ANDROID)
using RType = GVAutoplayRequestType;

static bool IsGVAutoplayRequestAllowed(nsPIDOMWindowInner* aWindow,
                                       RType aType) {
  if (!aWindow) {
    return false;
  }

  RefPtr<BrowsingContext> context = aWindow->GetBrowsingContext()->Top();
  GVAutoplayRequestStatus status =
      aType == RType::eAUDIBLE ? context->GetGVAudibleAutoplayRequestStatus()
                               : context->GetGVInaudibleAutoplayRequestStatus();
  return status == GVAutoplayRequestStatus::eALLOWED;
}

static bool IsGVAutoplayRequestAllowed(const HTMLMediaElement& aElement,
                                       RType aType) {
  // On GV, blocking model is the first thing we would check inside Gecko, and
  // if the media is not allowed by that, then we would check the response from
  // the embedding app to decide the final result.
  if (IsAllowedToPlayByBlockingModel(aElement)) {
    return true;
  }

  RefPtr<nsPIDOMWindowInner> window = aElement.OwnerDoc()->GetInnerWindow();
  if (!window) {
    return false;
  }
  return IsGVAutoplayRequestAllowed(window, aType);
}
#endif

static bool IsAllowedToPlayInternal(const HTMLMediaElement& aElement) {
#if defined(MOZ_WIDGET_ANDROID)
  if (StaticPrefs::media_geckoview_autoplay_request()) {
    return IsGVAutoplayRequestAllowed(
        aElement, IsMediaElementInaudible(aElement) ? RType::eINAUDIBLE
                                                    : RType::eAUDIBLE);
  }
#endif
  bool isInaudible = IsMediaElementInaudible(aElement);
  bool isUsingAutoplayModel = IsAllowedToPlayByBlockingModel(aElement);

  uint32_t defaultBehaviour = DefaultAutoplayBehaviour();
  uint32_t sitePermission =
      SiteAutoplayPerm(aElement.OwnerDoc()->GetInnerWindow());

  AUTOPLAY_LOG(
      "IsAllowedToPlayInternal, isInaudible=%d,"
      "isUsingAutoplayModel=%d, sitePermission=%d, defaultBehaviour=%d",
      isInaudible, isUsingAutoplayModel, sitePermission, defaultBehaviour);

  // For site permissions we store permissionManager values except
  // for BLOCKED_ALL, for the default pref values we store
  // nsIAutoplay values.
  if (sitePermission == nsIPermissionManager::ALLOW_ACTION) {
    return true;
  }

  if (sitePermission == nsIPermissionManager::DENY_ACTION) {
    return isInaudible || isUsingAutoplayModel;
  }

  if (sitePermission == nsIAutoplay::BLOCKED_ALL) {
    return isUsingAutoplayModel;
  }

  if (defaultBehaviour == nsIAutoplay::ALLOWED) {
    return true;
  }

  if (defaultBehaviour == nsIAutoplay::BLOCKED) {
    return isInaudible || isUsingAutoplayModel;
  }

  MOZ_ASSERT(defaultBehaviour == nsIAutoplay::BLOCKED_ALL);
  return isUsingAutoplayModel;
}

/* static */
bool AutoplayPolicy::IsAllowedToPlay(const HTMLMediaElement& aElement) {
  const bool result = IsAllowedToPlayInternal(aElement);
  AUTOPLAY_LOG("IsAllowedToPlay, mediaElement=%p, isAllowToPlay=%s", &aElement,
               result ? "allowed" : "blocked");
  return result;
}

/* static */
bool AutoplayPolicy::IsAllowedToPlay(const AudioContext& aContext) {
  /**
   * The autoplay checking has 5 different phases,
   * 1. check whether audio context itself meets the autoplay condition
   * 2. check if we enable blocking web audio or not
   *    (only support blocking when using user-gesture-activation model)
   * 3. check whether the site is in the autoplay whitelist
   * 4. check global autoplay setting and check wether the site is in the
   *    autoplay blacklist.
   * 5. check whether media is allowed under current blocking model
   *    (only support user-gesture-activation model)
   */

  if (aContext.IsOffline()) {
    return true;
  }

  if (!IsEnableBlockingWebAudioByUserGesturePolicy()) {
    return true;
  }

  nsPIDOMWindowInner* window = aContext.GetOwnerWindow();
  uint32_t sitePermission = SiteAutoplayPerm(window);

  if (sitePermission == nsIPermissionManager::ALLOW_ACTION) {
    AUTOPLAY_LOG(
        "Allow autoplay as document has permanent autoplay permission.");
    return true;
  }

  if (DefaultAutoplayBehaviour() == nsIAutoplay::ALLOWED &&
      sitePermission != nsIPermissionManager::DENY_ACTION &&
      sitePermission != nsIAutoplay::BLOCKED_ALL) {
    AUTOPLAY_LOG(
        "Allow autoplay as global autoplay setting is allowing autoplay by "
        "default.");
    return true;
  }

  return IsWindowAllowedToPlayOverall(window);
}

enum class DocumentAutoplayPolicy : uint8_t {
  Allowed,
  Allowed_muted,
  Disallowed
};

/* static */
DocumentAutoplayPolicy IsDocAllowedToPlay(const Document& aDocument) {
  RefPtr<nsPIDOMWindowInner> window = aDocument.GetInnerWindow();

#if defined(MOZ_WIDGET_ANDROID)
  if (StaticPrefs::media_geckoview_autoplay_request()) {
    const bool isWindowAllowedToPlay = IsWindowAllowedToPlayOverall(window);
    if (IsGVAutoplayRequestAllowed(window, RType::eAUDIBLE)) {
      return DocumentAutoplayPolicy::Allowed;
    }

    if (IsGVAutoplayRequestAllowed(window, RType::eINAUDIBLE)) {
      return isWindowAllowedToPlay ? DocumentAutoplayPolicy::Allowed
                                   : DocumentAutoplayPolicy::Allowed_muted;
    }

    return isWindowAllowedToPlay ? DocumentAutoplayPolicy::Allowed
                                 : DocumentAutoplayPolicy::Disallowed;
  }
#endif
  const uint32_t sitePermission = SiteAutoplayPerm(window);
  const uint32_t globalPermission = DefaultAutoplayBehaviour();
  const uint32_t policy = StaticPrefs::media_autoplay_blocking_policy();
  const bool isWindowAllowedToPlayByGesture =
      policy != sPOLICY_USER_INPUT_DEPTH &&
      IsWindowAllowedToPlayByUserGesture(window);
  const bool isWindowAllowedToPlayByTraits =
      IsWindowAllowedToPlayByTraits(window);

  AUTOPLAY_LOG(
      "IsDocAllowedToPlay(), policy=%d, sitePermission=%d, "
      "globalPermission=%d, isWindowAllowedToPlayByGesture=%d, "
      "isWindowAllowedToPlayByTraits=%d",
      policy, sitePermission, globalPermission, isWindowAllowedToPlayByGesture,
      isWindowAllowedToPlayByTraits);

  if ((globalPermission == nsIAutoplay::ALLOWED &&
       (sitePermission != nsIPermissionManager::DENY_ACTION &&
        sitePermission != nsIAutoplay::BLOCKED_ALL)) ||
      sitePermission == nsIPermissionManager::ALLOW_ACTION ||
      isWindowAllowedToPlayByGesture || isWindowAllowedToPlayByTraits) {
    return DocumentAutoplayPolicy::Allowed;
  }

  if ((globalPermission == nsIAutoplay::BLOCKED &&
       sitePermission != nsIAutoplay::BLOCKED_ALL) ||
      sitePermission == nsIPermissionManager::DENY_ACTION) {
    return DocumentAutoplayPolicy::Allowed_muted;
  }

  return DocumentAutoplayPolicy::Disallowed;
}

/* static */
uint32_t AutoplayPolicy::GetSiteAutoplayPermission(nsIPrincipal* aPrincipal) {
  if (!aPrincipal) {
    return nsIPermissionManager::DENY_ACTION;
  }

  nsCOMPtr<nsIPermissionManager> permMgr =
      components::PermissionManager::Service();
  if (!permMgr) {
    return nsIPermissionManager::DENY_ACTION;
  }

  uint32_t perm = nsIPermissionManager::DENY_ACTION;
  permMgr->TestExactPermissionFromPrincipal(aPrincipal, "autoplay-media"_ns,
                                            &perm);
  return perm;
}

/* static */
dom::AutoplayPolicy AutoplayPolicy::GetAutoplayPolicy(
    const dom::HTMLMediaElement& aElement) {
  // Note, the site permission can contain following values :
  // - UNKNOWN_ACTION : no permission set for this site
  // - ALLOW_ACTION : allowed to autoplay
  // - DENY_ACTION : allowed inaudible autoplay, disallowed inaudible autoplay
  // - nsIAutoplay::BLOCKED_ALL : autoplay disallowed
  // and the global permissions would be nsIAutoplay::{BLOCKED, ALLOWED,
  // BLOCKED_ALL}
  const uint32_t sitePermission =
      SiteAutoplayPerm(aElement.OwnerDoc()->GetInnerWindow());
  const uint32_t globalPermission = DefaultAutoplayBehaviour();
  const bool isAllowedToPlayByBlockingModel =
      IsAllowedToPlayByBlockingModel(aElement);

  AUTOPLAY_LOG(
      "IsAllowedToPlay(element), sitePermission=%d, globalPermission=%d, "
      "isAllowedToPlayByBlockingModel=%d",
      sitePermission, globalPermission, isAllowedToPlayByBlockingModel);

#if defined(MOZ_WIDGET_ANDROID)
  if (StaticPrefs::media_geckoview_autoplay_request()) {
    if (IsGVAutoplayRequestAllowed(aElement, RType::eAUDIBLE)) {
      return dom::AutoplayPolicy::Allowed;
    } else if (IsGVAutoplayRequestAllowed(aElement, RType::eINAUDIBLE)) {
      return isAllowedToPlayByBlockingModel
                 ? dom::AutoplayPolicy::Allowed
                 : dom::AutoplayPolicy::Allowed_muted;
    } else {
      return isAllowedToPlayByBlockingModel ? dom::AutoplayPolicy::Allowed
                                            : dom::AutoplayPolicy::Disallowed;
    }
  }
#endif

  // These are situations when an element is allowed to autoplay
  // 1. The site permission is explicitly allowed
  // 2. The global permission is allowed, and the site isn't explicitly
  //    disallowed
  // 3. The blocking model is explicitly allowed this element
  if (sitePermission == nsIPermissionManager::ALLOW_ACTION ||
      (globalPermission == nsIAutoplay::ALLOWED &&
       (sitePermission != nsIPermissionManager::DENY_ACTION &&
        sitePermission != nsIAutoplay::BLOCKED_ALL)) ||
      isAllowedToPlayByBlockingModel) {
    return dom::AutoplayPolicy::Allowed;
  }

  // These are situations when a element is allowed to autoplay only when it's
  // inaudible.
  // 1. The site permission is block-audible-autoplay
  // 2. The global permission is block-audible-autoplay, and the site permission
  //    isn't block-all-autoplay
  if (sitePermission == nsIPermissionManager::DENY_ACTION ||
      (globalPermission == nsIAutoplay::BLOCKED &&
       sitePermission != nsIAutoplay::BLOCKED_ALL)) {
    return dom::AutoplayPolicy::Allowed_muted;
  }

  return dom::AutoplayPolicy::Disallowed;
}

/* static */
dom::AutoplayPolicy AutoplayPolicy::GetAutoplayPolicy(
    const dom::AudioContext& aContext) {
  if (AutoplayPolicy::IsAllowedToPlay(aContext)) {
    return dom::AutoplayPolicy::Allowed;
  }
  return dom::AutoplayPolicy::Disallowed;
}

/* static */
dom::AutoplayPolicy AutoplayPolicy::GetAutoplayPolicy(
    const dom::AutoplayPolicyMediaType& aType, const dom::Document& aDoc) {
  DocumentAutoplayPolicy policy = IsDocAllowedToPlay(aDoc);
  // https://w3c.github.io/autoplay/#query-by-a-media-type
  if (aType == dom::AutoplayPolicyMediaType::Audiocontext) {
    return policy == DocumentAutoplayPolicy::Allowed
               ? dom::AutoplayPolicy::Allowed
               : dom::AutoplayPolicy::Disallowed;
  }
  MOZ_ASSERT(aType == dom::AutoplayPolicyMediaType::Mediaelement);
  if (policy == DocumentAutoplayPolicy::Allowed) {
    return dom::AutoplayPolicy::Allowed;
  }
  if (policy == DocumentAutoplayPolicy::Allowed_muted) {
    return dom::AutoplayPolicy::Allowed_muted;
  }
  return dom::AutoplayPolicy::Disallowed;
}

}  // namespace mozilla::media

Messung V0.5
C=91 H=100 G=95

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© 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.