/* 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();
}
Exemple #3
0
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();
}
Exemple #4
0
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;
}
Exemple #6
0
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();
}