tor-browser

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

MediaKeySystemAccessManager.h (11304B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 #ifndef DOM_MEDIA_MEDIAKEYSYSTEMACCESSMANAGER_H_
      6 #define DOM_MEDIA_MEDIAKEYSYSTEMACCESSMANAGER_H_
      7 
      8 #include "DecoderDoctorDiagnostics.h"
      9 #include "mozilla/MozPromise.h"
     10 #include "mozilla/dom/MediaKeySystemAccess.h"
     11 #include "nsCycleCollectionParticipant.h"
     12 #include "nsIObserver.h"
     13 #include "nsISupportsImpl.h"
     14 #include "nsITimer.h"
     15 
     16 namespace mozilla::dom {
     17 
     18 class DetailedPromise;
     19 class TestGMPVideoDecoder;
     20 
     21 /**
     22 * MediaKeySystemAccessManager implements the functionality for
     23 * Navigator.requestMediaKeySystemAccess(). The navigator may perform its own
     24 * logic before passing the request to this class, but the majority of
     25 * processing happens the MediaKeySystemAccessManager. The manager is expected
     26 * to be run entirely on the main thread of the content process for whichever
     27 * window it is associated with.
     28 *
     29 * As well as implementing the Navigator.requestMediaKeySystemAccess()
     30 * algorithm, the manager performs Gecko specific logic. For example, the EME
     31 * specification does not specify a process to check if a CDM is installed as
     32 * part of requesting access, but that is an important part of obtaining access
     33 * for Gecko, and is handled by the manager.
     34 *
     35 * A request made to the manager can be thought of as entering a pipeline.
     36 * In this pipeline the request must pass through various stages that can
     37 * reject the request and remove it from the pipeline. If a request is not
     38 * rejected by the end of the pipeline it is approved/resolved.
     39 *
     40 * The pipeline is structured in such a way that each step should be executed
     41 * even if it will quickly be exited. For example, the step that checks if a
     42 * window supports protected media is an instant approve on non-Windows OSes,
     43 * but we want to execute the function representing that step to ensure a
     44 * deterministic execution and logging path. The hope is this reduces
     45 * complexity for when we need to debug or change the code.
     46 *
     47 * While the pipeline metaphor generally holds, the implementation details of
     48 * the manager mean that processing is not always linear: a request may be
     49 * re-injected earlier into the pipeline for reprocessing. This can happen
     50 * if the request was pending some other operation, e.g. CDM install, after
     51 * which we wish to reprocess that request. However, we strive to keep it
     52 * as linear as possible.
     53 *
     54 * A high level version of the happy path pipeline is depicted below. If a
     55 * request were to fail any of the steps below it would be rejected and ejected
     56 * from the pipeline.
     57 *
     58 *       Request arrives from navigator
     59 *                   +
     60 *                   |
     61 *                   v
     62 *  Check if window supports protected media
     63 *                   +
     64 *                   +<-------------------+
     65 *                   v                    |
     66 *       Check request args are sane      |
     67 *                   +                    |
     68 *                   |           Wait for CDM and retry
     69 *                   v                    |
     70 *        Check if CDM is installed       |
     71 *                   +                    |
     72 *                   |                    |
     73 *                   +--------------------+
     74 *                   |
     75 *                   v
     76 *       Check if CDM supports args
     77 *                   +
     78 *                   |
     79 *                   v
     80 *    Check if app allows protected media
     81 *           (used by GeckoView)
     82 *                   +
     83 *                   |
     84 *                   v
     85 *            Provide access
     86 *
     87 */
     88 
     89 struct MediaKeySystemAccessRequest {
     90  MediaKeySystemAccessRequest(
     91      const nsAString& aKeySystem,
     92      const Sequence<MediaKeySystemConfiguration>& aConfigs)
     93      : mKeySystem(aKeySystem), mConfigs(aConfigs) {}
     94  virtual ~MediaKeySystemAccessRequest() = default;
     95  // The KeySystem passed for this request.
     96  const nsString mKeySystem;
     97  // The config(s) passed for this request.
     98  const Sequence<MediaKeySystemConfiguration> mConfigs;
     99  DecoderDoctorDiagnostics mDiagnostics;
    100 };
    101 
    102 class MediaKeySystemAccessManager final : public nsIObserver, public nsINamed {
    103 public:
    104  explicit MediaKeySystemAccessManager(nsPIDOMWindowInner* aWindow);
    105 
    106  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
    107  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(MediaKeySystemAccessManager,
    108                                           nsIObserver)
    109  NS_DECL_NSIOBSERVER
    110  NS_DECL_NSINAMED
    111 
    112  using MediaKeySystemAccessPromise =
    113      MozPromise<RefPtr<MediaKeySystemAccess>, MediaResult, true>;
    114 
    115  // Entry point for the navigator to call into the manager.
    116  void Request(DetailedPromise* aPromise, const nsAString& aKeySystem,
    117               const Sequence<MediaKeySystemConfiguration>& aConfig);
    118 
    119  // This is used for creating a key system access for Media Capabilities API.
    120  RefPtr<MediaKeySystemAccessPromise> Request(
    121      const nsAString& aKeySystem,
    122      const Sequence<MediaKeySystemConfiguration>& aConfigs);
    123 
    124  void Shutdown();
    125 
    126 private:
    127  // Encapsulates the information for a Navigator.requestMediaKeySystemAccess()
    128  // request that is being processed.
    129  struct PendingRequest : public MediaKeySystemAccessRequest {
    130    enum class RequestType { Initial, Subsequent };
    131 
    132    PendingRequest(DetailedPromise* aPromise, const nsAString& aKeySystem,
    133                   const Sequence<MediaKeySystemConfiguration>& aConfigs);
    134    virtual ~PendingRequest();
    135 
    136    // The JS promise associated with this request.
    137    RefPtr<DetailedPromise> mPromise;
    138 
    139    // If the request is
    140    // - A first attempt request from JS: RequestType::Initial.
    141    // - A request we're reprocessing due to a GMP being installed:
    142    //   RequestType::Subsequent.
    143    RequestType mRequestType = RequestType::Initial;
    144 
    145    // If we find a supported config for this request during processing it
    146    // should be stored here. Only if we have a supported config should a
    147    // request have access provided.
    148    Maybe<MediaKeySystemConfiguration> mSupportedConfig;
    149 
    150    // Will be set to trigger a timeout and re-processing of the request if the
    151    // request is pending on some potentially time consuming operation, e.g.
    152    // CDM install.
    153    nsCOMPtr<nsITimer> mTimer = nullptr;
    154 
    155    // Convenience methods to reject/resolve the wrapped promise.
    156    virtual void RejectPromiseWithInvalidAccessError(const nsACString& aReason);
    157    virtual void RejectPromiseWithNotSupportedError(const nsACString& aReason);
    158    virtual void RejectPromiseWithTypeError(const nsACString& aReason);
    159    virtual void ResolvePromise(MediaKeySystemAccess* aAccess);
    160 
    161    void CancelTimer();
    162  };
    163 
    164  struct PendingRequestWithMozPromise : public PendingRequest {
    165    PendingRequestWithMozPromise(
    166        const nsAString& aKeySystem,
    167        const Sequence<MediaKeySystemConfiguration>& aConfigs)
    168        : PendingRequest(nullptr, aKeySystem, aConfigs) {};
    169    ~PendingRequestWithMozPromise() = default;
    170 
    171    MozPromiseHolder<MediaKeySystemAccessPromise> mAccessPromise;
    172 
    173    void RejectPromiseWithInvalidAccessError(
    174        const nsACString& aReason) override;
    175    void RejectPromiseWithNotSupportedError(const nsACString& aReason) override;
    176    void RejectPromiseWithTypeError(const nsACString& aReason) override;
    177    void ResolvePromise(MediaKeySystemAccess* aAccess) override;
    178  };
    179 
    180  // Check if the application (e.g. a GeckoView app) allows protected media in
    181  // this window.
    182  //
    183  // This function is always expected to be executed as part of the pipeline of
    184  // processing a request, but its behavior differs depending on prefs set.
    185  //
    186  // If the `media_eme_require_app_approval` pref is false, then the function
    187  // assumes app approval and early returns. Otherwise the function will
    188  // create a permission request to be approved by the embedding app. If the
    189  // test prefs detailed in MediaKeySystemAccessPermissionRequest.h are set
    190  // then they will control handling, otherwise it is up to the embedding
    191  // app to handle the request.
    192  //
    193  // At the time of writing, only GeckoView based apps are expected to pref
    194  // on this behavior.
    195  //
    196  // This function is expected to run late/last in the pipeline so that if we
    197  // ask the app for permission we don't fail after the app okays the request.
    198  // This is to avoid cases where a user may be prompted by the app to approve
    199  // eme, this check then passes, but we fail later in the pipeline, leaving
    200  // the user wondering why their approval didn't work.
    201  void CheckDoesAppAllowProtectedMedia(UniquePtr<PendingRequest> aRequest);
    202 
    203  // Handles the result of the app allowing or disallowing protected media.
    204  // If there are pending requests in mPendingAppApprovalRequests then this
    205  // needs to be called on each.
    206  void OnDoesAppAllowProtectedMedia(bool aIsAllowed,
    207                                    UniquePtr<PendingRequest> aRequest);
    208 
    209  // Checks if the Window associated with this manager supports protected media
    210  // and calls into OnDoesWindowSupportEncryptedMedia with the result.
    211  void CheckDoesWindowSupportProtectedMedia(UniquePtr<PendingRequest> aRequest);
    212 
    213  // Handle the result of checking if the window associated with this manager
    214  // supports protected media. If the window doesn't support protected media
    215  // this will reject the request, otherwise the request will continue to be
    216  // processed.
    217  void OnDoesWindowSupportProtectedMedia(bool aIsSupportedInWindow,
    218                                         UniquePtr<PendingRequest> aRequest);
    219 
    220  // Performs the 'requestMediaKeySystemAccess' algorithm detailed in the EME
    221  // specification. Gecko may need to install a CDM to satisfy this check. If
    222  // CDM install is needed this function may be called again for the same
    223  // request once the CDM is installed or a timeout is reached.
    224  void RequestMediaKeySystemAccess(UniquePtr<PendingRequest> aRequest);
    225 
    226  // Approves aRequest and provides MediaKeySystemAccess by resolving the
    227  // promise associated with the request.
    228  void ProvideAccess(UniquePtr<PendingRequest> aRequest);
    229 
    230  ~MediaKeySystemAccessManager();
    231 
    232  bool EnsureObserversAdded();
    233 
    234  bool AwaitInstall(UniquePtr<PendingRequest> aRequest);
    235 
    236  void RetryRequest(UniquePtr<PendingRequest> aRequest);
    237 
    238  // Requests waiting on approval from the application to be processed.
    239  nsTArray<UniquePtr<PendingRequest>> mPendingAppApprovalRequests;
    240 
    241  // Requests waiting on CDM installation to be processed.
    242  nsTArray<UniquePtr<PendingRequest>> mPendingInstallRequests;
    243 
    244  nsCOMPtr<nsPIDOMWindowInner> mWindow;
    245  bool mAddedObservers = false;
    246 
    247  // Has the app approved protected media playback? If it has we cache the
    248  // value so we don't need to check again.
    249  Maybe<bool> mAppAllowsProtectedMedia;
    250 
    251  // If we're waiting for permission from the app to enable EME this holder
    252  // should contain the request.
    253  //
    254  // Note the type in the holder should match
    255  // MediaKeySystemAccessPermissionRequest::RequestPromise, but we can't
    256  // include MediaKeySystemAccessPermissionRequest's header here without
    257  // breaking the build, so we do this hack.
    258  MozPromiseRequestHolder<MozPromise<bool, bool, true>>
    259      mAppAllowsProtectedMediaPromiseRequest;
    260 };
    261 
    262 }  // namespace mozilla::dom
    263 
    264 #endif  // DOM_MEDIA_MEDIAKEYSYSTEMACCESSMANAGER_H_