tor-browser

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

HLSDecoder.cpp (10586B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "HLSDecoder.h"
      8 
      9 #include "AndroidBridge.h"
     10 #include "DecoderTraits.h"
     11 #include "HLSDemuxer.h"
     12 #include "HLSUtils.h"
     13 #include "JavaBuiltins.h"
     14 #include "MediaContainerType.h"
     15 #include "MediaDecoderStateMachine.h"
     16 #include "MediaFormatReader.h"
     17 #include "MediaShutdownManager.h"
     18 #include "base/process_util.h"
     19 #include "mozilla/NullPrincipal.h"
     20 #include "mozilla/StaticPrefs_media.h"
     21 #include "mozilla/dom/HTMLMediaElement.h"
     22 #include "mozilla/glean/DomMediaHlsMetrics.h"
     23 #include "mozilla/java/GeckoHLSResourceWrapperNatives.h"
     24 #include "nsContentUtils.h"
     25 #include "nsIChannel.h"
     26 #include "nsIURL.h"
     27 #include "nsNetUtil.h"
     28 #include "nsThreadUtils.h"
     29 
     30 namespace mozilla {
     31 
     32 class HLSResourceCallbacksSupport
     33    : public java::GeckoHLSResourceWrapper::Callbacks::Natives<
     34          HLSResourceCallbacksSupport> {
     35  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HLSResourceCallbacksSupport)
     36 public:
     37  typedef java::GeckoHLSResourceWrapper::Callbacks::Natives<
     38      HLSResourceCallbacksSupport>
     39      NativeCallbacks;
     40  using NativeCallbacks::AttachNative;
     41  using NativeCallbacks::DisposeNative;
     42 
     43  explicit HLSResourceCallbacksSupport(HLSDecoder* aResource);
     44  void Detach();
     45  void OnLoad(jni::String::Param aUrl);
     46  void OnDataArrived();
     47  void OnError(int aErrorCode);
     48 
     49 private:
     50  ~HLSResourceCallbacksSupport() {}
     51  Mutex mMutex MOZ_UNANNOTATED;
     52  HLSDecoder* mDecoder;
     53 };
     54 
     55 HLSResourceCallbacksSupport::HLSResourceCallbacksSupport(HLSDecoder* aDecoder)
     56    : mMutex("HLSResourceCallbacksSupport"), mDecoder(aDecoder) {
     57  MOZ_ASSERT(mDecoder);
     58 }
     59 
     60 void HLSResourceCallbacksSupport::Detach() {
     61  MOZ_ASSERT(NS_IsMainThread());
     62  MutexAutoLock lock(mMutex);
     63  mDecoder = nullptr;
     64 }
     65 
     66 void HLSResourceCallbacksSupport::OnLoad(jni::String::Param aUrl) {
     67  MutexAutoLock lock(mMutex);
     68  if (!mDecoder) {
     69    return;
     70  }
     71  RefPtr<HLSResourceCallbacksSupport> self = this;
     72  jni::String::GlobalRef url = std::move(aUrl);
     73  NS_DispatchToMainThread(NS_NewRunnableFunction(
     74      "HLSResourceCallbacksSupport::OnLoad", [self, url]() -> void {
     75        if (self->mDecoder) {
     76          self->mDecoder->NotifyLoad(url->ToCString());
     77        }
     78      }));
     79 }
     80 
     81 void HLSResourceCallbacksSupport::OnDataArrived() {
     82  HLS_DEBUG("HLSResourceCallbacksSupport", "OnDataArrived.");
     83  MutexAutoLock lock(mMutex);
     84  if (!mDecoder) {
     85    return;
     86  }
     87  RefPtr<HLSResourceCallbacksSupport> self = this;
     88  NS_DispatchToMainThread(NS_NewRunnableFunction(
     89      "HLSResourceCallbacksSupport::OnDataArrived", [self]() -> void {
     90        if (self->mDecoder) {
     91          self->mDecoder->NotifyDataArrived();
     92        }
     93      }));
     94 }
     95 
     96 void HLSResourceCallbacksSupport::OnError(int aErrorCode) {
     97  HLS_DEBUG("HLSResourceCallbacksSupport", "onError(%d)", aErrorCode);
     98  MutexAutoLock lock(mMutex);
     99  if (!mDecoder) {
    100    return;
    101  }
    102  RefPtr<HLSResourceCallbacksSupport> self = this;
    103  NS_DispatchToMainThread(NS_NewRunnableFunction(
    104      "HLSResourceCallbacksSupport::OnError", [self]() -> void {
    105        if (self->mDecoder) {
    106          // Since HLS source should be from the Internet, we treat all resource
    107          // errors from GeckoHlsPlayer as network errors.
    108          self->mDecoder->NetworkError(
    109              MediaResult(NS_ERROR_FAILURE, "HLS error"));
    110        }
    111      }));
    112 }
    113 
    114 size_t HLSDecoder::sAllocatedInstances = 0;
    115 
    116 // static
    117 RefPtr<HLSDecoder> HLSDecoder::Create(MediaDecoderInit& aInit) {
    118  MOZ_ASSERT(NS_IsMainThread());
    119 
    120  return sAllocatedInstances < StaticPrefs::media_hls_max_allocations()
    121             ? new HLSDecoder(aInit)
    122             : nullptr;
    123 }
    124 
    125 HLSDecoder::HLSDecoder(MediaDecoderInit& aInit) : MediaDecoder(aInit) {
    126  MOZ_ASSERT(NS_IsMainThread());
    127  sAllocatedInstances++;
    128  HLS_DEBUG("HLSDecoder", "HLSDecoder(): allocated=%zu", sAllocatedInstances);
    129 }
    130 
    131 HLSDecoder::~HLSDecoder() {
    132  MOZ_ASSERT(NS_IsMainThread());
    133  MOZ_ASSERT(sAllocatedInstances > 0);
    134  sAllocatedInstances--;
    135  HLS_DEBUG("HLSDecoder", "~HLSDecoder(): allocated=%zu", sAllocatedInstances);
    136 }
    137 
    138 MediaDecoderStateMachineBase* HLSDecoder::CreateStateMachine(
    139    bool aDisableExternalEngine) {
    140  MOZ_ASSERT(NS_IsMainThread());
    141 
    142  MediaFormatReaderInit init;
    143  init.mVideoFrameContainer = GetVideoFrameContainer();
    144  init.mKnowsCompositor = GetCompositor();
    145  init.mCrashHelper = GetOwner()->CreateGMPCrashHelper();
    146  init.mFrameStats = mFrameStats;
    147  init.mMediaDecoderOwnerID = mOwner;
    148  static Atomic<uint32_t> sTrackingIdCounter(0);
    149  init.mTrackingId =
    150      Some(TrackingId(TrackingId::Source::HLSDecoder, sTrackingIdCounter++,
    151                      TrackingId::TrackAcrossProcesses::Yes));
    152  mReader = new MediaFormatReader(
    153      init, new HLSDemuxer(mHLSResourceWrapper->GetPlayerId()));
    154 
    155  return new MediaDecoderStateMachine(this, mReader);
    156 }
    157 
    158 bool HLSDecoder::IsEnabled() { return StaticPrefs::media_hls_enabled(); }
    159 
    160 bool HLSDecoder::IsSupportedType(const MediaContainerType& aContainerType) {
    161  return IsEnabled() && DecoderTraits::IsHttpLiveStreamingType(aContainerType);
    162 }
    163 
    164 nsresult HLSDecoder::Load(nsIChannel* aChannel) {
    165  MOZ_ASSERT(NS_IsMainThread());
    166 
    167  nsresult rv = NS_GetFinalChannelURI(aChannel, getter_AddRefs(mURI));
    168  if (NS_WARN_IF(NS_FAILED(rv))) {
    169    return rv;
    170  }
    171 
    172  mChannel = aChannel;
    173  nsCString spec;
    174  (void)mURI->GetSpec(spec);
    175  mUsageRecorded = false;
    176 
    177  HLSResourceCallbacksSupport::Init();
    178  mJavaCallbacks = java::GeckoHLSResourceWrapper::Callbacks::New();
    179  mCallbackSupport = new HLSResourceCallbacksSupport(this);
    180  HLSResourceCallbacksSupport::AttachNative(mJavaCallbacks, mCallbackSupport);
    181  mHLSResourceWrapper = java::GeckoHLSResourceWrapper::Create(
    182      NS_ConvertUTF8toUTF16(spec), mJavaCallbacks);
    183  MOZ_ASSERT(mHLSResourceWrapper);
    184 
    185  rv = MediaShutdownManager::Instance().Register(this);
    186  if (NS_WARN_IF(NS_FAILED(rv))) {
    187    return rv;
    188  }
    189  return CreateAndInitStateMachine(false);
    190 }
    191 
    192 void HLSDecoder::AddSizeOfResources(ResourceSizes* aSizes) {
    193  MOZ_ASSERT(NS_IsMainThread());
    194  // TODO: track JAVA wrappers.
    195 }
    196 
    197 already_AddRefed<nsIPrincipal> HLSDecoder::GetCurrentPrincipal() {
    198  MOZ_ASSERT(NS_IsMainThread());
    199  return do_AddRef(mContentPrincipal);
    200 }
    201 
    202 bool HLSDecoder::HadCrossOriginRedirects() {
    203  MOZ_ASSERT(NS_IsMainThread());
    204  // Bug 1478843
    205  return false;
    206 }
    207 
    208 void HLSDecoder::Play() {
    209  MOZ_ASSERT(NS_IsMainThread());
    210  HLS_DEBUG("HLSDecoder", "MediaElement called Play");
    211  mHLSResourceWrapper->Play();
    212  return MediaDecoder::Play();
    213 }
    214 
    215 void HLSDecoder::Pause() {
    216  MOZ_ASSERT(NS_IsMainThread());
    217  HLS_DEBUG("HLSDecoder", "MediaElement called Pause");
    218  mHLSResourceWrapper->Pause();
    219  return MediaDecoder::Pause();
    220 }
    221 
    222 void HLSDecoder::Suspend() {
    223  MOZ_ASSERT(NS_IsMainThread());
    224  HLS_DEBUG("HLSDecoder", "Should suspend the resource fetching.");
    225  mHLSResourceWrapper->Suspend();
    226 }
    227 
    228 void HLSDecoder::Resume() {
    229  MOZ_ASSERT(NS_IsMainThread());
    230  HLS_DEBUG("HLSDecoder", "Should resume the resource fetching.");
    231  mHLSResourceWrapper->Resume();
    232 }
    233 
    234 void HLSDecoder::Shutdown() {
    235  HLS_DEBUG("HLSDecoder", "Shutdown");
    236  if (mCallbackSupport) {
    237    mCallbackSupport->Detach();
    238  }
    239  if (mHLSResourceWrapper) {
    240    mHLSResourceWrapper->Destroy();
    241    mHLSResourceWrapper = nullptr;
    242  }
    243  if (mJavaCallbacks) {
    244    HLSResourceCallbacksSupport::DisposeNative(mJavaCallbacks);
    245    mJavaCallbacks = nullptr;
    246  }
    247  MediaDecoder::Shutdown();
    248 }
    249 
    250 void HLSDecoder::NotifyDataArrived() {
    251  MOZ_ASSERT(NS_IsMainThread());
    252  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    253  NotifyReaderDataArrived();
    254  GetOwner()->DownloadProgressed();
    255 }
    256 
    257 void HLSDecoder::NotifyLoad(nsCString aMediaUrl) {
    258  MOZ_ASSERT(NS_IsMainThread());
    259  MOZ_DIAGNOSTIC_ASSERT(!IsShutdown());
    260 
    261  nsCOMPtr<nsIURI> uri;
    262  nsresult rv = NS_NewURI(getter_AddRefs(uri), aMediaUrl.Data());
    263  NS_ENSURE_SUCCESS_VOID(rv);
    264 
    265  RecordMediaUsage(uri);
    266  UpdateCurrentPrincipal(uri);
    267 }
    268 
    269 void HLSDecoder::RecordMediaUsage(nsIURI* aMediaUri) {
    270  if (mUsageRecorded) {
    271    return;
    272  }
    273 
    274  nsresult rv;
    275  nsCOMPtr<nsIURL> url = do_QueryInterface(aMediaUri, &rv);
    276  NS_ENSURE_SUCCESS_VOID(rv);
    277 
    278  // TODO: get hostname. See bug 1887053.
    279  nsAutoCString mediaExt;
    280  (void)url->GetFileExtension(mediaExt);
    281  glean::hls::MediaLoadExtra extra = {.mediaExtension = Some(mediaExt.get())};
    282  glean::hls::media_load.Record(Some(extra));
    283  mUsageRecorded = true;
    284 }
    285 
    286 // Should be called when the decoder loads media from a URL to ensure the
    287 // principal of the media element is appropriately set for CORS.
    288 void HLSDecoder::UpdateCurrentPrincipal(nsIURI* aMediaUri) {
    289  nsCOMPtr<nsIPrincipal> principal = GetContentPrincipal(aMediaUri);
    290  MOZ_DIAGNOSTIC_ASSERT(principal);
    291 
    292  // Check the subsumption of old and new principals. Should be either
    293  // equal or disjoint.
    294  if (!mContentPrincipal || principal->GetIsNullPrincipal()) {
    295    mContentPrincipal = std::move(principal);
    296  } else if (principal->Equals(mContentPrincipal)) {
    297    return;
    298  } else if (!principal->Subsumes(mContentPrincipal) &&
    299             !mContentPrincipal->Subsumes(principal)) {
    300    // Principals are disjoint -- no access.
    301    mContentPrincipal = NullPrincipal::Create(OriginAttributes());
    302  } else {
    303    MOZ_DIAGNOSTIC_CRASH("non-equal principals should be disjoint");
    304    mContentPrincipal = nullptr;
    305  }
    306  MediaDecoder::NotifyPrincipalChanged();
    307 }
    308 
    309 already_AddRefed<nsIPrincipal> HLSDecoder::GetContentPrincipal(
    310    nsIURI* aMediaUri) {
    311  RefPtr<dom::HTMLMediaElement> element = GetOwner()->GetMediaElement();
    312  nsSecurityFlags securityFlags =
    313      element->ShouldCheckAllowOrigin()
    314          ? nsILoadInfo::SEC_REQUIRE_CORS_INHERITS_SEC_CONTEXT
    315          : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_INHERITS_SEC_CONTEXT;
    316  if (element->GetCORSMode() == CORS_USE_CREDENTIALS) {
    317    securityFlags |= nsILoadInfo::SEC_COOKIES_INCLUDE;
    318  }
    319  nsCOMPtr<nsIPrincipal> principal = NullPrincipal::Create(OriginAttributes());
    320  nsCOMPtr<nsIChannel> channel;
    321  nsresult rv = NS_NewChannel(
    322      getter_AddRefs(channel), aMediaUri, static_cast<dom::Element*>(element),
    323      securityFlags, nsIContentPolicy::TYPE_INTERNAL_VIDEO);
    324  NS_ENSURE_SUCCESS(rv, principal.forget());
    325  nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
    326  if (!secMan) {
    327    return principal.forget();
    328  }
    329  secMan->GetChannelResultPrincipal(channel, getter_AddRefs(principal));
    330  return principal.forget();
    331 }
    332 
    333 }  // namespace mozilla