tor-browser

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

MediaStreamAudioSourceNode.cpp (9259B)


      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 "MediaStreamAudioSourceNode.h"
      8 
      9 #include "AudioNodeEngine.h"
     10 #include "AudioNodeExternalInputTrack.h"
     11 #include "AudioStreamTrack.h"
     12 #include "Tracing.h"
     13 #include "mozilla/dom/Document.h"
     14 #include "mozilla/dom/MediaStreamAudioSourceNodeBinding.h"
     15 #include "nsContentUtils.h"
     16 #include "nsGlobalWindowInner.h"
     17 #include "nsID.h"
     18 #include "nsIScriptError.h"
     19 
     20 namespace mozilla::dom {
     21 
     22 NS_IMPL_CYCLE_COLLECTION_CLASS(MediaStreamAudioSourceNode)
     23 
     24 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(MediaStreamAudioSourceNode)
     25  tmp->Destroy();
     26  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputStream)
     27  NS_IMPL_CYCLE_COLLECTION_UNLINK(mInputTrack)
     28  NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
     29 NS_IMPL_CYCLE_COLLECTION_UNLINK_END_INHERITED(AudioNode)
     30 
     31 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(MediaStreamAudioSourceNode,
     32                                                  AudioNode)
     33  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputStream)
     34  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mInputTrack)
     35  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener)
     36 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
     37 
     38 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MediaStreamAudioSourceNode)
     39 NS_INTERFACE_MAP_END_INHERITING(AudioNode)
     40 
     41 NS_IMPL_ADDREF_INHERITED(MediaStreamAudioSourceNode, AudioNode)
     42 NS_IMPL_RELEASE_INHERITED(MediaStreamAudioSourceNode, AudioNode)
     43 
     44 MediaStreamAudioSourceNode::MediaStreamAudioSourceNode(
     45    AudioContext* aContext, TrackChangeBehavior aBehavior)
     46    : AudioNode(aContext, 2, ChannelCountMode::Max,
     47                ChannelInterpretation::Speakers),
     48      mBehavior(aBehavior) {}
     49 
     50 /* static */
     51 already_AddRefed<MediaStreamAudioSourceNode> MediaStreamAudioSourceNode::Create(
     52    AudioContext& aAudioContext, const MediaStreamAudioSourceOptions& aOptions,
     53    ErrorResult& aRv) {
     54  // The spec has a pointless check here.  See
     55  // https://github.com/WebAudio/web-audio-api/issues/2149
     56  MOZ_RELEASE_ASSERT(!aAudioContext.IsOffline(), "Bindings messed up?");
     57 
     58  RefPtr<MediaStreamAudioSourceNode> node =
     59      new MediaStreamAudioSourceNode(&aAudioContext, LockOnTrackPicked);
     60 
     61  // aOptions.mMediaStream is not nullable.
     62  node->Init(*aOptions.mMediaStream, aRv);
     63  if (aRv.Failed()) {
     64    return nullptr;
     65  }
     66 
     67  return node.forget();
     68 }
     69 
     70 void MediaStreamAudioSourceNode::Init(DOMMediaStream& aMediaStream,
     71                                      ErrorResult& aRv) {
     72  mListener = new TrackListener(this);
     73  mInputStream = &aMediaStream;
     74  AudioNodeEngine* engine = new MediaStreamAudioSourceNodeEngine(this);
     75  mTrack = AudioNodeExternalInputTrack::Create(Context()->Graph(), engine);
     76  mInputStream->AddConsumerToKeepAlive(ToSupports(this));
     77 
     78  mInputStream->RegisterTrackListener(mListener);
     79  if (mInputStream->Audible()) {
     80    NotifyAudible();
     81  }
     82  AttachToRightTrack(mInputStream, aRv);
     83 }
     84 
     85 void MediaStreamAudioSourceNode::Destroy() {
     86  if (mInputStream) {
     87    mInputStream->UnregisterTrackListener(mListener);
     88    mInputStream = nullptr;
     89    mListener = nullptr;
     90  }
     91  DetachFromTrack();
     92 }
     93 
     94 MediaStreamAudioSourceNode::~MediaStreamAudioSourceNode() { Destroy(); }
     95 
     96 void MediaStreamAudioSourceNode::AttachToTrack(AudioStreamTrack* aTrack) {
     97  MOZ_ASSERT(aTrack);
     98  MOZ_ASSERT(!mInputTrack);
     99  MOZ_DIAGNOSTIC_ASSERT(!aTrack->Ended());
    100 
    101  if (!mTrack) {
    102    return;
    103  }
    104 
    105  mInputTrack = aTrack;
    106  ProcessedMediaTrack* outputTrack =
    107      static_cast<ProcessedMediaTrack*>(mTrack.get());
    108  mInputPort = aTrack->AddConsumerPort(outputTrack);
    109  MOZ_DIAGNOSTIC_ASSERT(mInputPort);
    110 
    111  PrincipalChanged(mInputTrack);  // trigger enabling/disabling of the connector
    112  mInputTrack->AddPrincipalChangeObserver(this);
    113  MarkActive();
    114 }
    115 
    116 void MediaStreamAudioSourceNode::DetachFromTrack() {
    117  if (mInputTrack) {
    118    mInputTrack->RemovePrincipalChangeObserver(this);
    119    mInputTrack->RemoveConsumerPort(mInputPort);
    120    mInputTrack = nullptr;
    121  }
    122  if (mInputPort) {
    123    mInputPort->Destroy();
    124    mInputPort = nullptr;
    125  }
    126 }
    127 
    128 static int AudioTrackCompare(const RefPtr<AudioStreamTrack>& aLhs,
    129                             const RefPtr<AudioStreamTrack>& aRhs) {
    130  nsAutoStringN<NSID_LENGTH> IDLhs;
    131  nsAutoStringN<NSID_LENGTH> IDRhs;
    132  aLhs->GetId(IDLhs);
    133  aRhs->GetId(IDRhs);
    134  return Compare(NS_ConvertUTF16toUTF8(IDLhs), NS_ConvertUTF16toUTF8(IDRhs));
    135 }
    136 
    137 void MediaStreamAudioSourceNode::AttachToRightTrack(
    138    const RefPtr<DOMMediaStream>& aMediaStream, ErrorResult& aRv) {
    139  nsTArray<RefPtr<AudioStreamTrack>> tracks;
    140  aMediaStream->GetAudioTracks(tracks);
    141 
    142  if (tracks.IsEmpty() && mBehavior == LockOnTrackPicked) {
    143    aRv.ThrowInvalidStateError("No audio tracks in MediaStream");
    144    return;
    145  }
    146 
    147  // Sort the track to have a stable order, on their ID by lexicographic
    148  // ordering on sequences of code unit values.
    149  tracks.Sort(AudioTrackCompare);
    150 
    151  for (const RefPtr<AudioStreamTrack>& track : tracks) {
    152    if (mBehavior == FollowChanges) {
    153      if (track->Ended()) {
    154        continue;
    155      }
    156    }
    157 
    158    if (!track->Ended()) {
    159      AttachToTrack(track);
    160    }
    161    return;
    162  }
    163 
    164  // There was no track available. We'll allow the node to be garbage collected.
    165  MarkInactive();
    166 }
    167 
    168 void MediaStreamAudioSourceNode::NotifyTrackAdded(
    169    const RefPtr<MediaStreamTrack>& aTrack) {
    170  if (mBehavior != FollowChanges) {
    171    return;
    172  }
    173  if (mInputTrack) {
    174    return;
    175  }
    176 
    177  if (!aTrack->AsAudioStreamTrack()) {
    178    return;
    179  }
    180 
    181  AttachToTrack(aTrack->AsAudioStreamTrack());
    182 }
    183 
    184 void MediaStreamAudioSourceNode::NotifyTrackRemoved(
    185    const RefPtr<MediaStreamTrack>& aTrack) {
    186  if (mBehavior == FollowChanges) {
    187    if (aTrack != mInputTrack) {
    188      return;
    189    }
    190 
    191    DetachFromTrack();
    192    AttachToRightTrack(mInputStream, IgnoreErrors());
    193  }
    194 }
    195 
    196 void MediaStreamAudioSourceNode::NotifyAudible() {
    197  MOZ_ASSERT(mInputStream);
    198  Context()->StartBlockedAudioContextIfAllowed();
    199 }
    200 
    201 /**
    202 * Changes the principal. Note that this will be called on the main thread, but
    203 * changes will be enacted on the MediaTrackGraph thread. If the principal
    204 * change results in the document principal losing access to the stream, then
    205 * there needs to be other measures in place to ensure that any media that is
    206 * governed by the new stream principal is not available to the MediaTrackGraph
    207 * before this change completes. Otherwise, a site could get access to
    208 * media that they are not authorized to receive.
    209 *
    210 * One solution is to block the altered content, call this method, then dispatch
    211 * another change request to the MediaTrackGraph thread that allows the content
    212 * under the new principal to flow. This might be unnecessary if the principal
    213 * change is changing to be the document principal.
    214 */
    215 void MediaStreamAudioSourceNode::PrincipalChanged(
    216    MediaStreamTrack* aMediaStreamTrack) {
    217  MOZ_ASSERT(aMediaStreamTrack == mInputTrack);
    218 
    219  bool subsumes = false;
    220  Document* doc = nullptr;
    221  if (nsGlobalWindowInner* parent = Context()->GetOwnerWindow()) {
    222    doc = parent->GetExtantDoc();
    223    if (doc) {
    224      nsIPrincipal* docPrincipal = doc->NodePrincipal();
    225      nsIPrincipal* trackPrincipal = aMediaStreamTrack->GetPrincipal();
    226      if (!trackPrincipal ||
    227          NS_FAILED(docPrincipal->Subsumes(trackPrincipal, &subsumes))) {
    228        subsumes = false;
    229      }
    230    }
    231  }
    232  auto track = static_cast<AudioNodeExternalInputTrack*>(mTrack.get());
    233  bool enabled = subsumes;
    234  track->SetInt32Parameter(MediaStreamAudioSourceNodeEngine::ENABLE, enabled);
    235 
    236  if (!enabled && doc) {
    237    nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "Web Audio"_ns,
    238                                    doc, nsContentUtils::eDOM_PROPERTIES,
    239                                    CrossOriginErrorString());
    240  }
    241 }
    242 
    243 size_t MediaStreamAudioSourceNode::SizeOfExcludingThis(
    244    MallocSizeOf aMallocSizeOf) const {
    245  // Future:
    246  // - mInputStream
    247  size_t amount = AudioNode::SizeOfExcludingThis(aMallocSizeOf);
    248  if (mInputPort) {
    249    amount += mInputPort->SizeOfIncludingThis(aMallocSizeOf);
    250  }
    251  return amount;
    252 }
    253 
    254 size_t MediaStreamAudioSourceNode::SizeOfIncludingThis(
    255    MallocSizeOf aMallocSizeOf) const {
    256  return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf);
    257 }
    258 
    259 void MediaStreamAudioSourceNode::DestroyMediaTrack() {
    260  DetachFromTrack();
    261  AudioNode::DestroyMediaTrack();
    262 }
    263 
    264 JSObject* MediaStreamAudioSourceNode::WrapObject(
    265    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
    266  return MediaStreamAudioSourceNode_Binding::Wrap(aCx, this, aGivenProto);
    267 }
    268 
    269 NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaStreamAudioSourceNode::TrackListener,
    270                                   DOMMediaStream::TrackListener, mNode)
    271 NS_IMPL_ADDREF_INHERITED(MediaStreamAudioSourceNode::TrackListener,
    272                         DOMMediaStream::TrackListener)
    273 NS_IMPL_RELEASE_INHERITED(MediaStreamAudioSourceNode::TrackListener,
    274                          DOMMediaStream::TrackListener)
    275 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
    276    MediaStreamAudioSourceNode::TrackListener)
    277 NS_INTERFACE_MAP_END_INHERITING(DOMMediaStream::TrackListener)
    278 
    279 }  // namespace mozilla::dom