|  | // Copyright 2014 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. | 
|  |  | 
|  | #include "modules/credentialmanager/CredentialsContainer.h" | 
|  |  | 
|  | #include "bindings/core/v8/Dictionary.h" | 
|  | #include "bindings/core/v8/ScriptPromise.h" | 
|  | #include "bindings/core/v8/ScriptPromiseResolver.h" | 
|  | #include "core/dom/DOMException.h" | 
|  | #include "core/dom/Document.h" | 
|  | #include "core/dom/ExceptionCode.h" | 
|  | #include "core/dom/ExecutionContext.h" | 
|  | #include "core/frame/Frame.h" | 
|  | #include "core/frame/UseCounter.h" | 
|  | #include "core/page/FrameTree.h" | 
|  | #include "modules/credentialmanager/Credential.h" | 
|  | #include "modules/credentialmanager/CredentialManagerClient.h" | 
|  | #include "modules/credentialmanager/CredentialRequestOptions.h" | 
|  | #include "modules/credentialmanager/FederatedCredential.h" | 
|  | #include "modules/credentialmanager/FederatedCredentialRequestOptions.h" | 
|  | #include "modules/credentialmanager/PasswordCredential.h" | 
|  | #include "platform/weborigin/SecurityOrigin.h" | 
|  | #include "public/platform/Platform.h" | 
|  | #include "public/platform/WebCredential.h" | 
|  | #include "public/platform/WebCredentialManagerClient.h" | 
|  | #include "public/platform/WebCredentialManagerError.h" | 
|  | #include "public/platform/WebFederatedCredential.h" | 
|  | #include "public/platform/WebPasswordCredential.h" | 
|  |  | 
|  | namespace blink { | 
|  |  | 
|  | static void rejectDueToCredentialManagerError(ScriptPromiseResolver* resolver, WebCredentialManagerError reason) | 
|  | { | 
|  | switch (reason) { | 
|  | case WebCredentialManagerDisabledError: | 
|  | resolver->reject(DOMException::create(InvalidStateError, "The credential manager is disabled.")); | 
|  | break; | 
|  | case WebCredentialManagerPendingRequestError: | 
|  | resolver->reject(DOMException::create(InvalidStateError, "A 'get()' request is pending.")); | 
|  | break; | 
|  | case WebCredentialManagerUnknownError: | 
|  | default: | 
|  | resolver->reject(DOMException::create(NotReadableError, "An unknown error occurred while talking to the credential manager.")); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | class NotificationCallbacks : public WebCredentialManagerClient::NotificationCallbacks { | 
|  | WTF_MAKE_NONCOPYABLE(NotificationCallbacks); | 
|  | public: | 
|  | explicit NotificationCallbacks(ScriptPromiseResolver* resolver) : m_resolver(resolver) { } | 
|  | ~NotificationCallbacks() override { } | 
|  |  | 
|  | void onSuccess() override | 
|  | { | 
|  | Frame* frame = toDocument(m_resolver->getScriptState()->getExecutionContext())->frame(); | 
|  | SECURITY_CHECK(!frame || frame == frame->tree().top()); | 
|  |  | 
|  | m_resolver->resolve(); | 
|  | } | 
|  |  | 
|  | void onError(WebCredentialManagerError reason) override | 
|  | { | 
|  | rejectDueToCredentialManagerError(m_resolver, reason); | 
|  | } | 
|  |  | 
|  | private: | 
|  | const Persistent<ScriptPromiseResolver> m_resolver; | 
|  | }; | 
|  |  | 
|  | class RequestCallbacks : public WebCredentialManagerClient::RequestCallbacks { | 
|  | WTF_MAKE_NONCOPYABLE(RequestCallbacks); | 
|  | public: | 
|  | explicit RequestCallbacks(ScriptPromiseResolver* resolver) : m_resolver(resolver) { } | 
|  | ~RequestCallbacks() override { } | 
|  |  | 
|  | void onSuccess(std::unique_ptr<WebCredential> webCredential) override | 
|  | { | 
|  | Frame* frame = toDocument(m_resolver->getScriptState()->getExecutionContext())->frame(); | 
|  | SECURITY_CHECK(!frame || frame == frame->tree().top()); | 
|  |  | 
|  | OwnPtr<WebCredential> credential = adoptPtr(webCredential.release()); | 
|  | if (!credential || !frame) { | 
|  | m_resolver->resolve(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | ASSERT(credential->isPasswordCredential() || credential->isFederatedCredential()); | 
|  | UseCounter::count(m_resolver->getScriptState()->getExecutionContext(), UseCounter::CredentialManagerGetReturnedCredential); | 
|  | if (credential->isPasswordCredential()) | 
|  | m_resolver->resolve(PasswordCredential::create(static_cast<WebPasswordCredential*>(credential.get()))); | 
|  | else | 
|  | m_resolver->resolve(FederatedCredential::create(static_cast<WebFederatedCredential*>(credential.get()))); | 
|  | } | 
|  |  | 
|  | void onError(WebCredentialManagerError reason) override | 
|  | { | 
|  | rejectDueToCredentialManagerError(m_resolver, reason); | 
|  | } | 
|  |  | 
|  | private: | 
|  | const Persistent<ScriptPromiseResolver> m_resolver; | 
|  | }; | 
|  |  | 
|  |  | 
|  | CredentialsContainer* CredentialsContainer::create() | 
|  | { | 
|  | return new CredentialsContainer(); | 
|  | } | 
|  |  | 
|  | CredentialsContainer::CredentialsContainer() | 
|  | { | 
|  | } | 
|  |  | 
|  | static bool checkBoilerplate(ScriptPromiseResolver* resolver) | 
|  | { | 
|  | Frame* frame = toDocument(resolver->getScriptState()->getExecutionContext())->frame(); | 
|  | if (!frame || frame != frame->tree().top()) { | 
|  | resolver->reject(DOMException::create(SecurityError, "CredentialContainer methods may only be executed in a top-level document.")); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | String errorMessage; | 
|  | if (!resolver->getScriptState()->getExecutionContext()->isSecureContext(errorMessage)) { | 
|  | resolver->reject(DOMException::create(SecurityError, errorMessage)); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | CredentialManagerClient* client = CredentialManagerClient::from(resolver->getScriptState()->getExecutionContext()); | 
|  | if (!client) { | 
|  | resolver->reject(DOMException::create(InvalidStateError, "Could not establish connection to the credential manager.")); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | ScriptPromise CredentialsContainer::get(ScriptState* scriptState, const CredentialRequestOptions& options) | 
|  | { | 
|  | ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); | 
|  | ScriptPromise promise = resolver->promise(); | 
|  | if (!checkBoilerplate(resolver)) | 
|  | return promise; | 
|  |  | 
|  | Vector<KURL> providers; | 
|  | if (options.hasFederated() && options.federated().hasProviders()) { | 
|  | // TODO(mkwst): CredentialRequestOptions::federated() needs to return a reference, not a value. | 
|  | // Because it returns a temporary value now, a for loop that directly references the value | 
|  | // generates code that holds a reference to a value that no longer exists by the time the loop | 
|  | // starts looping. In order to avoid this crazyness for the moment, we're making a copy of the | 
|  | // vector. https://crbug.com/587088 | 
|  | const Vector<String> providerStrings = options.federated().providers(); | 
|  | for (const auto& string : providerStrings) { | 
|  | KURL url = KURL(KURL(), string); | 
|  | if (url.isValid()) | 
|  | providers.append(url); | 
|  | } | 
|  | } | 
|  |  | 
|  | UseCounter::count(scriptState->getExecutionContext(), options.unmediated() ? UseCounter::CredentialManagerGetWithoutUI : UseCounter::CredentialManagerGetWithUI); | 
|  |  | 
|  | CredentialManagerClient::from(scriptState->getExecutionContext())->dispatchGet(options.unmediated(), options.password(), providers, new RequestCallbacks(resolver)); | 
|  | return promise; | 
|  | } | 
|  |  | 
|  | ScriptPromise CredentialsContainer::store(ScriptState* scriptState, Credential* credential) | 
|  | { | 
|  | ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); | 
|  | ScriptPromise promise = resolver->promise(); | 
|  | if (!checkBoilerplate(resolver)) | 
|  | return promise; | 
|  |  | 
|  | auto webCredential = WebCredential::create(credential->getPlatformCredential()); | 
|  | CredentialManagerClient::from(scriptState->getExecutionContext())->dispatchStore(*webCredential, new NotificationCallbacks(resolver)); | 
|  | return promise; | 
|  | } | 
|  |  | 
|  | ScriptPromise CredentialsContainer::requireUserMediation(ScriptState* scriptState) | 
|  | { | 
|  | ScriptPromiseResolver* resolver = ScriptPromiseResolver::create(scriptState); | 
|  | ScriptPromise promise = resolver->promise(); | 
|  | if (!checkBoilerplate(resolver)) | 
|  | return promise; | 
|  |  | 
|  | CredentialManagerClient::from(scriptState->getExecutionContext())->dispatchRequireUserMediation(new NotificationCallbacks(resolver)); | 
|  | return promise; | 
|  | } | 
|  |  | 
|  | } // namespace blink |