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_