/* static */ already_AddRefed<PresentationRequest> PresentationRequest::Constructor(const GlobalObject& aGlobal, const Sequence<nsString>& aUrls, ErrorResult& aRv) { nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(aGlobal.GetAsSupports()); if (!window) { aRv.Throw(NS_ERROR_UNEXPECTED); return nullptr; } if (aUrls.IsEmpty()) { aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return nullptr; } // Resolve relative URL to absolute URL nsCOMPtr<nsIURI> baseUri = window->GetDocBaseURI(); nsTArray<nsString> urls; for (const auto& url : aUrls) { nsAutoString absoluteUrl; nsresult rv = GetAbsoluteURL(url, baseUri, window->GetExtantDoc(), absoluteUrl); if (NS_WARN_IF(NS_FAILED(rv))) { aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR); return nullptr; } urls.AppendElement(absoluteUrl); } RefPtr<PresentationRequest> request = new PresentationRequest(window, Move(urls)); return NS_WARN_IF(!request->Init()) ? nullptr : request.forget(); }
/* static */ already_AddRefed<DOMMediaStream> DOMMediaStream::Constructor(const GlobalObject& aGlobal, const Sequence<OwningNonNull<MediaStreamTrack>>& aTracks, ErrorResult& aRv) { nsCOMPtr<nsIDOMWindow> ownerWindow = do_QueryInterface(aGlobal.GetAsSupports()); if (!ownerWindow) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } RefPtr<DOMMediaStream> newStream = new DOMMediaStream(); newStream->mWindow = ownerWindow; for (MediaStreamTrack& track : aTracks) { if (!newStream->GetPlaybackStream()) { MOZ_RELEASE_ASSERT(track.GetStream()); MOZ_RELEASE_ASSERT(track.GetStream()->GetPlaybackStream()); MOZ_RELEASE_ASSERT(track.GetStream()->GetPlaybackStream()->Graph()); MediaStreamGraph* graph = track.GetStream()->GetPlaybackStream()->Graph(); newStream->InitPlaybackStreamCommon(graph); } newStream->AddTrack(track); } if (!newStream->GetPlaybackStream()) { MOZ_ASSERT(aTracks.IsEmpty()); MediaStreamGraph* graph = MediaStreamGraph::GetInstance(MediaStreamGraph::SYSTEM_THREAD_DRIVER, AudioChannel::Normal); newStream->InitPlaybackStreamCommon(graph); } return newStream.forget(); }
already_AddRefed<Promise> Cache::AddAll(const Sequence<OwningRequestOrUSVString>& aRequests, ErrorResult& aRv) { MOZ_ASSERT(mActor); nsRefPtr<Promise> promise = Promise::Create(mGlobal, aRv); if (!promise) { return nullptr; } // If there is no work to do, then resolve immediately if (aRequests.IsEmpty()) { promise->MaybeResolve(JS::UndefinedHandleValue); return promise.forget(); } AutoChildRequestList requests(this, aRequests.Length()); for (uint32_t i = 0; i < aRequests.Length(); ++i) { if (!IsValidPutRequestMethod(aRequests[i], aRv)) { return nullptr; } nsRefPtr<InternalRequest> ir = ToInternalRequest(aRequests[i], ReadBody, aRv); if (aRv.Failed()) { return nullptr; } requests.Add(ir, ReadBody, ExpandReferrer, NetworkErrorOnInvalidScheme, aRv); if (aRv.Failed()) { return nullptr; } } RequestId requestId = AddRequestPromise(promise, aRv); unused << mActor->SendAddAll(requestId, requests.SendAsRequestList()); return promise.forget(); }
void FileList::ToSequence(Sequence<OwningFileOrDirectory>& aSequence, ErrorResult& aRv) const { MOZ_ASSERT(aSequence.IsEmpty()); if (mFilesOrDirectories.IsEmpty()) { return; } if (!aSequence.SetLength(mFilesOrDirectories.Length(), mozilla::fallible_t())) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } for (uint32_t i = 0; i < mFilesOrDirectories.Length(); ++i) { aSequence[i] = mFilesOrDirectories[i]; } }
// 3.1.2.2 Get Supported Configuration and Consent static bool GetSupportedConfig(mozIGeckoMediaPluginService* aGMPService, const KeySystemConfig& aKeySystem, const MediaKeySystemConfiguration& aCandidate, MediaKeySystemConfiguration& aOutConfig, DecoderDoctorDiagnostics* aDiagnostics) { // Let accumulated configuration be a new MediaKeySystemConfiguration dictionary. MediaKeySystemConfiguration config; // Set the label member of accumulated configuration to equal the label member of // candidate configuration. config.mLabel = aCandidate.mLabel; // If the initDataTypes member of candidate configuration is non-empty, run the // following steps: if (!aCandidate.mInitDataTypes.IsEmpty()) { // Let supported types be an empty sequence of DOMStrings. nsTArray<nsString> supportedTypes; // For each value in candidate configuration's initDataTypes member: for (const nsString& initDataType : aCandidate.mInitDataTypes) { // Let initDataType be the value. // If the implementation supports generating requests based on initDataType, // add initDataType to supported types. String comparison is case-sensitive. // The empty string is never supported. if (aKeySystem.mInitDataTypes.Contains(initDataType)) { supportedTypes.AppendElement(initDataType); } } // If supported types is empty, return NotSupported. if (supportedTypes.IsEmpty()) { EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; " "no supported initDataTypes provided.", NS_ConvertUTF16toUTF8(aCandidate.mLabel).get()); return false; } // Set the initDataTypes member of accumulated configuration to supported types. if (!config.mInitDataTypes.Assign(supportedTypes)) { return false; } } if (!CheckRequirement(aCandidate.mDistinctiveIdentifier, aKeySystem.mDistinctiveIdentifier, config.mDistinctiveIdentifier)) { EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; " "distinctiveIdentifier requirement not satisfied.", NS_ConvertUTF16toUTF8(aCandidate.mLabel).get()); return false; } if (!CheckRequirement(aCandidate.mPersistentState, aKeySystem.mPersistentState, config.mPersistentState)) { EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; " "persistentState requirement not satisfied.", NS_ConvertUTF16toUTF8(aCandidate.mLabel).get()); return false; } Sequence<nsString> sessionTypes(UnboxSessionTypes(aCandidate.mSessionTypes)); if (sessionTypes.IsEmpty()) { // Malloc failure. return false; } // For each value in session types: for (const auto& sessionTypeString : sessionTypes) { // Let session type be the value. MediaKeySessionType sessionType; if (!ToSessionType(sessionTypeString, sessionType)) { // (Assume invalid sessionType is unsupported as per steps below). EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; " "invalid session type specified.", NS_ConvertUTF16toUTF8(aCandidate.mLabel).get()); return false; } // If accumulated configuration's persistentState value is "not-allowed" // and the Is persistent session type? algorithm returns true for session // type return NotSupported. if (config.mPersistentState == MediaKeysRequirement::Not_allowed && IsPersistentSessionType(sessionType)) { EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; " "persistent session requested but keysystem doesn't" "support persistent state.", NS_ConvertUTF16toUTF8(aCandidate.mLabel).get()); return false; } // If the implementation does not support session type in combination // with accumulated configuration and restrictions for other reasons, // return NotSupported. if (!aKeySystem.mSessionTypes.Contains(sessionType)) { EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; " "session type '%s' unsupported by keySystem.", NS_ConvertUTF16toUTF8(aCandidate.mLabel).get(), NS_ConvertUTF16toUTF8(sessionTypeString).get()); return false; } // If accumulated configuration's persistentState value is "optional" // and the result of running the Is persistent session type? algorithm // on session type is true, change accumulated configuration's // persistentState value to "required". if (config.mPersistentState == MediaKeysRequirement::Optional && IsPersistentSessionType(sessionType)) { config.mPersistentState = MediaKeysRequirement::Required; } } // Set the sessionTypes member of accumulated configuration to session types. config.mSessionTypes.Construct(Move(sessionTypes)); // If the videoCapabilities and audioCapabilities members in candidate // configuration are both empty, return NotSupported. // TODO: Most sites using EME still don't pass capabilities, so we // can't reject on it yet without breaking them. So add this later. // If the videoCapabilities member in candidate configuration is non-empty: if (!aCandidate.mVideoCapabilities.IsEmpty()) { // Let video capabilities be the result of executing the Get Supported // Capabilities for Audio/Video Type algorithm on Video, candidate // configuration's videoCapabilities member, accumulated configuration, // and restrictions. Sequence<MediaKeySystemMediaCapability> caps = GetSupportedCapabilities(Video, aGMPService, aCandidate.mVideoCapabilities, config, aKeySystem, aDiagnostics); // If video capabilities is null, return NotSupported. if (caps.IsEmpty()) { EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; " "no supported video capabilities.", NS_ConvertUTF16toUTF8(aCandidate.mLabel).get()); return false; } // Set the videoCapabilities member of accumulated configuration to video capabilities. config.mVideoCapabilities = Move(caps); } else { // Otherwise: // Set the videoCapabilities member of accumulated configuration to an empty sequence. } // If the audioCapabilities member in candidate configuration is non-empty: if (!aCandidate.mAudioCapabilities.IsEmpty()) { // Let audio capabilities be the result of executing the Get Supported Capabilities // for Audio/Video Type algorithm on Audio, candidate configuration's audioCapabilities // member, accumulated configuration, and restrictions. Sequence<MediaKeySystemMediaCapability> caps = GetSupportedCapabilities(Audio, aGMPService, aCandidate.mAudioCapabilities, config, aKeySystem, aDiagnostics); // If audio capabilities is null, return NotSupported. if (caps.IsEmpty()) { EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; " "no supported audio capabilities.", NS_ConvertUTF16toUTF8(aCandidate.mLabel).get()); return false; } // Set the audioCapabilities member of accumulated configuration to audio capabilities. config.mAudioCapabilities = Move(caps); } else { // Otherwise: // Set the audioCapabilities member of accumulated configuration to an empty sequence. } // If accumulated configuration's distinctiveIdentifier value is "optional", follow the // steps for the first matching condition from the following list: if (config.mDistinctiveIdentifier == MediaKeysRequirement::Optional) { // If the implementation requires use Distinctive Identifier(s) or // Distinctive Permanent Identifier(s) for any of the combinations // in accumulated configuration if (aKeySystem.mDistinctiveIdentifier == KeySystemFeatureSupport::Required) { // Change accumulated configuration's distinctiveIdentifier value to "required". config.mDistinctiveIdentifier = MediaKeysRequirement::Required; } else { // Otherwise, change accumulated configuration's distinctiveIdentifier // value to "not-allowed". config.mDistinctiveIdentifier = MediaKeysRequirement::Not_allowed; } } // If accumulated configuration's persistentState value is "optional", follow the // steps for the first matching condition from the following list: if (config.mPersistentState == MediaKeysRequirement::Optional) { // If the implementation requires persisting state for any of the combinations // in accumulated configuration if (aKeySystem.mPersistentState == KeySystemFeatureSupport::Required) { // Change accumulated configuration's persistentState value to "required". config.mPersistentState = MediaKeysRequirement::Required; } else { // Otherwise, change accumulated configuration's persistentState // value to "not-allowed". config.mPersistentState = MediaKeysRequirement::Not_allowed; } } // Note: Omitting steps 20-22. We don't ask for consent. #if defined(XP_WIN) // Widevine CDM doesn't include an AAC decoder. So if WMF can't decode AAC, // and a codec wasn't specified, be conservative and reject the MediaKeys request. if (IsWidevineKeySystem(aKeySystem.mKeySystem) && (aCandidate.mAudioCapabilities.IsEmpty() || aCandidate.mVideoCapabilities.IsEmpty()) && !WMFDecoderModule::HasAAC()) { if (aDiagnostics) { aDiagnostics->SetKeySystemIssue( DecoderDoctorDiagnostics::eWidevineWithNoWMF); } EME_LOG("MediaKeySystemConfiguration (label='%s') rejected; " "WMF required for Widevine decoding, but it's not available.", NS_ConvertUTF16toUTF8(aCandidate.mLabel).get()); return false; } #endif // Return accumulated configuration. aOutConfig = config; return true; }
already_AddRefed<Promise> WebAuthentication::MakeCredential(JSContext* aCx, const Account& aAccount, const Sequence<ScopedCredentialParameters>& aCryptoParameters, const ArrayBufferViewOrArrayBuffer& aChallenge, const ScopedCredentialOptions& aOptions) { MOZ_ASSERT(mParent); nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()); if (!global) { return nullptr; } ErrorResult rv; RefPtr<Promise> promise = Promise::Create(global, rv); nsresult initRv = InitLazily(); if (NS_FAILED(initRv)) { promise->MaybeReject(initRv); return promise.forget(); } // 4.1.1.1 If timeoutSeconds was specified, check if its value lies within a // reasonable range as defined by the platform and if not, correct it to the // closest value lying within that range. double adjustedTimeout = 30.0; if (aOptions.mTimeoutSeconds.WasPassed()) { adjustedTimeout = aOptions.mTimeoutSeconds.Value(); adjustedTimeout = std::max(15.0, adjustedTimeout); adjustedTimeout = std::min(120.0, adjustedTimeout); } // 4.1.1.2 Let promise be a new Promise. Return promise and start a timer for // adjustedTimeout seconds. RefPtr<CredentialRequest> requestMonitor = new CredentialRequest(); requestMonitor->SetDeadline(TimeDuration::FromSeconds(adjustedTimeout)); if (mOrigin.EqualsLiteral("null")) { // 4.1.1.3 If callerOrigin is an opaque origin, reject promise with a // DOMException whose name is "NotAllowedError", and terminate this // algorithm MOZ_LOG(gWebauthLog, LogLevel::Debug, ("Rejecting due to opaque origin")); promise->MaybeReject(NS_ERROR_DOM_NOT_ALLOWED_ERR); return promise.forget(); } nsCString rpId; if (!aOptions.mRpId.WasPassed()) { // 4.1.1.3.a If rpId is not specified, then set rpId to callerOrigin, and // rpIdHash to the SHA-256 hash of rpId. rpId.Assign(NS_ConvertUTF16toUTF8(mOrigin)); } else { // 4.1.1.3.b If rpId is specified, then invoke the procedure used for // relaxing the same-origin restriction by setting the document.domain // attribute, using rpId as the given value but without changing the current // document’s domain. If no errors are thrown, set rpId to the value of host // as computed by this procedure, and rpIdHash to the SHA-256 hash of rpId. // Otherwise, reject promise with a DOMException whose name is // "SecurityError", and terminate this algorithm. if (NS_FAILED(RelaxSameOrigin(aOptions.mRpId.Value(), rpId))) { promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); return promise.forget(); } } CryptoBuffer rpIdHash; if (!rpIdHash.SetLength(SHA256_LENGTH, fallible)) { promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); return promise.forget(); } nsresult srv; nsCOMPtr<nsICryptoHash> hashService = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &srv); if (NS_WARN_IF(NS_FAILED(srv))) { promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); return promise.forget(); } srv = HashCString(hashService, rpId, rpIdHash); if (NS_WARN_IF(NS_FAILED(srv))) { promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); return promise.forget(); } // 4.1.1.4 Process each element of cryptoParameters using the following steps, // to produce a new sequence normalizedParameters. nsTArray<ScopedCredentialParameters> normalizedParams; for (size_t a = 0; a < aCryptoParameters.Length(); ++a) { // 4.1.1.4.a Let current be the currently selected element of // cryptoParameters. // 4.1.1.4.b If current.type does not contain a ScopedCredentialType // supported by this implementation, then stop processing current and move // on to the next element in cryptoParameters. if (aCryptoParameters[a].mType != ScopedCredentialType::ScopedCred) { continue; } // 4.1.1.4.c Let normalizedAlgorithm be the result of normalizing an // algorithm using the procedure defined in [WebCryptoAPI], with alg set to // current.algorithm and op set to 'generateKey'. If an error occurs during // this procedure, then stop processing current and move on to the next // element in cryptoParameters. nsString algName; if (NS_FAILED(GetAlgorithmName(aCx, aCryptoParameters[a].mAlgorithm, algName))) { continue; } // 4.1.1.4.d Add a new object of type ScopedCredentialParameters to // normalizedParameters, with type set to current.type and algorithm set to // normalizedAlgorithm. ScopedCredentialParameters normalizedObj; normalizedObj.mType = aCryptoParameters[a].mType; normalizedObj.mAlgorithm.SetAsString().Assign(algName); if (!normalizedParams.AppendElement(normalizedObj, mozilla::fallible)){ promise->MaybeReject(NS_ERROR_OUT_OF_MEMORY); return promise.forget(); } } // 4.1.1.5 If normalizedAlgorithm is empty and cryptoParameters was not empty, // cancel the timer started in step 2, reject promise with a DOMException // whose name is "NotSupportedError", and terminate this algorithm. if (normalizedParams.IsEmpty() && !aCryptoParameters.IsEmpty()) { promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR); return promise.forget(); } // 4.1.1.6 If excludeList is undefined, set it to the empty list. // 4.1.1.7 If extensions was specified, process any extensions supported by // this client platform, to produce the extension data that needs to be sent // to the authenticator. If an error is encountered while processing an // extension, skip that extension and do not produce any extension data for // it. Call the result of this processing clientExtensions. // Currently no extensions are supported // 4.1.1.8 Use attestationChallenge, callerOrigin and rpId, along with the // token binding key associated with callerOrigin (if any), to create a // ClientData structure representing this request. Choose a hash algorithm for // hashAlg and compute the clientDataJSON and clientDataHash. CryptoBuffer challenge; if (!challenge.Assign(aChallenge)) { promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); return promise.forget(); } nsAutoCString clientDataJSON; srv = AssembleClientData(mOrigin, challenge, clientDataJSON); if (NS_WARN_IF(NS_FAILED(srv))) { promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); return promise.forget(); } CryptoBuffer clientDataHash; if (!clientDataHash.SetLength(SHA256_LENGTH, fallible)) { promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); return promise.forget(); } srv = HashCString(hashService, clientDataJSON, clientDataHash); if (NS_WARN_IF(NS_FAILED(srv))) { promise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR); return promise.forget(); } // 4.1.1.9 Initialize issuedRequests to an empty list. RefPtr<CredentialPromise> monitorPromise = requestMonitor->Ensure(); // 4.1.1.10 For each authenticator currently available on this platform: // asynchronously invoke the authenticatorMakeCredential operation on that // authenticator with rpIdHash, clientDataHash, accountInformation, // normalizedParameters, excludeList and clientExtensions as parameters. Add a // corresponding entry to issuedRequests. for (Authenticator u2ftoken : mAuthenticators) { // 4.1.1.10.a For each credential C in excludeList that has a non-empty // transports list, optionally use only the specified transports to test for // the existence of C. U2FAuthMakeCredential(requestMonitor, u2ftoken, rpIdHash, clientDataJSON, clientDataHash, aAccount, normalizedParams, aOptions.mExcludeList, aOptions.mExtensions); } requestMonitor->CompleteTask(); monitorPromise->Then(AbstractThread::MainThread(), __func__, [promise] (CredentialPtr aInfo) { promise->MaybeResolve(aInfo); }, [promise] (nsresult aErrorCode) { promise->MaybeReject(aErrorCode); }); return promise.forget(); }