summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorvabr <vabr@chromium.org>2015-11-19 09:42:48 -0800
committerCommit bot <commit-bot@chromium.org>2015-11-19 17:43:32 +0000
commitb0a8964d59e03ab82709c85dd4f201cd7d44d3ba (patch)
treedf0a7076bce54b21b1de221001fc5cbef686c15a
parentc41ed1abe28b265bf0bd776876127ed999f7f772 (diff)
downloadchromium_src-b0a8964d59e03ab82709c85dd4f201cd7d44d3ba.zip
chromium_src-b0a8964d59e03ab82709c85dd4f201cd7d44d3ba.tar.gz
chromium_src-b0a8964d59e03ab82709c85dd4f201cd7d44d3ba.tar.bz2
Move JS-related password manager code upstream
Moves credential_manager.h credential_manager.mm js_credential_manager.h js_credential_manager.mm js_password_manager.h js_password_manager.mm resources/credential_manager.js resources/password_controller.js from the downstream to ios/chrome/browser/passwords. This is the upstream part of the internal CL 310987013. BUG=514241 Review URL: https://codereview.chromium.org/1456983002 Cr-Commit-Position: refs/heads/master@{#360617}
-rw-r--r--ios/chrome/browser/DEPS1
-rw-r--r--ios/chrome/browser/passwords/credential_manager.h131
-rw-r--r--ios/chrome/browser/passwords/credential_manager.mm360
-rw-r--r--ios/chrome/browser/passwords/js_credential_manager.h50
-rw-r--r--ios/chrome/browser/passwords/js_credential_manager.mm103
-rw-r--r--ios/chrome/browser/passwords/js_password_manager.h65
-rw-r--r--ios/chrome/browser/passwords/js_password_manager.mm107
-rw-r--r--ios/chrome/browser/passwords/resources/credential_manager.js574
-rw-r--r--ios/chrome/browser/passwords/resources/password_controller.js418
-rw-r--r--ios/chrome/ios_chrome.gyp10
-rw-r--r--ios/web/BUILD.gn2
-rw-r--r--ios/web/ios_web.gyp12
-rw-r--r--ios/web/public/web_state/js/credential_util.h (renamed from ios/web/web_state/js/credential_util.h)6
-rw-r--r--ios/web/web_state/js/credential_util.mm2
-rw-r--r--ios/web/web_state/js/credential_util_unittest.mm2
-rw-r--r--ios/web/web_state/ui/crw_web_controller.mm2
16 files changed, 1832 insertions, 13 deletions
diff --git a/ios/chrome/browser/DEPS b/ios/chrome/browser/DEPS
index 8f4750b..fbacbaa 100644
--- a/ios/chrome/browser/DEPS
+++ b/ios/chrome/browser/DEPS
@@ -34,6 +34,7 @@ include_rules = [
"+components/omnibox/browser",
"+components/open_from_clipboard",
"+components/password_manager/core/browser",
+ "+components/password_manager/core/common",
"+components/password_manager/sync/browser",
"+components/pref_registry",
"+components/profile_metrics",
diff --git a/ios/chrome/browser/passwords/credential_manager.h b/ios/chrome/browser/passwords/credential_manager.h
new file mode 100644
index 0000000..9a0efce
--- /dev/null
+++ b/ios/chrome/browser/passwords/credential_manager.h
@@ -0,0 +1,131 @@
+// Copyright 2015 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.
+
+#ifndef IOS_CHROME_BROWSER_PASSWORDS_CREDENTIAL_MANAGER_H_
+#define IOS_CHROME_BROWSER_PASSWORDS_CREDENTIAL_MANAGER_H_
+
+#include <string>
+#include <vector>
+
+#import "base/mac/scoped_nsobject.h"
+#include "base/macros.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "base/prefs/pref_member.h"
+#include "components/password_manager/core/browser/credential_manager_password_form_manager.h"
+#include "components/password_manager/core/browser/credential_manager_pending_request_task.h"
+#include "components/password_manager/core/browser/credential_manager_pending_require_user_mediation_task.h"
+#include "components/password_manager/core/browser/password_manager_client.h"
+#include "components/password_manager/core/browser/password_store.h"
+#include "ios/web/public/web_state/web_state_observer.h"
+
+@class JSCredentialManager;
+
+// Implements the app-side of the CredentialManagement JavaScript API.
+// Injects and listens to the injected JavaScript, owns and drives the user
+// interface, and integrates with the password manager. This is the iOS
+// equivalent of the upstream class CredentialManagerDispatcher. Note: Only
+// activates on iOS 8 and later.
+class CredentialManager
+ : public password_manager::CredentialManagerPasswordFormManagerDelegate,
+ public password_manager::CredentialManagerPendingRequestTaskDelegate,
+ public password_manager::
+ CredentialManagerPendingRequireUserMediationTaskDelegate,
+ public web::WebStateObserver {
+ public:
+ CredentialManager(web::WebState* web_state,
+ password_manager::PasswordManagerClient* client,
+ password_manager::PasswordManagerDriver* driver,
+ JSCredentialManager* js_manager);
+ ~CredentialManager() override;
+
+ // web::WebStateObserver:
+ void PageLoaded(
+ web::PageLoadCompletionStatus load_completion_status) override;
+ void CredentialsRequested(int request_id,
+ const GURL& source_url,
+ bool zero_click_only,
+ const std::vector<std::string>& federations,
+ bool is_user_initiated) override;
+ void SignedIn(int request_id,
+ const GURL& source_url,
+ const web::Credential& credential) override;
+ void SignedOut(int request_id, const GURL& source_url) override;
+ void WebStateDestroyed() override;
+
+ // password_manager::CredentialManagerPendingRequestTaskDelegate:
+ bool IsZeroClickAllowed() const override;
+ GURL GetOrigin() const override;
+ void SendCredential(
+ int id,
+ const password_manager::CredentialInfo& credential) override;
+ password_manager::PasswordManagerClient* client() const override;
+ autofill::PasswordForm GetSynthesizedFormForOrigin() const override;
+
+ // password_manager::CredentialManagerPendingRequireUserMediationTaskDelegate:
+ password_manager::PasswordStore* GetPasswordStore() override;
+ void DoneRequiringUserMediation() override;
+
+ // CredentialManagerPasswordFormManagerDelegate:
+ void OnProvisionalSaveComplete() override;
+
+ private:
+ // The errors that can cause a request to fail.
+ enum ErrorType {
+ // An existing request is outstanding.
+ ERROR_TYPE_PENDING_REQUEST = 0,
+
+ // The password store isn't available.
+ ERROR_TYPE_PASSWORD_STORE_UNAVAILABLE,
+
+ // The page origin is untrusted.
+ ERROR_TYPE_SECURITY_ERROR_UNTRUSTED_ORIGIN,
+ };
+
+ // Sends a message via |js_manager_| to resolve the JavaScript Promise
+ // associated with |request_id|. Invoked after a page-initiated credential
+ // event is acknowledged by the PasswordStore.
+ void ResolvePromise(int request_id);
+
+ // Sends a message via |js_manager_| to reject the JavaScript Promise
+ // associated with |request_id_| with the given |error_type|. Invoked after a
+ // page-initiated credential event, store, or retrieval fails.
+ void RejectPromise(int request_id, ErrorType error_type);
+
+ // Determines the currently loaded page's URL from the active WebState, but
+ // only if it is absolutely trusted. Does not hit the network, but still might
+ // be costly depending on the webview. Returns true if successful.
+ bool GetUrlWithAbsoluteTrust(GURL* page_url);
+
+ // The request to retrieve credentials from the PasswordStore.
+ scoped_ptr<password_manager::CredentialManagerPendingRequestTask>
+ pending_request_;
+
+ // The task to notify the password manager that the user was signed out.
+ scoped_ptr<password_manager::CredentialManagerPendingRequireUserMediationTask>
+ pending_require_user_mediation_;
+
+ // Saves credentials to the PasswordStore.
+ scoped_ptr<password_manager::CredentialManagerPasswordFormManager>
+ form_manager_;
+
+ // Injected JavaScript to provide the API to web pages.
+ base::scoped_nsobject<JSCredentialManager> js_manager_;
+
+ // Client to access Chrome-specific password manager functionality. Weak.
+ password_manager::PasswordManagerClient* client_;
+
+ // Driver to access embedder-specific password manager functionality. Weak.
+ password_manager::PasswordManagerDriver* driver_;
+
+ // Whether zero-click sign-in is enabled.
+ BooleanPrefMember zero_click_sign_in_enabled_;
+
+ // Weak pointer factory for asynchronously resolving requests.
+ base::WeakPtrFactory<CredentialManager> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(CredentialManager);
+};
+
+#endif // IOS_CHROME_BROWSER_PASSWORDS_CREDENTIAL_MANAGER_H_
diff --git a/ios/chrome/browser/passwords/credential_manager.mm b/ios/chrome/browser/passwords/credential_manager.mm
new file mode 100644
index 0000000..405e22f
--- /dev/null
+++ b/ios/chrome/browser/passwords/credential_manager.mm
@@ -0,0 +1,360 @@
+// Copyright 2015 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.
+
+#import "ios/chrome/browser/passwords/credential_manager.h"
+
+#include "base/ios/ios_util.h"
+#import "base/ios/weak_nsobject.h"
+#include "base/mac/bind_objc_block.h"
+#include "base/memory/scoped_vector.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/sys_string_conversions.h"
+#include "components/password_manager/core/browser/password_store_consumer.h"
+#include "components/password_manager/core/common/credential_manager_types.h"
+#include "components/password_manager/core/common/password_manager_pref_names.h"
+#import "ios/chrome/browser/passwords/js_credential_manager.h"
+#import "ios/web/public/url_scheme_util.h"
+#include "ios/web/public/web_state/credential.h"
+#include "ios/web/public/web_state/url_verification_constants.h"
+#include "ios/web/public/web_state/web_state.h"
+
+namespace {
+
+// Converts a password_manager::CredentialInfo to a web::Credential.
+web::Credential WebCredentialFromCredentialInfo(
+ const password_manager::CredentialInfo& credential_info) {
+ web::Credential credential;
+ switch (credential_info.type) {
+ case password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY:
+ credential.type = web::CredentialType::CREDENTIAL_TYPE_EMPTY;
+ break;
+ case password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD:
+ credential.type = web::CredentialType::CREDENTIAL_TYPE_PASSWORD;
+ break;
+ case password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED:
+ credential.type = web::CredentialType::CREDENTIAL_TYPE_FEDERATED;
+ break;
+ }
+ credential.id = credential_info.id;
+ credential.name = credential_info.name;
+ credential.avatar_url = credential_info.icon;
+ credential.password = credential_info.password;
+ credential.federation_url = credential_info.federation;
+ return credential;
+}
+
+// Converts a web::Credential to a password_manager::CredentialInfo.
+password_manager::CredentialInfo CredentialInfoFromWebCredential(
+ const web::Credential& credential) {
+ password_manager::CredentialInfo credential_info;
+ switch (credential.type) {
+ case web::CredentialType::CREDENTIAL_TYPE_EMPTY:
+ credential_info.type =
+ password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY;
+ break;
+ case web::CredentialType::CREDENTIAL_TYPE_PASSWORD:
+ credential_info.type =
+ password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD;
+ break;
+ case web::CredentialType::CREDENTIAL_TYPE_FEDERATED:
+ credential_info.type =
+ password_manager::CredentialType::CREDENTIAL_TYPE_FEDERATED;
+ break;
+ }
+ credential_info.id = credential.id;
+ credential_info.name = credential.name;
+ credential_info.icon = credential.avatar_url;
+ credential_info.password = credential.password;
+ credential_info.federation = credential.federation_url;
+ return credential_info;
+}
+
+} // namespace
+
+CredentialManager::CredentialManager(
+ web::WebState* web_state,
+ password_manager::PasswordManagerClient* client,
+ password_manager::PasswordManagerDriver* driver,
+ JSCredentialManager* js_manager)
+ : web::WebStateObserver(web_state),
+ pending_request_(nullptr),
+ form_manager_(nullptr),
+ js_manager_([js_manager retain]),
+ client_(client),
+ driver_(driver),
+ weak_factory_(this) {
+ zero_click_sign_in_enabled_.Init(
+ password_manager::prefs::kPasswordManagerAutoSignin, client_->GetPrefs());
+}
+
+CredentialManager::~CredentialManager() = default;
+
+void CredentialManager::PageLoaded(
+ web::PageLoadCompletionStatus load_completion_status) {
+ // Ensure the JavaScript is loaded when the page finishes loading.
+ web::URLVerificationTrustLevel trust_level =
+ web::URLVerificationTrustLevel::kNone;
+ const GURL page_url(web_state()->GetCurrentURL(&trust_level));
+ if (!base::ios::IsRunningOnIOS8OrLater() ||
+ trust_level != web::URLVerificationTrustLevel::kAbsolute ||
+ !web::UrlHasWebScheme(page_url) || !web_state()->ContentIsHTML()) {
+ return;
+ }
+ [js_manager_ inject];
+}
+
+void CredentialManager::CredentialsRequested(
+ int request_id,
+ const GURL& source_url,
+ bool zero_click_only,
+ const std::vector<std::string>& federations,
+ bool is_user_initiated) {
+ // Invoked when the page invokes navigator.credentials.request(), this
+ // function will attempt to retrieve a Credential from the PasswordStore that
+ // meets the specified parameters and, if successful, send it back to the page
+ // via SendCredential.
+ DCHECK_GE(request_id, 0);
+ password_manager::PasswordStore* store = GetPasswordStore();
+
+ // If there's an outstanding request, or the PasswordStore isn't loaded yet,
+ // the request should fail outright and the JS Promise should be rejected
+ // with an appropriate error.
+ if (pending_request_ || !store) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&CredentialManager::RejectPromise,
+ weak_factory_.GetWeakPtr(), request_id,
+ pending_request_ ? ERROR_TYPE_PENDING_REQUEST
+ : ERROR_TYPE_PASSWORD_STORE_UNAVAILABLE));
+ return;
+ }
+
+ // If the page requested a zero-click credential -- one that can be returned
+ // without first asking the user -- and if zero-click isn't currently
+ // available, send back an empty credential.
+ if (zero_click_only && !IsZeroClickAllowed()) {
+ base::MessageLoop::current()->PostTask(
+ FROM_HERE, base::Bind(&CredentialManager::SendCredential,
+ weak_factory_.GetWeakPtr(), request_id,
+ password_manager::CredentialInfo()));
+ return;
+ }
+
+ // If the page origin is untrusted, the request should be rejected.
+ GURL page_url;
+ if (!GetUrlWithAbsoluteTrust(&page_url)) {
+ RejectPromise(request_id, ERROR_TYPE_SECURITY_ERROR_UNTRUSTED_ORIGIN);
+ return;
+ }
+
+ // Bundle up the arguments and forward them to the PasswordStore, which will
+ // asynchronously return the resulting Credential by invoking
+ // |SendCredential|.
+ std::vector<GURL> federation_urls;
+ for (const auto& federation : federations)
+ federation_urls.push_back(GURL(federation));
+ std::vector<std::string> realms;
+ pending_request_.reset(
+ new password_manager::CredentialManagerPendingRequestTask(
+ this, request_id, zero_click_only, page_url, federation_urls,
+ realms));
+ store->GetAutofillableLogins(pending_request_.get());
+}
+
+void CredentialManager::SignedIn(int request_id,
+ const GURL& source_url,
+ const web::Credential& credential) {
+ // Invoked when the page invokes navigator.credentials.notifySignedIn(), this
+ // function stores the signed-in |credential| and sends a message back to the
+ // page to resolve the Promise associated with |request_id|.
+ DCHECK(credential.type != web::CredentialType::CREDENTIAL_TYPE_EMPTY);
+ DCHECK_GE(request_id, 0);
+
+ // Requests from untrusted origins should be rejected.
+ GURL page_url;
+ if (!GetUrlWithAbsoluteTrust(&page_url)) {
+ RejectPromise(request_id, ERROR_TYPE_SECURITY_ERROR_UNTRUSTED_ORIGIN);
+ return;
+ }
+
+ // Notify the page that the notification was successful to avoid blocking the
+ // page while storing the credential. This is okay because the notification
+ // doesn't imply that the credential will be stored, just that it might be.
+ // It isn't the page's concern to know whether the storage took place or not.
+ ResolvePromise(request_id);
+
+ // Do nothing if the password manager isn't active.
+ if (!client_->IsSavingAndFillingEnabledForCurrentPage())
+ return;
+
+ // Store the signed-in credential so that the user can save it, if desired.
+ // Prompting the user and saving are handled by the PasswordFormManager.
+ scoped_ptr<autofill::PasswordForm> form(
+ password_manager::CreatePasswordFormFromCredentialInfo(
+ CredentialInfoFromWebCredential(credential), page_url));
+ form->skip_zero_click = !IsZeroClickAllowed();
+
+ // TODO(mkwst): This is a stub; we should be checking the PasswordStore to
+ // determine whether or not the credential exists, and calling UpdateLogin
+ // accordingly.
+ form_manager_.reset(
+ new password_manager::CredentialManagerPasswordFormManager(
+ client_, driver_->AsWeakPtr(), *form, this));
+}
+
+void CredentialManager::SignedOut(int request_id, const GURL& source_url) {
+ // Invoked when the page invokes navigator.credentials.notifySignedOut, this
+ // function notifies the PasswordStore that zero-click sign-in should be
+ // disabled for the current page origin.
+ DCHECK_GE(request_id, 0);
+
+ // Requests from untrusted origins should be rejected.
+ GURL page_url;
+ if (!GetUrlWithAbsoluteTrust(&page_url)) {
+ RejectPromise(request_id, ERROR_TYPE_SECURITY_ERROR_UNTRUSTED_ORIGIN);
+ return;
+ }
+
+ // The user signed out of the current page, so future zero-click credential
+ // requests for this page should fail: otherwise, the next time the user
+ // visits the page, if zero-click requests succeeded, the user might be auto-
+ // signed-in again with the credential that they just signed out. Forward this
+ // information to the PasswordStore via an asynchronous task.
+ password_manager::PasswordStore* store = GetPasswordStore();
+ if (store) {
+ // Bundle the origins that are sent to the PasswordStore if the task hasn't
+ // yet resolved. This task lives across page-loads to enable this bundling.
+ if (pending_require_user_mediation_) {
+ pending_require_user_mediation_->AddOrigin(page_url);
+ } else {
+ pending_require_user_mediation_.reset(
+ new password_manager::
+ CredentialManagerPendingRequireUserMediationTask(
+ this, page_url, std::vector<std::string>()));
+
+ // This will result in a callback to
+ // CredentialManagerPendingSignedOutTask::OnGetPasswordStoreResults().
+ store->GetAutofillableLogins(pending_require_user_mediation_.get());
+ }
+ }
+
+ // Acknowledge the page's signOut notification without waiting for the
+ // PasswordStore interaction to complete. The implementation of the sign-out
+ // notification isn't the page's concern.
+ ResolvePromise(request_id);
+}
+
+void CredentialManager::WebStateDestroyed() {
+ // When the WebState is destroyed, clean up everything that depends on it.
+ js_manager_.reset();
+}
+
+bool CredentialManager::IsZeroClickAllowed() const {
+ // Zero-click sign-in is only allowed when the user hasn't turned it off and
+ // when the user isn't in incognito mode.
+ return *zero_click_sign_in_enabled_ && !client_->IsOffTheRecord();
+}
+
+GURL CredentialManager::GetOrigin() const {
+ web::URLVerificationTrustLevel trust_level =
+ web::URLVerificationTrustLevel::kNone;
+ const GURL page_url(web_state()->GetCurrentURL(&trust_level));
+ DCHECK_EQ(trust_level, web::URLVerificationTrustLevel::kAbsolute);
+ return page_url;
+}
+
+void CredentialManager::SendCredential(
+ int request_id,
+ const password_manager::CredentialInfo& credential) {
+ // Invoked when the asynchronous interaction with the PasswordStore completes,
+ // this function forwards a |credential| back to the page via |js_manager_| by
+ // resolving the JavaScript Promise associated with |request_id|.
+ base::WeakPtr<CredentialManager> weak_this = weak_factory_.GetWeakPtr();
+ [js_manager_
+ resolvePromiseWithRequestID:request_id
+ credential:WebCredentialFromCredentialInfo(credential)
+ completionHandler:^(BOOL) {
+ if (weak_this)
+ weak_this->pending_request_.reset();
+ }];
+}
+
+password_manager::PasswordManagerClient* CredentialManager::client() const {
+ return client_;
+}
+
+autofill::PasswordForm CredentialManager::GetSynthesizedFormForOrigin() const {
+ autofill::PasswordForm synthetic_form;
+ synthetic_form.origin = web_state()->GetLastCommittedURL().GetOrigin();
+ synthetic_form.signon_realm = synthetic_form.origin.spec();
+ synthetic_form.scheme = autofill::PasswordForm::SCHEME_HTML;
+ synthetic_form.ssl_valid = synthetic_form.origin.SchemeIsCryptographic() &&
+ !client_->DidLastPageLoadEncounterSSLErrors();
+ return synthetic_form;
+}
+
+void CredentialManager::OnProvisionalSaveComplete() {
+ // Invoked after a credential sent up by the page was stored in a FormManager
+ // by |SignedIn|, this function asks the user if the password should be stored
+ // in the password manager.
+ DCHECK(form_manager_);
+ if (client_->IsSavingAndFillingEnabledForCurrentPage() &&
+ !form_manager_->IsBlacklisted()) {
+ client_->PromptUserToSaveOrUpdatePassword(
+ form_manager_.Pass(),
+ password_manager::CredentialSourceType::CREDENTIAL_SOURCE_API, false);
+ }
+}
+
+password_manager::PasswordStore* CredentialManager::GetPasswordStore() {
+ return client_ ? client_->GetPasswordStore() : nullptr;
+}
+
+void CredentialManager::DoneRequiringUserMediation() {
+ // Invoked when the PasswordStore has finished processing the list of origins
+ // that should have zero-click sign-in disabled.
+ DCHECK(pending_require_user_mediation_);
+ pending_require_user_mediation_.reset();
+}
+
+void CredentialManager::ResolvePromise(int request_id) {
+ [js_manager_ resolvePromiseWithRequestID:request_id completionHandler:nil];
+}
+
+void CredentialManager::RejectPromise(int request_id, ErrorType error_type) {
+ NSString* type = nil;
+ NSString* message = nil;
+ switch (error_type) {
+ case ERROR_TYPE_PENDING_REQUEST:
+ type = base::SysUTF8ToNSString(kCredentialsPendingRequestErrorType);
+ message = base::SysUTF8ToNSString(kCredentialsPendingRequestErrorMessage);
+ break;
+ case ERROR_TYPE_PASSWORD_STORE_UNAVAILABLE:
+ type = base::SysUTF8ToNSString(
+ kCredentialsPasswordStoreUnavailableErrorType);
+ message = base::SysUTF8ToNSString(
+ kCredentialsPasswordStoreUnavailableErrorMessage);
+ break;
+ case ERROR_TYPE_SECURITY_ERROR_UNTRUSTED_ORIGIN:
+ type = base::SysUTF8ToNSString(kCredentialsSecurityErrorType);
+ message = base::SysUTF8ToNSString(
+ kCredentialsSecurityErrorMessageUntrustedOrigin);
+ break;
+ }
+ [js_manager_ rejectPromiseWithRequestID:request_id
+ errorType:type
+ message:message
+ completionHandler:nil];
+}
+
+bool CredentialManager::GetUrlWithAbsoluteTrust(GURL* page_url) {
+ web::URLVerificationTrustLevel trust_level =
+ web::URLVerificationTrustLevel::kNone;
+ const GURL possibly_untrusted_url(web_state()->GetCurrentURL(&trust_level));
+ if (trust_level == web::URLVerificationTrustLevel::kAbsolute) {
+ *page_url = possibly_untrusted_url;
+ return true;
+ }
+ return false;
+}
diff --git a/ios/chrome/browser/passwords/js_credential_manager.h b/ios/chrome/browser/passwords/js_credential_manager.h
new file mode 100644
index 0000000..d6d3490
--- /dev/null
+++ b/ios/chrome/browser/passwords/js_credential_manager.h
@@ -0,0 +1,50 @@
+// Copyright 2015 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.
+
+#ifndef IOS_CHROME_BROWSER_PASSWORDS_JS_CREDENTIAL_MANAGER_H_
+#define IOS_CHROME_BROWSER_PASSWORDS_JS_CREDENTIAL_MANAGER_H_
+
+#include "ios/web/public/web_state/credential.h"
+#import "ios/web/public/web_state/js/crw_js_injection_manager.h"
+
+namespace base {
+class DictionaryValue;
+} // namespace base
+
+// Constants for rejecting requests.
+extern const char kCredentialsPendingRequestErrorType[];
+extern const char kCredentialsPendingRequestErrorMessage[];
+extern const char kCredentialsSecurityErrorType[];
+extern const char kCredentialsPasswordStoreUnavailableErrorType[];
+extern const char kCredentialsPasswordStoreUnavailableErrorMessage[];
+extern const char kCredentialsSecurityErrorMessageUntrustedOrigin[];
+
+// Injects the JavaScript that implements the request credentials API and
+// provides an app-side interface for interacting with it.
+@interface JSCredentialManager : CRWJSInjectionManager
+
+// Resolves the JavaScript Promise associated with |requestID| with the
+// specified |credential|. |completionHandler| will be invoked after the
+// operation has completed with YES if successful.
+- (void)resolvePromiseWithRequestID:(NSInteger)requestID
+ credential:(const web::Credential&)credential
+ completionHandler:(void (^)(BOOL))completionHandler;
+
+// Resolves the JavaScript Promise associated with |requestID|.
+// |completionHandler| will be invoked after the operation has completed with
+// YES if successful.
+- (void)resolvePromiseWithRequestID:(NSInteger)requestID
+ completionHandler:(void (^)(BOOL))completionHandler;
+
+// Rejects the JavaScript Promise associated with |requestID| with an Error of
+// the specified |errorType| and |message|. |completionHandler| will be invoked
+// after the operation has completed with YES if successful.
+- (void)rejectPromiseWithRequestID:(NSInteger)requestID
+ errorType:(NSString*)errorType
+ message:(NSString*)message
+ completionHandler:(void (^)(BOOL))completionHandler;
+
+@end
+
+#endif // IOS_CHROME_BROWSER_PASSWORDS_JS_CREDENTIAL_MANAGER_H_
diff --git a/ios/chrome/browser/passwords/js_credential_manager.mm b/ios/chrome/browser/passwords/js_credential_manager.mm
new file mode 100644
index 0000000..abdbd41d
--- /dev/null
+++ b/ios/chrome/browser/passwords/js_credential_manager.mm
@@ -0,0 +1,103 @@
+// Copyright 2015 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.
+
+#import "ios/chrome/browser/passwords/js_credential_manager.h"
+
+#include "base/json/json_writer.h"
+#include "base/json/string_escape.h"
+#include "base/logging.h"
+#include "base/strings/sys_string_conversions.h"
+#include "base/values.h"
+#include "ios/web/public/web_state/js/credential_util.h"
+
+namespace {
+
+// Sanitizes |JSON| and wraps it in quotes so it can be injected safely in
+// JavaScript.
+NSString* JSONEscape(NSString* JSON) {
+ return base::SysUTF8ToNSString(
+ base::GetQuotedJSONString(base::SysNSStringToUTF8(JSON)));
+}
+
+} // namespace
+
+const char kCredentialsPendingRequestErrorType[] = "PendingRequestError";
+const char kCredentialsPendingRequestErrorMessage[] =
+ "There is already an outstanding request";
+const char kCredentialsSecurityErrorType[] = "SecurityError";
+const char kCredentialsPasswordStoreUnavailableErrorType[] =
+ "PasswordStoreUnavailableError";
+const char kCredentialsPasswordStoreUnavailableErrorMessage[] =
+ "The password store is unavailable";
+const char kCredentialsSecurityErrorMessageUntrustedOrigin[] =
+ "The origin is untrusted";
+
+@interface JSCredentialManager ()
+
+// Evaluates the JavaScript in |script|, which should evaluate to a JavaScript
+// boolean value. That value will be passed to |completionHandler|.
+- (void)evaluateScript:(NSString*)script
+ completionHandler:(void (^)(BOOL))completionHandler;
+
+@end
+
+@implementation JSCredentialManager
+
+- (void)resolvePromiseWithRequestID:(NSInteger)requestID
+ credential:(const web::Credential&)credential
+ completionHandler:(void (^)(BOOL))completionHandler {
+ base::DictionaryValue credentialData;
+ web::CredentialToDictionaryValue(credential, &credentialData);
+ std::string credentialDataJSON;
+ base::JSONWriter::Write(credentialData, &credentialDataJSON);
+ NSString* script = [NSString
+ stringWithFormat:@"__gCrWeb['credentialManager'].resolve(%ld, %@)",
+ static_cast<long>(requestID),
+ base::SysUTF8ToNSString(credentialDataJSON)];
+ [self evaluate:script
+ stringResultHandler:^(NSString* result, NSError* error) {
+ if (completionHandler)
+ completionHandler(!error && [result isEqualToString:@"true"]);
+ }];
+}
+
+- (void)resolvePromiseWithRequestID:(NSInteger)requestID
+ completionHandler:(void (^)(BOOL))completionHandler {
+ NSString* script =
+ [NSString stringWithFormat:@"__gCrWeb['credentialManager'].resolve(%ld)",
+ static_cast<long>(requestID)];
+ [self evaluateScript:script completionHandler:completionHandler];
+}
+
+- (void)rejectPromiseWithRequestID:(NSInteger)requestID
+ errorType:(NSString*)errorType
+ message:(NSString*)message
+ completionHandler:(void (^)(BOOL))completionHandler {
+ NSString* script = [NSString
+ stringWithFormat:@"__gCrWeb['credentialManager'].reject(%ld, %@, %@)",
+ static_cast<long>(requestID), JSONEscape(errorType),
+ JSONEscape(message)];
+ [self evaluateScript:script completionHandler:completionHandler];
+}
+
+- (void)evaluateScript:(NSString*)script
+ completionHandler:(void (^)(BOOL))completionHandler {
+ [self evaluate:script
+ stringResultHandler:^(NSString* result, NSError* error) {
+ if (completionHandler)
+ completionHandler(!error && [result isEqualToString:@"true"]);
+ }];
+}
+
+#pragma mark - Protected methods
+
+- (NSString*)scriptPath {
+ return @"credential_manager";
+}
+
+- (NSString*)presenceBeacon {
+ return @"__gCrWeb.credentialManager";
+}
+
+@end
diff --git a/ios/chrome/browser/passwords/js_password_manager.h b/ios/chrome/browser/passwords/js_password_manager.h
new file mode 100644
index 0000000..260ae2e
--- /dev/null
+++ b/ios/chrome/browser/passwords/js_password_manager.h
@@ -0,0 +1,65 @@
+// Copyright 2012 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.
+
+#ifndef IOS_CHROME_BROWSER_PASSWORDS_JS_PASSWORD_MANAGER_H_
+#define IOS_CHROME_BROWSER_PASSWORDS_JS_PASSWORD_MANAGER_H_
+
+#include "base/ios/block_types.h"
+#import "ios/web/public/web_state/js/crw_js_injection_manager.h"
+
+@class CRWJSInjectionReceiver;
+
+// Loads the JavaScript file, password_controller.js, which contains password
+// form parsing and autofill functions. It will be evaluated on a page that
+// is known to have at least one password form (see hasPasswordForms in
+// core.js.) It returns contents of those password forms and also
+// registers functions that are later used to autofill them.
+@interface JsPasswordManager : CRWJSInjectionManager
+
+// Finds any password forms on the web page.
+// |completionHandler| is then called with the JSON string result (which can
+// be a zero-length string if there was an error). |completionHandler| cannot be
+// nil.
+// For example the JSON string for a form with a single password field is:
+// [{"action":null,"method":null,"usernameElement":"","usernameValue":"","
+// passwords":[{"element":"","value":"asd"}]}]
+- (void)findPasswordFormsWithCompletionHandler:
+ (void (^)(NSString*))completionHandler;
+
+// Extracts the password form with the given name from a web page.
+// |completionHandler| is called with the JSON string containing the info about
+// submitted password forms from a web page (it can be zero-length if there was
+// an error). |completionHandler| cannot be nil.
+// For example. the JSON string for a form with a single password field is:
+// {"action":null,"method":null,"usernameElement":"","usernameValue":"",
+// "passwords":[{"element":"","value":"asd"}]}
+- (void)extractForm:(NSString*)formName
+ completionHandler:(void (^)(NSString*))completionHandler;
+
+// Fills in the password form specified by |JSONString| with the given
+// |username| and |password|. Assumes JavaScript has been injected previously
+// by calling |findPasswordFormsWithCompletionHandle| or
+// |extractSubmittedFormWithCompletionHandler|. Calls |completionHandler| with
+// YES if the filling of the password was successful, NO otherwise.
+// |completionHandler| cannot be nil.
+- (void)fillPasswordForm:(NSString*)JSONString
+ withUsername:(NSString*)username
+ password:(NSString*)password
+ completionHandler:(void (^)(BOOL))completionHandler;
+
+// Clears autofilled credentials in the specified form. Invokes
+// |completionHandler| with YES if successful and NO otherwise.
+- (void)clearAutofilledPasswordsInForm:(NSString*)formName
+ completionHandler:(void (^)(BOOL))completionHandler;
+
+// Fills all password fields in the form identified by |formName| with
+// |password| and invokes |completionHandler| with true if any fields were
+// filled.
+- (void)fillPasswordForm:(NSString*)formName
+ withGeneratedPassword:(NSString*)password
+ completionHandler:(void (^)(BOOL))completionHandler;
+
+@end
+
+#endif // IOS_CHROME_BROWSER_PASSWORDS_JS_PASSWORD_MANAGER_H_
diff --git a/ios/chrome/browser/passwords/js_password_manager.mm b/ios/chrome/browser/passwords/js_password_manager.mm
new file mode 100644
index 0000000..d28205b
--- /dev/null
+++ b/ios/chrome/browser/passwords/js_password_manager.mm
@@ -0,0 +1,107 @@
+// Copyright 2012 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.
+
+#import "ios/chrome/browser/passwords/js_password_manager.h"
+
+#include "base/json/string_escape.h"
+#include "base/logging.h"
+#include "base/strings/sys_string_conversions.h"
+
+namespace {
+// Sanitizes |JSONString| and wraps it in quotes so it can be injected safely in
+// JavaScript.
+NSString* JSONEscape(NSString* JSONString) {
+ return base::SysUTF8ToNSString(
+ base::GetQuotedJSONString(base::SysNSStringToUTF8(JSONString)));
+}
+} // namespace
+
+@interface JsPasswordManager ()
+// Injects a script that does two things:
+// 1. Injects password controller JavaScript in the page.
+// 2. Extracts the _submitted_ password form data from the DOM on the page.
+// The result is returned in |completionHandler|.
+// |completionHandler| cannot be nil.
+- (void)evaluateExtraScript:(NSString*)script
+ completionHandler:(void (^)(NSString*))completionHandler;
+@end
+
+@implementation JsPasswordManager
+
+- (void)findPasswordFormsWithCompletionHandler:
+ (void (^)(NSString*))completionHandler {
+ DCHECK(completionHandler);
+ [self evaluateExtraScript:@"__gCrWeb.findPasswordForms()"
+ completionHandler:completionHandler];
+}
+
+- (void)extractForm:(NSString*)formName
+ completionHandler:(void (^)(NSString*))completionHandler {
+ DCHECK(completionHandler);
+ NSString* extra = [NSString
+ stringWithFormat:@"__gCrWeb.getPasswordForm(%@)", JSONEscape(formName)];
+ [self evaluateExtraScript:extra completionHandler:completionHandler];
+}
+
+- (void)fillPasswordForm:(NSString*)JSONString
+ withUsername:(NSString*)username
+ password:(NSString*)password
+ completionHandler:(void (^)(BOOL))completionHandler {
+ DCHECK(completionHandler);
+ NSString* script = [NSString
+ stringWithFormat:@"__gCrWeb.fillPasswordForm(%@, %@, %@)", JSONString,
+ JSONEscape(username), JSONEscape(password)];
+ [self evaluate:script
+ stringResultHandler:^(NSString* result, NSError* error) {
+ completionHandler(!error && [result isEqualToString:@"true"]);
+ }];
+}
+
+- (void)clearAutofilledPasswordsInForm:(NSString*)formName
+ completionHandler:(void (^)(BOOL))completionHandler {
+ NSString* script =
+ [NSString stringWithFormat:@"__gCrWeb.clearAutofilledPasswords(%@)",
+ JSONEscape(formName)];
+ [self evaluate:script
+ stringResultHandler:^(NSString* result, NSError* error) {
+ completionHandler(!error && [result isEqualToString:@"true"]);
+ }];
+}
+
+- (void)fillPasswordForm:(NSString*)formName
+ withGeneratedPassword:(NSString*)password
+ completionHandler:(void (^)(BOOL))completionHandler {
+ NSString* script =
+ [NSString stringWithFormat:
+ @"__gCrWeb.fillPasswordFormWithGeneratedPassword(%@, %@)",
+ JSONEscape(formName), JSONEscape(password)];
+ [self evaluate:script
+ stringResultHandler:^(NSString* result, NSError* error) {
+ if (completionHandler)
+ completionHandler(!error && [result isEqualToString:@"true"]);
+ }];
+}
+
+#pragma mark -
+#pragma mark ProtectedMethods
+
+- (NSString*)scriptPath {
+ return @"password_controller";
+}
+
+#pragma mark -
+#pragma mark Private
+
+- (void)evaluateExtraScript:(NSString*)script
+ completionHandler:(void (^)(NSString*))completionHandler {
+ DCHECK(completionHandler);
+ [self injectDependenciesIfMissing];
+ NSString* JS = [[self injectionContent] stringByAppendingString:script];
+ [self evaluate:JS
+ stringResultHandler:^(NSString* result, NSError*) {
+ completionHandler(result);
+ }];
+}
+
+@end
diff --git a/ios/chrome/browser/passwords/resources/credential_manager.js b/ios/chrome/browser/passwords/resources/credential_manager.js
new file mode 100644
index 0000000..8e2e840
--- /dev/null
+++ b/ios/chrome/browser/passwords/resources/credential_manager.js
@@ -0,0 +1,574 @@
+// Copyright 2015 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.
+
+/**
+ * @fileoverview JavaScript implementation of the credential management API
+ * defined at http://w3c.github.io/webappsec/specs/credentialmanagement.
+ * This is a minimal implementation that sends data to the app side to
+ * integrate with the password manager. When loaded, installs the API onto
+ * the window.navigator object.
+ */
+
+// Namespace for all credential management stuff. __gCrWeb must have already
+// been defined.
+__gCrWeb['credentialManager'] = {
+ /**
+ * The next ID for forwarding a call from JS to the App and tracking its
+ * associated Promise resolvers and rejecters.
+ * @private {number}
+ */
+ nextId_: 0,
+
+ /**
+ * Tracks navigator.credentials Promise resolvers.
+ * @type {!Object<number, function(?Credential|undefined)>}
+ * @private
+ */
+ resolvers_: {},
+
+ /**
+ * Tracks navigator.credentials Promise rejecters.
+ * @type {!Object<number, function(?Error)>}
+ * @private
+ */
+ rejecters_: {}
+};
+
+
+/** @enum {string} */
+__gCrWeb.credentialManager.CredentialType = {
+ CREDENTIAL: 'Credential',
+ PASSWORD_CREDENTIAL: 'PasswordCredential',
+ FEDERATED_CREDENTIAL: 'FederatedCredential',
+ PENDING_CREDENTIAL: 'PendingCredential'
+};
+
+
+/** @typedef {
+ * type: __gCrWeb.credentialManager.CredentialType,
+ * id: string,
+ * name: (string|undefined),
+ * avatarURL: (string|undefined),
+ * federation: (string|undefined)
+ * }
+ */
+__gCrWeb.credentialManager.SerializedCredential;
+
+
+/**
+ * Creates and returns a Promise whose resolver and rejecter functions are
+ * stored with the associated |requestId|. They can be accessed by calling
+ * |resolve| or |reject| on __gCrWeb['credentialManager'] with that ID.
+ * @param {number} requestId An identifier to track the resolver and rejecter
+ * associated with the returned Promise.
+ * @return {!Promise<?Credential|undefined>} A new Promise.
+ * @private
+ */
+__gCrWeb['credentialManager'].createPromise_ = function(requestId) {
+ return new Promise(function(resolve, reject) {
+ __gCrWeb['credentialManager'].resolvers_[requestId] = resolve;
+ __gCrWeb['credentialManager'].rejecters_[requestId] = reject;
+ });
+};
+
+
+/**
+ * Deletes the resolver and rejecter of the Promise associated with |requestId|.
+ * @param {number} requestId The identifier of the Promise.
+ * @private
+ */
+__gCrWeb['credentialManager'].removePromise_ = function(requestId) {
+ delete __gCrWeb['credentialManager'].rejecters_[requestId];
+ delete __gCrWeb['credentialManager'].resolvers_[requestId];
+};
+
+
+/**
+ * Parses |credentialData| into a Credential object.
+ * @param {!__gCrWeb.credentialManager.SerializedCredential} credentialData A
+ * simple object representation of a Credential like might be obtained from
+ * Credential.prototype.serialize.
+ * @return {?Credential} A Credential object, or null if parsing was
+ * unsuccessful.
+ * @private
+ */
+__gCrWeb['credentialManager'].parseCredential_ = function(credentialData) {
+ var CredentialType = __gCrWeb.credentialManager.CredentialType;
+ switch (credentialData['type']) {
+ case CredentialType.CREDENTIAL:
+ return Credential.parse(credentialData);
+ case CredentialType.PASSWORD_CREDENTIAL:
+ return PasswordCredential.parse(credentialData);
+ case CredentialType.FEDERATED_CREDENTIAL:
+ return FederatedCredential.parse(credentialData);
+ case CredentialType.PENDING_CREDENTIAL:
+ return PendingCredential.parse(credentialData);
+ default:
+ return null;
+ }
+};
+
+
+/**
+ * Resolves the Promise that was created with the given |requestId| with a
+ * Credential object parsed from |opt_credentialData| and removes that promise
+ * from the global state. Future attempts to resolve or reject the Promise will
+ * fail.
+ * @param {number} requestId The identifier of the Promise to resolve.
+ * @param {Object=} opt_credentialData An object describing a credential. If
+ * provided, this parameter will be parsed into the appropriate Credential
+ * type.
+ * @return {boolean} Indicates whether the Promise was successfully resolved.
+ */
+__gCrWeb['credentialManager'].resolve = function(requestId,
+ opt_credentialData) {
+ var resolver = __gCrWeb['credentialManager'].resolvers_[requestId];
+ if (!resolver) {
+ return false;
+ }
+ if (opt_credentialData) {
+ var credential = null;
+ try {
+ credential =
+ __gCrWeb['credentialManager'].parseCredential_(opt_credentialData);
+ } catch (e) {
+ // Failed to parse |opt_credentialData|. The app side sent bad data.
+ return false;
+ }
+ resolver(credential);
+ } else {
+ resolver();
+ }
+ __gCrWeb['credentialManager'].removePromise_(requestId);
+ return true;
+};
+
+
+/**
+ * Rejects the Promise that was created with the given |requestId| and cleans up
+ * that Promise.
+ * @param {number} requestId The identifier of the Promise to resolve.
+ * @param {string} errorType The type of the Error to pass to the rejecter
+ * function. If not recognized, a standard JavaScript Error will be used.
+ * @param {string} message A human-readable description of the error.
+ * @return {boolean} Indicates whether the Promise was successfully rejected.
+ */
+__gCrWeb['credentialManager'].reject = function(requestId, errorType, message) {
+ var rejecter = __gCrWeb['credentialManager'].rejecters_[requestId];
+ if (!rejecter) {
+ return false;
+ }
+ var error = null;
+ if (errorType == 'SecurityError') {
+ error = new SecurityError(message);
+ } else if (errorType == 'InvalidStateError') {
+ error = new InvalidStateError(message);
+ } else {
+ error = new Error(message);
+ }
+ rejecter(error);
+ __gCrWeb['credentialManager'].removePromise_(requestId);
+ return true;
+};
+
+
+/**
+ * Sends a command representing |method| of navigator.credentials to the app
+ * side with the given |opt_options|.
+ * @param {string} command The name of the command being sent.
+ * @param {Object=} opt_options A dictionary of additional properties to forward
+ * to the app.
+ * @return {!Promise<?Credential|undefined>} A promise for the result.
+ * @private
+ */
+__gCrWeb['credentialManager'].invokeOnHost_ = function(command, opt_options) {
+ var requestId = __gCrWeb['credentialManager'].nextId_++;
+ var message = {
+ 'command': command,
+ 'requestId': requestId
+ };
+ if (opt_options) {
+ Object.keys(opt_options).forEach(function(key) {
+ message[key] = opt_options[key];
+ });
+ }
+ __gCrWeb.message.invokeOnHost(message);
+ return __gCrWeb['credentialManager'].createPromise_(requestId);
+};
+
+
+
+/**
+ * Creates a new SecurityError to represent failures caused by violating
+ * security requirements of the API.
+ * @param {string} message A human-readable message describing this error.
+ * @extends {Error}
+ * @constructor
+ */
+function SecurityError(message) {
+ Error.call(this, message);
+ this.name = 'SecurityError';
+ this.message = message;
+}
+SecurityError.prototype = Object.create(Error.prototype);
+SecurityError.prototype.constructor = SecurityError;
+
+
+
+/**
+ * Creates a new InvalidStateError to represent failures caused by inconsistent
+ * internal state.
+ * @param {string} message A human-readable message describing this error.
+ * @extends {Error}
+ * @constructor
+ */
+function InvalidStateError(message) {
+ Error.call(this, message);
+ this.name = 'InvalidStateError';
+ this.message = message;
+}
+InvalidStateError.prototype = Object.create(Error.prototype);
+InvalidStateError.prototype.constructor = InvalidStateError;
+
+
+
+/**
+ * Creates a new Credential object. For more information, see
+ * https://w3c.github.io/webappsec/specs/credentialmanagement/#credential
+ * @param {string} id The credential’s identifier. This might be a username or
+ * email address, for instance.
+ * @param {string=} opt_name A name associated with the credential, intended as
+ * a human-understandable public name.
+ * @param {string=} opt_avatarUrl A URL pointing to an avatar image for the
+ * user. This URL MUST NOT be an a priori insecure URL.
+ * @constructor
+ */
+function Credential(id, opt_name, opt_avatarUrl) {
+ if (id === null || id === undefined)
+ throw new TypeError('id must be provided');
+ /**
+ * The credential's identifier. Read-only.
+ * @type {string}
+ */
+ this.id;
+ Object.defineProperty(this, 'id', {
+ configurable: false,
+ enumerable: true,
+ value: id,
+ writable: false
+ });
+ /**
+ * A human-understandable public name associated with the credential.
+ * Read-only.
+ * @type {string}
+ */
+ this.name;
+ Object.defineProperty(this, 'name', {
+ configurable: false,
+ enumerable: true,
+ value: opt_name || '',
+ writable: false
+ });
+ /**
+ * A URL pointing to an avatar image for the user. Read-only.
+ * NOTE: This property name deviates from the Google JavaScript style guide
+ * in order to meet the public API specification.
+ * @type {string}
+ */
+ this.avatarURL;
+ Object.defineProperty(this, 'avatarURL', {
+ configurable: false,
+ enumerable: true,
+ value: opt_avatarUrl || '',
+ writable: false
+ });
+}
+
+
+/**
+ * Returns a simple object representation of this credential suitable for
+ * sending to the app side.
+ * @return {__gCrWeb.credentialManager.SerializedCredential} An object
+ * representing this credential.
+ */
+Credential.prototype.serialize = function() {
+ var serialized = {
+ 'type': this.constructor.name,
+ 'id': this.id,
+ 'name': this.name
+ };
+ if (this.avatarURL && this.avatarURL.length > 0)
+ serialized['avatarURL'] = this.avatarURL;
+ return serialized;
+};
+
+
+/**
+ * Parses |credentialData| into a Credential object.
+ * @param {!__gCrWeb.credentialManager.SerializedCredential} credentialData A
+ * simple object representation of a Credential like might be obtained from
+ * Credential.prototype.serialize.
+ * @return {Credential} A Credential object.
+ */
+Credential.parse = function(credentialData) {
+ return new Credential(credentialData['id'],
+ credentialData['name'],
+ credentialData['avatarURL']);
+};
+
+
+
+/**
+ * Creates a new PasswordCredential object. For more information, see
+ * https://w3c.github.io/webappsec/specs/credentialmanagement/#localcredential
+ * @param {string} id The credential’s identifier. This might be a username or
+ * email address, for instance.
+ * @param {string} password The credential's password.
+ * @param {string=} opt_name A name associated with the credential, intended as
+ * a human-understandable public name.
+ * @param {string=} opt_avatarUrl A URL pointing to an avatar image for the
+ * user. This URL MUST NOT be an a priori insecure URL.
+ * @constructor
+ * @extends {Credential}
+ */
+function PasswordCredential(id, password, opt_name, opt_avatarUrl) {
+ if (password === null || password === undefined)
+ throw new TypeError('password must be provided');
+ Credential.call(this, id, opt_name, opt_avatarUrl);
+ var formData = new FormData();
+ formData.append('username', id);
+ formData.append('password', password);
+ /**
+ * A FormData object, containing two entries: one named username, the other
+ * named password. Read-only.
+ * @type {FormData}
+ */
+ this.formData;
+ Object.defineProperty(this, 'formData', {
+ configurable: false,
+ enumerable: true,
+ value: formData,
+ writable: false
+ });
+ /**
+ * The credential's password.
+ * @type {string}
+ * @private
+ */
+ this.password_;
+ Object.defineProperty(this, 'password_', {
+ configurable: false,
+ enumerable: false,
+ value: password,
+ writable: false
+ });
+}
+PasswordCredential.prototype = Object.create(Credential.prototype);
+PasswordCredential.prototype.constructor = PasswordCredential;
+
+
+/**
+ * Returns a simple object representation of this credential suitable for
+ * sending to the app side.
+ * @return {__gCrWeb.credentialManager.SerializedCredential} An object
+ * representing this credential.
+ */
+PasswordCredential.prototype.serialize = function() {
+ var obj = Credential.prototype.serialize.call(this);
+ obj.password = this.password_;
+ return obj;
+};
+
+
+/**
+ * Parses |credentialData| into a PasswordCredential object.
+ * @param {!__gCrWeb.credentialManager.SerializedCredential} credentialData A
+ * simple object representation of a PasswordCredential like might be
+ * obtained from PasswordCredential.prototype.serialize.
+ * @return {PasswordCredential} A PasswordCredential object.
+ */
+PasswordCredential.parse = function(credentialData) {
+ return new PasswordCredential(credentialData['id'],
+ credentialData['password'],
+ credentialData['name'],
+ credentialData['avatarURL']);
+};
+
+
+
+/**
+ * Creates a new FederatedCredential object. For more information, see
+ * https://w3c.github.io/webappsec/specs/credentialmanagement/#federatedcredential
+ * @param {string} id The credential’s identifier. This might be a username or
+ * email address, for instance.
+ * @param {string} federation The credential’s federation. For details regarding
+ * valid formats, see https://w3c.github.io/webappsec/specs/credentialmanagement/#identifying-federations
+ * @param {string=} opt_name A name associated with the credential, intended as
+ * a human-understandable public name.
+ * @param {string=} opt_avatarUrl A URL pointing to an avatar image for the
+ * user. This URL MUST NOT be an a priori insecure URL.
+ * @constructor
+ * @extends {Credential}
+ */
+function FederatedCredential(id, federation, opt_name, opt_avatarUrl) {
+ if (federation === null || federation === undefined)
+ throw new TypeError('federation must be provided');
+ Credential.call(this, id, opt_name, opt_avatarUrl);
+ /**
+ * The credential’s federation. Read-only.
+ * @type {string}
+ */
+ this.federation;
+ Object.defineProperty(this, 'federation', {
+ configurable: false,
+ enumerable: true,
+ value: federation,
+ writable: false
+ });
+}
+FederatedCredential.prototype = Object.create(Credential.prototype);
+FederatedCredential.prototype.constructor = FederatedCredential;
+
+
+/**
+ * Returns a simple object representation of this credential suitable for
+ * sending to the app side.
+ * @return {__gCrWeb.credentialManager.SerializedCredential} An object
+ * representing this credential.
+ */
+FederatedCredential.prototype.serialize = function() {
+ var obj = Credential.prototype.serialize.call(this);
+ obj.federation = this.federation;
+ return obj;
+};
+
+
+/**
+ * Parses |credentialData| into a FederatedCredential object.
+ * @param {!__gCrWeb.credentialManager.SerializedCredential} credentialData A
+ * simple object representation of a FederatedCredential like might be
+ * obtained from FederatedCredential.prototype.serialize.
+ * @return {!FederatedCredential} A FederatedCredential object.
+ */
+FederatedCredential.parse = function(credentialData) {
+ return new FederatedCredential(credentialData['id'],
+ credentialData['federation'],
+ credentialData['name'],
+ credentialData['avatarURL']);
+};
+
+
+
+/**
+ * Creates a new PendingCredential object. For more information, see
+ * https://w3c.github.io/webappsec/specs/credentialmanagement/#pendingcredential
+ * @param {string} id The credential’s identifier. This might be a username or
+ * email address, for instance.
+ * @param {string=} opt_name A name associated with the credential, intended as
+ * a human-understandable public name.
+ * @param {string=} opt_avatarUrl A URL pointing to an avatar image for the
+ * user. This URL MUST NOT be an a priori insecure URL.
+ * @constructor
+ * @extends {Credential}
+ */
+function PendingCredential(id, opt_name, opt_avatarUrl) {
+ Credential.call(this, id, opt_name, opt_avatarUrl);
+}
+PendingCredential.prototype = Object.create(Credential.prototype);
+PendingCredential.prototype.constructor = PendingCredential;
+
+
+/**
+ * Parses |credentialData| into a PendingCredential object.
+ * @param {!__gCrWeb.credentialManager.SerializedCredential} credentialData A
+ * simple object representation of a PendingCredential like might be
+ * obtained from PendingCredential.prototype.serialize.
+ * @return {!Credential} A PendingCredential object.
+ */
+PendingCredential.parse = function(credentialData) {
+ return new PendingCredential(credentialData['id'],
+ credentialData['name'],
+ credentialData['avatarURL']);
+};
+
+
+
+/**
+ * Implements the public Credential Management API. For more information, see
+ * http://w3c.github.io/webappsec/specs/credentialmanagement/#interfaces-credential-manager
+ * @constructor
+ */
+function CredentialsContainer() {
+}
+
+
+/**
+ * Requests a credential from the credential manager.
+ * @param {{suppressUI: boolean, federations: Array<string>}=} opt_options An
+ * optional dictionary of parameters for the request. If |suppressUI| is
+ * true, the returned promise will only be resolved with a credential if
+ * this is possible without user interaction; otherwise, the returned
+ * promise will be resolved with |undefined|. |federations| specifies a
+ * list of acceptable federation providers. For more information, see
+ * https://w3c.github.io/webappsec/specs/credentialmanagement/#interfaces-request-options
+ * @return {!Promise<?Credential|undefined>} A promise for retrieving the result
+ * of the request.
+ */
+CredentialsContainer.prototype.request = function(opt_options) {
+ var options = {
+ 'suppressUI': !!opt_options && !!opt_options['suppressUI'],
+ 'federations': (!!opt_options && opt_options['federations']) || []
+ };
+ return __gCrWeb['credentialManager'].invokeOnHost_(
+ 'navigator.credentials.request', options);
+};
+
+
+/**
+ * Notifies the browser that the user has successfully signed in.
+ * @param {Credential=} opt_successfulCredential The credential that was used
+ * to sign in.
+ * @return {!Promise<?Credential|undefined>} A promise to wait for
+ * acknowledgement from the browser.
+ */
+CredentialsContainer.prototype.notifySignedIn = function(
+ opt_successfulCredential) {
+ var options = opt_successfulCredential && {
+ 'credential': opt_successfulCredential.serialize()
+ };
+ return __gCrWeb['credentialManager'].invokeOnHost_(
+ 'navigator.credentials.notifySignedIn', options);
+};
+
+
+/**
+ * Notifies the browser that the user failed to sign in.
+ * @param {Credential=} opt_failedCredential The credential that failed to
+ * sign in.
+ * @return {!Promise<?Credential|undefined>} A promise to wait for
+ * acknowledgement from the browser.
+ */
+CredentialsContainer.prototype.notifyFailedSignIn = function(
+ opt_failedCredential) {
+ var options = opt_failedCredential && {
+ 'credential': opt_failedCredential.serialize()
+ };
+ return __gCrWeb['credentialManager'].invokeOnHost_(
+ 'navigator.credentials.notifyFailedSignIn', options);
+};
+
+
+/**
+ * Notifies the browser that the user signed out.
+ * @return {!Promise<?Credential|undefined>} A promise to wait for
+ * acknowledgement from the browser.
+ */
+CredentialsContainer.prototype.notifySignedOut = function() {
+ return __gCrWeb['credentialManager'].invokeOnHost_(
+ 'navigator.credentials.notifySignedOut');
+};
+
+
+// Install the public interface.
+window.navigator.credentials = new CredentialsContainer();
diff --git a/ios/chrome/browser/passwords/resources/password_controller.js b/ios/chrome/browser/passwords/resources/password_controller.js
new file mode 100644
index 0000000..8131ff29
--- /dev/null
+++ b/ios/chrome/browser/passwords/resources/password_controller.js
@@ -0,0 +1,418 @@
+// Copyright 2012 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.
+
+// This file adheres to closure-compiler conventions in order to enable
+// compilation with ADVANCED_OPTIMIZATIONS. See http://goo.gl/FwOgy
+//
+// Installs password management functions on the |__gCrWeb| object.
+//
+// Finds all password forms in the current document and extracts
+// their attributes and elements using the same logic as
+// third_party/WebKit/Source/WebCore/html/HTMLFormElement.cpp
+//
+// Returns a JSON string representing an array of objects,
+// where each object represents a password form with the discovered
+// elements and their values.
+//
+// The search for password form fields follows the same algorithm
+// as the WebKit implementation, see http://goo.gl/4hwh6
+
+// Only install the password management functions once.
+if (__gCrWeb && !__gCrWeb['fillPasswordForm']) {
+
+ /**
+ * Finds all password forms in the window and returns form data as a JSON
+ * string.
+ * @return {string} Form data as a JSON string.
+ */
+ __gCrWeb['findPasswordForms'] = function() {
+ var formDataList = [];
+ if (__gCrWeb.hasPasswordField()) {
+ __gCrWeb.getPasswordFormDataList(formDataList, window);
+ }
+ return __gCrWeb.stringify(formDataList);
+ };
+
+ /**
+ * Returns the password form with the given |name| as a JSON string.
+ * @param {string} name The name of the form to extract.
+ * @return {string} The password form.
+ */
+ __gCrWeb['getPasswordForm'] = function(name) {
+ var el = __gCrWeb.common.getFormElementFromIdentifier(name);
+ if (!el)
+ return 'noPasswordsFound';
+ var formData = __gCrWeb.getPasswordFormData(el);
+ if (!formData)
+ return 'noPasswordsFound';
+ return __gCrWeb.stringify(formData);
+ };
+
+ /**
+ * Returns an array of forms on the page that match the structure described by
+ * |formData|. The form matching logic follows that in
+ * chrome/renderer/autofill/password_autofill_manager.h.
+ * @param {Object} formData Form data.
+ * @param {Object} doc A document containing formData.
+ * @param {string=} opt_normalizedAction The action URL to compare to.
+ * @return {Array.<Element>} Array of forms found.
+ */
+ __gCrWeb.findMatchingPasswordForms = function(formData, doc,
+ opt_normalizedAction) {
+ var forms = doc.forms;
+ var fields = formData['fields'];
+ var matching = [];
+ for (var i = 0; i < forms.length; i++) {
+ var form = forms[i];
+ var normalizedFormAction = opt_normalizedAction ||
+ __gCrWeb.common.removeQueryAndReferenceFromURL(
+ __gCrWeb.common.absoluteURL(doc, form.action));
+ if (formData.action != normalizedFormAction) {
+ continue;
+ }
+
+ // We need to find all input fields matching |formData| in this form,
+ // otherwise it is the wrong form.
+ var inputs = form.getElementsByTagName('input');
+ var foundAllFields = true;
+ for (var fieldIndex = 0; fieldIndex < fields.length; fieldIndex++) {
+ var name = fields[fieldIndex]['name'];
+ var value = fields[fieldIndex]['value'];
+ // The first field in |formData| is always the username field,
+ // the second is always the password field.
+ var findingUsername = fieldIndex == 0;
+ var findingPassword = fieldIndex == 1;
+ var foundField = false;
+ for (var k = 0; k < inputs.length; k++) {
+ var input = inputs[k];
+
+ // Ensure that the field is the right type.
+ if (findingPassword && input.type != 'password') {
+ continue;
+ }
+ if (!findingPassword && (input.type == 'password' ||
+ !__gCrWeb.common.isTextField(input))) {
+ continue;
+ }
+
+ // Skip read-only fields without a value since they cannot be filled.
+ if (input.readOnly && input.value == '') {
+ continue;
+ }
+
+ // If more than one match is made, then we have an ambiguity (due to
+ // misuse of 'name' attribute) and the form is considered a mismatch.
+ if (input.name == name) {
+ if (foundField) {
+ foundField = false;
+ break;
+ }
+ foundField = true;
+ }
+ }
+
+ if (!foundField) {
+ foundAllFields = false;
+ break;
+ }
+ }
+
+ if (foundAllFields) {
+ matching.push(form);
+ }
+ }
+ return matching;
+ };
+
+ /**
+ * Clears autofilled credentials in the form with the specified name.
+ * @param {string} formName The name of the form to clear.
+ * @return {boolean} Whether the form was successfully cleared.
+ */
+ __gCrWeb['clearAutofilledPasswords'] = function(formName) {
+ var el = __gCrWeb.common.getFormElementFromIdentifier(formName);
+ if (!el)
+ return false;
+ var formData = __gCrWeb.getPasswordFormData(el);
+ if (!formData)
+ return false;
+ var usernameElement =
+ __gCrWeb.getElementByNameWithParent(el, formData.usernameElement);
+ __gCrWeb.setAutofilled(usernameElement, false);
+ formData.passwords.forEach(function(password) {
+ var passwordElement =
+ __gCrWeb.getElementByNameWithParent(el, password.element);
+ if (__gCrWeb.isAutofilled(passwordElement)) {
+ __gCrWeb.setAutofilled(passwordElement, false);
+ passwordElement.value = '';
+ }
+ });
+ return true;
+ };
+
+ /**
+ * Finds the form described by |formData| and fills in the
+ * username and password values.
+ *
+ * This is a public function invoked by Chrome. There is no information
+ * passed to this function that the page does not have access to anyway.
+ *
+ * @param {!Object.<string, *>} formData Dictionary of parameters,
+ * including:
+ * 'action': <string> The form action URL;
+ * 'fields': {Array.{Object.<string, string>}} Field name/value pairs;
+ * @param {string} username The username to fill.
+ * @param {string} password The password to fill.
+ * @param {string=} opt_normalizedOrigin The origin URL to compare to.
+ * @return {boolean} Whether a form field has been filled.
+ */
+ __gCrWeb['fillPasswordForm'] = function(formData, username, password,
+ opt_normalizedOrigin) {
+ return __gCrWeb.fillPasswordFormWithData(
+ formData, username, password, window, opt_normalizedOrigin);
+ };
+
+ /**
+ * Returns the element with the specified name that is a child of the
+ * specified parent element.
+ * @param {Element} parent The parent of the desired element.
+ * @param {string} name The name of the desired element.
+ * @return {Element} The element if found, otherwise null;
+ */
+ __gCrWeb['getElementByNameWithParent'] = function(parent, name) {
+ if (parent.name === name) {
+ return parent;
+ }
+ for (var i = 0; i < parent.children.length; i++) {
+ var el = __gCrWeb.getElementByNameWithParent(parent.children[i], name);
+ if (el) {
+ return el;
+ }
+ }
+ return null;
+ };
+
+ /**
+ * Given a description of a form (origin, action and input fields),
+ * finds that form on the page and fills in the specified username
+ * and password.
+ *
+ * @param {Object} formData Form data.
+ * @param {string} username The username to fill.
+ * @param {string} password The password to fill.
+ * @param {Object} win A window or a frame containing formData.
+ * @param {string=} opt_normalizedOrigin The origin URL to compare to.
+ * @return {boolean} Whether a form field has been filled.
+ */
+ __gCrWeb.fillPasswordFormWithData =
+ function(formData, username, password, win, opt_normalizedOrigin) {
+ var doc = win.document;
+
+ // If unable to read the 'document' property from a frame in a different
+ // origin, do nothing.
+ if (!doc) {
+ return false;
+ }
+
+ var origin = formData['origin'];
+ var normalizedOrigin = opt_normalizedOrigin ||
+ __gCrWeb.common.removeQueryAndReferenceFromURL(win.location.href);
+ if (origin != normalizedOrigin) {
+ return false;
+ }
+
+ var filled = false;
+
+ __gCrWeb.findMatchingPasswordForms(formData, doc, opt_normalizedOrigin).
+ forEach(function(form) {
+ var usernameInput =
+ __gCrWeb.getElementByNameWithParent(form, formData.fields[0].name);
+ var passwordInput =
+ __gCrWeb.getElementByNameWithParent(form, formData.fields[1].name);
+ if (!usernameInput.disabled && !passwordInput.disabled) {
+ // If username was provided on a read-only field and it matches the
+ // requested username, fill the form.
+ if (usernameInput.readOnly && usernameInput.value) {
+ if (usernameInput.value == username) {
+ passwordInput.value = password;
+ __gCrWeb.setAutofilled(passwordInput, true);
+ filled = true;
+ }
+ } else {
+ usernameInput.value = username;
+ passwordInput.value = password;
+ __gCrWeb.setAutofilled(passwordInput, true);
+ __gCrWeb.setAutofilled(usernameInput, true);
+ filled = true;
+ }
+ }
+ });
+
+ // Recursively invoke for all frames/iframes.
+ var frames = win.frames;
+ for (var i = 0; i < frames.length; i++) {
+ if (__gCrWeb.fillPasswordFormWithData(
+ formData, username, password, frames[i], opt_normalizedOrigin)) {
+ filled = true;
+ }
+ }
+
+ return filled;
+ };
+
+ /**
+ * Returns true if the supplied field |inputElement| was autofilled.
+ * @param {Element} inputElement The form field for which we need to
+ * acquire the autofilled indicator.
+ * @return {boolean} Whether inputElement was autofilled.
+ */
+ __gCrWeb.isAutofilled = function(inputElement) {
+ return inputElement['__gCrWebAutofilled'];
+ };
+
+ /**
+ * Marks the supplied field as autofilled or not depending on the
+ * |value|.
+ * @param {Element} inputElement The form field for which the indicator
+ * needs to be set.
+ * @param {boolean} value The new value of the indicator.
+ */
+ __gCrWeb.setAutofilled = function(inputElement, value) {
+ inputElement['__gCrWebAutofilled'] = value;
+ };
+
+ /**
+ * Selects text starting from |selectFrom| in the specified field.
+ * @param {string} formName The name of the form to select in.
+ * @param {string} fieldName The name of the field to select in.
+ * @param {number} selectFrom The starting index for selection.
+ * @return {boolean} Whether the operation was successful.
+ */
+ __gCrWeb['selectText'] = function(formName, fieldName, selectFrom) {
+ var form = __gCrWeb.common.getFormElementFromIdentifier(formName);
+ var el = __gCrWeb.getElementByNameWithParent(form, fieldName);
+ if (!el)
+ return false;
+ el.selectionStart = selectFrom;
+ el.selectionEnd = el.value.length;
+ return true;
+ };
+
+ /**
+ * Fills all password fields in the form identified by |formName|
+ * with |password| and marks them as autofilled.
+ *
+ * @param {string} formName The name of the form to fill.
+ * @param {string} password The password to fill.
+ * @return {boolean} Whether a password field has been filled.
+ */
+ __gCrWeb['fillPasswordFormWithGeneratedPassword'] =
+ function(formName, password) {
+ var form = __gCrWeb.common.getFormElementFromIdentifier(formName);
+ if (!form)
+ return false;
+ var fields = form.querySelectorAll('input[type=password]');
+ for (var i = 0; i < fields.length; i++) {
+ var field = fields[i];
+ field.value = password;
+ __gCrWeb.setAutofilled(field, true);
+ }
+ return fields.length > 0;
+ };
+
+ /**
+ * Finds all forms with passwords in the supplied window or frame and appends
+ * JS objects containing the form data to |formDataList|.
+ * @param {!Array.<Object>} formDataList A list that this function populates
+ * with descriptions of discovered forms.
+ * @param {Window} win A window (or frame) in which the function should
+ * look for password forms.
+ */
+ __gCrWeb.getPasswordFormDataList = function(formDataList, win) {
+ var doc = win.document;
+
+ // We may not be allowed to read the 'document' property from a frame
+ // that is in a different domain.
+ if (!doc) {
+ return;
+ }
+
+ var forms = doc.forms;
+ for (var i = 0; i < forms.length; i++) {
+ var formData = __gCrWeb.getPasswordFormData(forms[i]);
+ if (formData) {
+ formDataList.push(formData);
+ }
+ }
+
+ // Recursively invoke for all frames/iframes.
+ var frames = win.frames;
+ for (var i = 0; i < frames.length; i++) {
+ __gCrWeb.getPasswordFormDataList(formDataList, frames[i]);
+ }
+ };
+
+ /**
+ * Returns a JS object containing the data from |formElement|.
+ * @param {Element} formElement An HTML Form element.
+ * @return {Object} Object of data from formElement.
+ */
+ __gCrWeb.getPasswordFormData = function(formElement) {
+ var inputs = formElement.getElementsByTagName('input');
+
+ var fields = [];
+ var passwords = [];
+ var firstPasswordIndex = 0;
+ for (var j = 0; j < inputs.length; j++) {
+ // TODO(dplotnikov): figure out a way to identify the activated
+ // submit, which is the button that the user has already hit
+ // before this code is called.
+
+ var input = inputs[j];
+
+ fields.push({
+ 'element': input.name,
+ 'type': input.type
+ });
+
+ if (!input.disabled && input.type == 'password') {
+ if (passwords.length == 0) {
+ firstPasswordIndex = j;
+ }
+ passwords.push({
+ 'element': input.name,
+ 'value': input.value
+ });
+ }
+ }
+
+ if (passwords.length == 0)
+ return null;
+
+ var usernameElement = '';
+ var usernameValue = '';
+ for (var j = firstPasswordIndex - 1; j >= 0; j--) {
+ var input = inputs[j];
+ if (!input.disabled && __gCrWeb.common.isTextField(input)) {
+ usernameElement = input.name;
+ usernameValue = input.value;
+ break;
+ }
+ }
+
+ var origin = __gCrWeb.common.removeQueryAndReferenceFromURL(
+ formElement.ownerDocument.location.href);
+
+ return {
+ 'action': formElement.getAttribute('action'),
+ 'method': formElement.getAttribute('method'),
+ 'name': __gCrWeb.common.getFormIdentifier(formElement),
+ 'origin': origin,
+ 'fields': fields,
+ 'usernameElement': usernameElement,
+ 'usernameValue': usernameValue,
+ 'passwords': passwords
+ };
+ };
+}
diff --git a/ios/chrome/ios_chrome.gyp b/ios/chrome/ios_chrome.gyp
index 18e7e4c..e17a037 100644
--- a/ios/chrome/ios_chrome.gyp
+++ b/ios/chrome/ios_chrome.gyp
@@ -356,10 +356,16 @@
'browser/ntp_snippets/ios_chrome_ntp_snippets_service_factory.h',
'browser/open_from_clipboard/create_clipboard_recent_content.h',
'browser/open_from_clipboard/create_clipboard_recent_content.mm',
+ 'browser/passwords/credential_manager.h',
+ 'browser/passwords/credential_manager.mm',
'browser/passwords/ios_chrome_password_manager_setting_migrator_service_factory.cc',
'browser/passwords/ios_chrome_password_manager_setting_migrator_service_factory.h',
'browser/passwords/ios_chrome_password_store_factory.cc',
'browser/passwords/ios_chrome_password_store_factory.h',
+ 'browser/passwords/js_credential_manager.h',
+ 'browser/passwords/js_credential_manager.mm',
+ 'browser/passwords/js_password_manager.h',
+ 'browser/passwords/js_password_manager.mm',
'browser/passwords/password_generation_edit_view.h',
'browser/passwords/password_generation_edit_view.mm',
'browser/passwords/password_generation_offer_view.h',
@@ -609,11 +615,15 @@
'type': 'none',
'sources': [
'browser/find_in_page/resources/find_in_page.js',
+ 'browser/passwords/resources/credential_manager.js',
+ 'browser/passwords/resources/password_controller.js',
],
'includes': [ '../../ios/web/js_compile.gypi' ],
'link_settings': {
'mac_bundle_resources': [
+ '<(SHARED_INTERMEDIATE_DIR)/credential_manager.js',
'<(SHARED_INTERMEDIATE_DIR)/find_in_page.js',
+ '<(SHARED_INTERMEDIATE_DIR)/password_controller.js',
],
},
},
diff --git a/ios/web/BUILD.gn b/ios/web/BUILD.gn
index 3701f47..c2ca4a0 100644
--- a/ios/web/BUILD.gn
+++ b/ios/web/BUILD.gn
@@ -159,6 +159,7 @@ source_set("web") {
"public/web_state/crw_web_view_proxy.h",
"public/web_state/crw_web_view_scroll_view_proxy.h",
"public/web_state/global_web_state_observer.h",
+ "public/web_state/js/credential_util.h",
"public/web_state/js/crw_js_injection_evaluator.h",
"public/web_state/js/crw_js_injection_manager.h",
"public/web_state/js/crw_js_injection_receiver.h",
@@ -204,7 +205,6 @@ source_set("web") {
"web_state/global_web_state_event_tracker.cc",
"web_state/global_web_state_event_tracker.h",
"web_state/global_web_state_observer.cc",
- "web_state/js/credential_util.h",
"web_state/js/credential_util.mm",
"web_state/js/crw_js_early_script_manager.h",
"web_state/js/crw_js_early_script_manager.mm",
diff --git a/ios/web/ios_web.gyp b/ios/web/ios_web.gyp
index 16e4018..814fec5 100644
--- a/ios/web/ios_web.gyp
+++ b/ios/web/ios_web.gyp
@@ -100,8 +100,8 @@
'navigation/time_smoother.h',
'navigation/web_load_params.h',
'navigation/web_load_params.mm',
- 'net/cert_host_pair.h',
'net/cert_host_pair.cc',
+ 'net/cert_host_pair.h',
'net/cert_policy.cc',
'net/cert_store_impl.cc',
'net/cert_store_impl.h',
@@ -182,6 +182,7 @@
'public/web_state/crw_web_view_proxy.h',
'public/web_state/crw_web_view_scroll_view_proxy.h',
'public/web_state/global_web_state_observer.h',
+ 'public/web_state/js/credential_util.h',
'public/web_state/js/crw_js_injection_evaluator.h',
'public/web_state/js/crw_js_injection_manager.h',
'public/web_state/js/crw_js_injection_receiver.h',
@@ -227,7 +228,6 @@
'web_state/global_web_state_event_tracker.cc',
'web_state/global_web_state_event_tracker.h',
'web_state/global_web_state_observer.cc',
- 'web_state/js/credential_util.h',
'web_state/js/credential_util.mm',
'web_state/js/crw_js_early_script_manager.h',
'web_state/js/crw_js_early_script_manager.mm',
@@ -289,6 +289,8 @@
'web_state/web_view_internal_creation_util.mm',
'web_state/wk_web_view_security_util.h',
'web_state/wk_web_view_security_util.mm',
+ 'web_thread_impl.cc',
+ 'web_thread_impl.h',
'web_view_counter_impl.h',
'web_view_counter_impl.mm',
'web_view_creation_util.mm',
@@ -313,8 +315,6 @@
'webui/web_ui_ios_data_source_impl.h',
'webui/web_ui_ios_impl.h',
'webui/web_ui_ios_impl.mm',
- 'web_thread_impl.cc',
- 'web_thread_impl.h',
],
'link_settings': {
# TODO(crbug.com/541549): change to regular linking once support for
@@ -475,10 +475,10 @@
'public/test/web_test_util.h',
'test/crw_fake_web_controller_observer.h',
'test/crw_fake_web_controller_observer.mm',
- 'test/web_int_test.h',
- 'test/web_int_test.mm',
'test/test_web_thread.cc',
'test/test_web_thread_bundle.cc',
+ 'test/web_int_test.h',
+ 'test/web_int_test.mm',
'test/web_test.h',
'test/web_test.mm',
'test/web_test_suite.cc',
diff --git a/ios/web/web_state/js/credential_util.h b/ios/web/public/web_state/js/credential_util.h
index 39230de..f18b274 100644
--- a/ios/web/web_state/js/credential_util.h
+++ b/ios/web/public/web_state/js/credential_util.h
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef IOS_WEB_WEB_STATE_JS_CREDENTIAL_UTIL_H_
-#define IOS_WEB_WEB_STATE_JS_CREDENTIAL_UTIL_H_
+#ifndef IOS_WEB_PUBLIC_WEB_STATE_JS_CREDENTIAL_UTIL_H_
+#define IOS_WEB_PUBLIC_WEB_STATE_JS_CREDENTIAL_UTIL_H_
namespace base {
class DictionaryValue;
@@ -44,4 +44,4 @@ void CredentialToDictionaryValue(const Credential& credential,
} // namespace web
-#endif // IOS_WEB_WEB_STATE_JS_CREDENTIAL_UTIL_H_
+#endif // IOS_WEB_PUBLIC_WEB_STATE_JS_CREDENTIAL_UTIL_H_
diff --git a/ios/web/web_state/js/credential_util.mm b/ios/web/web_state/js/credential_util.mm
index d0a9b4a..9017e42 100644
--- a/ios/web/web_state/js/credential_util.mm
+++ b/ios/web/web_state/js/credential_util.mm
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ios/web/web_state/js/credential_util.h"
+#include "ios/web/public/web_state/js/credential_util.h"
#include "base/logging.h"
#include "base/strings/string16.h"
diff --git a/ios/web/web_state/js/credential_util_unittest.mm b/ios/web/web_state/js/credential_util_unittest.mm
index dc42abd..7982ea1 100644
--- a/ios/web/web_state/js/credential_util_unittest.mm
+++ b/ios/web/web_state/js/credential_util_unittest.mm
@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ios/web/web_state/js/credential_util.h"
+#include "ios/web/public/web_state/js/credential_util.h"
#include "base/memory/scoped_ptr.h"
#include "base/strings/utf_string_conversions.h"
diff --git a/ios/web/web_state/ui/crw_web_controller.mm b/ios/web/web_state/ui/crw_web_controller.mm
index 2fa81f2..28f95f4 100644
--- a/ios/web/web_state/ui/crw_web_controller.mm
+++ b/ios/web/web_state/ui/crw_web_controller.mm
@@ -54,6 +54,7 @@
#include "ios/web/public/web_state/credential.h"
#import "ios/web/public/web_state/crw_web_controller_observer.h"
#import "ios/web/public/web_state/crw_web_view_scroll_view_proxy.h"
+#import "ios/web/public/web_state/js/credential_util.h"
#import "ios/web/public/web_state/js/crw_js_injection_manager.h"
#import "ios/web/public/web_state/js/crw_js_injection_receiver.h"
#import "ios/web/public/web_state/ui/crw_content_view.h"
@@ -66,7 +67,6 @@
#import "ios/web/web_state/crw_web_view_proxy_impl.h"
#import "ios/web/web_state/error_translation_util.h"
#include "ios/web/web_state/frame_info.h"
-#import "ios/web/web_state/js/credential_util.h"
#import "ios/web/web_state/js/crw_js_early_script_manager.h"
#import "ios/web/web_state/js/crw_js_plugin_placeholder_manager.h"
#import "ios/web/web_state/js/crw_js_window_id_manager.h"