| Index: third_party/WebKit/Source/modules/webauth/WebAuthentication.cpp | 
| diff --git a/third_party/WebKit/Source/modules/webauth/WebAuthentication.cpp b/third_party/WebKit/Source/modules/webauth/WebAuthentication.cpp | 
| index 06cba2c2be2f6b4296dedf8ac97fc645435b2f85..b2f17e4469c8efebfaf4a79936e87f8556f3a3f6 100644 | 
| --- a/third_party/WebKit/Source/modules/webauth/WebAuthentication.cpp | 
| +++ b/third_party/WebKit/Source/modules/webauth/WebAuthentication.cpp | 
| @@ -1,4 +1,4 @@ | 
| -// Copyright 2016 The Chromium Authors. All rights reserved. | 
| +// Copyright 2017 The Chromium Authors. All rights reserved. | 
| // Use of this source code is governed by a BSD-style license that can be | 
| // found in the LICENSE file. | 
|  | 
| @@ -20,23 +20,26 @@ | 
|  | 
| namespace { | 
| const char kNoAuthenticatorError[] = "Authenticator unavailable."; | 
| +// Time to wait for an authenticator to successfully complete an operation. | 
| +const int kAdjustedTimeoutLowerInSeconds = 60; | 
| +const int kAdjustedTimeoutUpperInSeconds = 120; | 
| }  // anonymous namespace | 
|  | 
| namespace mojo { | 
| - | 
| using webauth::mojom::blink::RelyingPartyAccount; | 
| using webauth::mojom::blink::RelyingPartyAccountPtr; | 
| +using webauth::mojom::blink::AuthenticatorStatus; | 
| +using webauth::mojom::blink::ScopedCredentialDescriptor; | 
| using webauth::mojom::blink::ScopedCredentialOptions; | 
| using webauth::mojom::blink::ScopedCredentialOptionsPtr; | 
| using webauth::mojom::blink::ScopedCredentialParameters; | 
| using webauth::mojom::blink::ScopedCredentialParametersPtr; | 
| -using webauth::mojom::blink::ScopedCredentialDescriptor; | 
| using webauth::mojom::blink::ScopedCredentialType; | 
| using webauth::mojom::blink::Transport; | 
|  | 
| // TODO(kpaulhamus): Make this a TypeConverter | 
| Vector<uint8_t> ConvertBufferSource(const blink::BufferSource& buffer) { | 
| -  DCHECK(buffer.isNull()); | 
| +  DCHECK(!buffer.isNull()); | 
| Vector<uint8_t> vector; | 
| if (buffer.isArrayBuffer()) { | 
| vector.Append(static_cast<uint8_t*>(buffer.getAsArrayBuffer()->Data()), | 
| @@ -69,6 +72,7 @@ Transport ConvertTransport(const String& transport) { | 
| return Transport::USB; | 
| } | 
|  | 
| +// TODO(kpaulhamus): Make this a TypeConverter | 
| RelyingPartyAccountPtr ConvertRelyingPartyAccount( | 
| const blink::RelyingPartyAccount& account_information, | 
| blink::ScriptPromiseResolver* resolver) { | 
| @@ -88,17 +92,32 @@ ScopedCredentialOptionsPtr ConvertScopedCredentialOptions( | 
| const blink::ScopedCredentialOptions options, | 
| blink::ScriptPromiseResolver* resolver) { | 
| auto mojo_options = ScopedCredentialOptions::New(); | 
| -  mojo_options->timeout_seconds = options.timeoutSeconds(); | 
| -  mojo_options->relying_party_id = options.rpId(); | 
| - | 
| -  // Adds the excludeList members (which are ScopedCredentialDescriptors) | 
| -  for (const auto& descriptor : options.excludeList()) { | 
| -    auto mojo_descriptor = ScopedCredentialDescriptor::New(); | 
| -    mojo_descriptor->type = ConvertScopedCredentialType(descriptor.type()); | 
| -    mojo_descriptor->id = ConvertBufferSource(descriptor.id()); | 
| -    for (const auto& transport : descriptor.transports()) | 
| -      mojo_descriptor->transports.push_back(ConvertTransport(transport)); | 
| -    mojo_options->exclude_list.push_back(std::move(mojo_descriptor)); | 
| +  if (options.hasRpId()) { | 
| +    // if rpID is missing, it will later be set to the origin of the page | 
| +    // in the secure browser process. | 
| +    mojo_options->relying_party_id = options.rpId(); | 
| +  } | 
| + | 
| +  // Step 4 of https://w3c.github.io/webauthn/#createCredential | 
| +  int predicted_timeout = kAdjustedTimeoutLowerInSeconds; | 
| +  if (options.hasTimeoutSeconds()) { | 
| +    predicted_timeout = static_cast<int>(options.timeoutSeconds()); | 
| +  } | 
| + | 
| +  mojo_options->adjusted_timeout = static_cast<double>( | 
| +      std::max(kAdjustedTimeoutLowerInSeconds, | 
| +               std::min(kAdjustedTimeoutUpperInSeconds, predicted_timeout))); | 
| + | 
| +  if (options.hasExcludeList()) { | 
| +    // Adds the excludeList members (which are ScopedCredentialDescriptors) | 
| +    for (const auto& descriptor : options.excludeList()) { | 
| +      auto mojo_descriptor = ScopedCredentialDescriptor::New(); | 
| +      mojo_descriptor->type = ConvertScopedCredentialType(descriptor.type()); | 
| +      mojo_descriptor->id = ConvertBufferSource(descriptor.id()); | 
| +      for (const auto& transport : descriptor.transports()) | 
| +        mojo_descriptor->transports.push_back(ConvertTransport(transport)); | 
| +      mojo_options->exclude_list.push_back(std::move(mojo_descriptor)); | 
| +    } | 
| } | 
| // TODO(kpaulhamus): add AuthenticationExtensions; | 
| return mojo_options; | 
| @@ -113,18 +132,40 @@ ScopedCredentialParametersPtr ConvertScopedCredentialParameter( | 
| // TODO(kpaulhamus): add AlgorithmIdentifier | 
| return mojo_parameter; | 
| } | 
| + | 
| +blink::DOMException* CreateExceptionFromStatus(AuthenticatorStatus status) { | 
| +  switch (status) { | 
| +    case AuthenticatorStatus::NOT_IMPLEMENTED: | 
| +      return blink::DOMException::Create(blink::kNotSupportedError, | 
| +                                         "Not implemented."); | 
| +    case AuthenticatorStatus::NOT_ALLOWED_ERROR: | 
| +      return blink::DOMException::Create(blink::kNotAllowedError, | 
| +                                         "Not allowed."); | 
| +    case AuthenticatorStatus::NOT_SUPPORTED_ERROR: | 
| +      return blink::DOMException::Create( | 
| +          blink::kNotSupportedError, | 
| +          "Parameters for this operation are not supported."); | 
| +    case AuthenticatorStatus::SECURITY_ERROR: | 
| +      return blink::DOMException::Create(blink::kSecurityError, | 
| +                                         "The operation was not allowed."); | 
| +    case AuthenticatorStatus::UNKNOWN_ERROR: | 
| +      return blink::DOMException::Create(blink::kUnknownError, | 
| +                                         "Request failed."); | 
| +    case AuthenticatorStatus::CANCELLED: | 
| +      return blink::DOMException::Create(blink::kNotAllowedError, | 
| +                                         "User canceled the operation."); | 
| +    case AuthenticatorStatus::SUCCESS: | 
| +      return nullptr; | 
| +    default: | 
| +      NOTREACHED(); | 
| +      return nullptr; | 
| +  } | 
| +} | 
| }  // namespace mojo | 
|  | 
| namespace blink { | 
| - | 
| WebAuthentication::WebAuthentication(LocalFrame& frame) | 
| -    : ContextLifecycleObserver(frame.GetDocument()) { | 
| -  frame.GetInterfaceProvider()->GetInterface( | 
| -      mojo::MakeRequest(&authenticator_)); | 
| -  authenticator_.set_connection_error_handler(ConvertToBaseCallback( | 
| -      WTF::Bind(&WebAuthentication::OnAuthenticatorConnectionError, | 
| -                WrapWeakPersistent(this)))); | 
| -} | 
| +    : ContextLifecycleObserver(frame.GetDocument()) {} | 
|  | 
| WebAuthentication::~WebAuthentication() { | 
| // |authenticator_| may still be valid but there should be no more | 
| @@ -132,40 +173,36 @@ WebAuthentication::~WebAuthentication() { | 
| DCHECK(authenticator_requests_.IsEmpty()); | 
| } | 
|  | 
| -void WebAuthentication::Dispose() {} | 
| - | 
| ScriptPromise WebAuthentication::makeCredential( | 
| ScriptState* script_state, | 
| const RelyingPartyAccount& account_information, | 
| const HeapVector<ScopedCredentialParameters> crypto_parameters, | 
| const BufferSource& attestation_challenge, | 
| ScopedCredentialOptions& options) { | 
| -  if (!authenticator_) { | 
| -    return ScriptPromise::RejectWithDOMException( | 
| -        script_state, DOMException::Create(kNotSupportedError)); | 
| -  } | 
| +  ScriptPromise promise = RejectIfNotSupported(script_state); | 
| +  if (!promise.IsEmpty()) | 
| +    return promise; | 
|  | 
| ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state); | 
| -  ScriptPromise promise = resolver->Promise(); | 
|  | 
| -  // TODO(kpaulhamus) validate parameters according to spec | 
| -  auto account = | 
| -      mojo::ConvertRelyingPartyAccount(account_information, resolver); | 
| Vector<uint8_t> buffer = mojo::ConvertBufferSource(attestation_challenge); | 
| auto opts = mojo::ConvertScopedCredentialOptions(options, resolver); | 
| Vector<webauth::mojom::blink::ScopedCredentialParametersPtr> parameters; | 
| for (const auto& parameter : crypto_parameters) { | 
| -    parameters.push_back( | 
| -        mojo::ConvertScopedCredentialParameter(parameter, resolver)); | 
| +    if (parameter.hasType()) { | 
| +      parameters.push_back( | 
| +          mojo::ConvertScopedCredentialParameter(parameter, resolver)); | 
| +    } | 
| } | 
| - | 
| +  auto account = | 
| +      mojo::ConvertRelyingPartyAccount(account_information, resolver); | 
| authenticator_requests_.insert(resolver); | 
| authenticator_->MakeCredential( | 
| std::move(account), std::move(parameters), buffer, std::move(opts), | 
| -      ConvertToBaseCallback(Bind(&WebAuthentication::OnMakeCredential, | 
| -                                 WrapPersistent(this), | 
| -                                 WrapPersistent(resolver)))); | 
| -  return promise; | 
| +      ConvertToBaseCallback(WTF::Bind(&WebAuthentication::OnMakeCredential, | 
| +                                      WrapPersistent(this), | 
| +                                      WrapPersistent(resolver)))); | 
| +  return resolver->Promise(); | 
| } | 
|  | 
| ScriptPromise WebAuthentication::getAssertion( | 
| @@ -177,44 +214,66 @@ ScriptPromise WebAuthentication::getAssertion( | 
| } | 
|  | 
| void WebAuthentication::ContextDestroyed(ExecutionContext*) { | 
| -  authenticator_.reset(); | 
| -  authenticator_requests_.clear(); | 
| -} | 
| - | 
| -void WebAuthentication::OnAuthenticatorConnectionError() { | 
| -  authenticator_.reset(); | 
| -  for (ScriptPromiseResolver* resolver : authenticator_requests_) { | 
| -    resolver->Reject( | 
| -        DOMException::Create(kNotFoundError, kNoAuthenticatorError)); | 
| -  } | 
| -  authenticator_requests_.clear(); | 
| +  Cleanup(); | 
| } | 
|  | 
| void WebAuthentication::OnMakeCredential( | 
| ScriptPromiseResolver* resolver, | 
| -    Vector<webauth::mojom::blink::ScopedCredentialInfoPtr> credentials) { | 
| +    webauth::mojom::blink::AuthenticatorStatus status, | 
| +    webauth::mojom::blink::ScopedCredentialInfoPtr credential) { | 
| if (!MarkRequestComplete(resolver)) | 
| return; | 
|  | 
| -  HeapVector<Member<ScopedCredentialInfo>> scoped_credentials; | 
| -  for (auto& credential : credentials) { | 
| -    if (credential->client_data.IsEmpty() || | 
| -        credential->attestation.IsEmpty()) { | 
| -      resolver->Reject( | 
| -          DOMException::Create(kNotFoundError, "No credentials returned.")); | 
| +  DOMException* error = mojo::CreateExceptionFromStatus(status); | 
| +  if (error) { | 
| +    DCHECK(!credential); | 
| +    resolver->Reject(error); | 
| +    Cleanup(); | 
| +    return; | 
| +  } | 
| + | 
| +  if (credential->client_data.IsEmpty() || credential->attestation.IsEmpty()) { | 
| +    resolver->Reject( | 
| +        DOMException::Create(kNotFoundError, "No credential returned.")); | 
| +    return; | 
| +  } | 
| + | 
| +  DOMArrayBuffer* clientDataBuffer = DOMArrayBuffer::Create( | 
| +      static_cast<void*>(&credential->client_data.front()), | 
| +      credential->client_data.size()); | 
| + | 
| +  DOMArrayBuffer* attestationBuffer = DOMArrayBuffer::Create( | 
| +      static_cast<void*>(&credential->attestation.front()), | 
| +      credential->attestation.size()); | 
| + | 
| +  ScopedCredentialInfo* scopedCredential = | 
| +      ScopedCredentialInfo::Create(clientDataBuffer, attestationBuffer); | 
| +  resolver->Resolve(scopedCredential); | 
| +} | 
| + | 
| +ScriptPromise WebAuthentication::RejectIfNotSupported( | 
| +    ScriptState* script_state) { | 
| +  if (!authenticator_) { | 
| +    if (!GetFrame()) { | 
| +      return ScriptPromise::RejectWithDOMException( | 
| +          script_state, DOMException::Create(kNotSupportedError)); | 
| } | 
| -    DOMArrayBuffer* client_data_buffer = DOMArrayBuffer::Create( | 
| -        static_cast<void*>(&credential->client_data.front()), | 
| -        credential->client_data.size()); | 
| +    GetFrame()->GetInterfaceProvider()->GetInterface( | 
| +        mojo::MakeRequest(&authenticator_)); | 
|  | 
| -    DOMArrayBuffer* attestation_buffer = DOMArrayBuffer::Create( | 
| -        static_cast<void*>(&credential->attestation.front()), | 
| -        credential->attestation.size()); | 
| +    authenticator_.set_connection_error_handler(ConvertToBaseCallback( | 
| +        WTF::Bind(&WebAuthentication::OnAuthenticatorConnectionError, | 
| +                  WrapWeakPersistent(this)))); | 
| +  } | 
| +  return ScriptPromise(); | 
| +} | 
|  | 
| -    scoped_credentials.push_back( | 
| -        ScopedCredentialInfo::Create(client_data_buffer, attestation_buffer)); | 
| +void WebAuthentication::OnAuthenticatorConnectionError() { | 
| +  for (ScriptPromiseResolver* resolver : authenticator_requests_) { | 
| +    resolver->Reject( | 
| +        DOMException::Create(kNotFoundError, kNoAuthenticatorError)); | 
| } | 
| -  resolver->Resolve(); | 
| +  Cleanup(); | 
| } | 
|  | 
| bool WebAuthentication::MarkRequestComplete(ScriptPromiseResolver* resolver) { | 
| @@ -230,4 +289,10 @@ DEFINE_TRACE(WebAuthentication) { | 
| ContextLifecycleObserver::Trace(visitor); | 
| } | 
|  | 
| +// Clears the promise resolver, timer, and closes the Mojo connection. | 
| +void WebAuthentication::Cleanup() { | 
| +  authenticator_.reset(); | 
| +  authenticator_requests_.clear(); | 
| +} | 
| + | 
| }  // namespace blink | 
|  |