diff options
author | khorimoto <khorimoto@chromium.org> | 2015-06-12 20:31:01 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-06-13 03:32:25 +0000 |
commit | 03daaabaec0b367ca2a61ecebc33572ea79e4cf9 (patch) | |
tree | 0d286270ddab9d74c07cce5c362f08630f74a5e2 | |
parent | 6f840fc5d6482ef18945e501f729f8eb1c954f84 (diff) | |
download | chromium_src-03daaabaec0b367ca2a61ecebc33572ea79e4cf9.zip chromium_src-03daaabaec0b367ca2a61ecebc33572ea79e4cf9.tar.gz chromium_src-03daaabaec0b367ca2a61ecebc33572ea79e4cf9.tar.bz2 |
Implement the chrome.passwordsPrivate API.
See https://docs.google.com/document/d/1vapDN-76XS4dLsNRFQP9NMnhDstPAC88vPuKblhgMok/edit?usp=sharing for details.
BUG=485227
TBR=dbeam@chromium.org
NOPRESUBMIT=true
Review URL: https://codereview.chromium.org/1142693003
Cr-Commit-Position: refs/heads/master@{#334318}
30 files changed, 1368 insertions, 40 deletions
diff --git a/chrome/browser/android/password_ui_view_android.cc b/chrome/browser/android/password_ui_view_android.cc index b079b22..4cbf00b 100644 --- a/chrome/browser/android/password_ui_view_android.cc +++ b/chrome/browser/android/password_ui_view_android.cc @@ -37,7 +37,10 @@ Profile* PasswordUIViewAndroid::GetProfile() { } void PasswordUIViewAndroid::ShowPassword( - size_t index, const base::string16& password_value) { + size_t index, + const std::string& origin_url, + const std::string& username, + const base::string16& password_value) { NOTIMPLEMENTED(); } diff --git a/chrome/browser/android/password_ui_view_android.h b/chrome/browser/android/password_ui_view_android.h index 2d826d9..50fed62 100644 --- a/chrome/browser/android/password_ui_view_android.h +++ b/chrome/browser/android/password_ui_view_android.h @@ -25,8 +25,11 @@ class PasswordUIViewAndroid : public PasswordUIView { // PasswordUIView implementation. Profile* GetProfile() override; - void ShowPassword(size_t index, - const base::string16& password_value) override; + void ShowPassword( + size_t index, + const std::string& origin_url, + const std::string& username, + const base::string16& password_value) override; void SetPasswordList( const ScopedVector<autofill::PasswordForm>& password_list, bool show_passwords) override; diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc index ce1128a..11f4926 100644 --- a/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc +++ b/chrome/browser/extensions/api/passwords_private/passwords_private_api.cc @@ -5,7 +5,10 @@ #include "chrome/browser/extensions/api/passwords_private/passwords_private_api.h" #include "base/values.h" +#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h" #include "chrome/common/extensions/api/passwords_private.h" +#include "components/password_manager/core/common/experiments.h" +#include "content/public/browser/web_contents.h" #include "extensions/browser/extension_function_registry.h" namespace extensions { @@ -18,9 +21,9 @@ PasswordsPrivateCanPasswordAccountBeManagedFunction:: ExtensionFunction::ResponseAction PasswordsPrivateCanPasswordAccountBeManagedFunction::Run() { - // TODO(khorimoto): Implement. - - return RespondNow(NoArguments()); + scoped_ptr<base::FundamentalValue> visible(new base::FundamentalValue( + password_manager::ManageAccountLinkExperimentEnabled())); + return RespondNow(OneArgument(visible.Pass())); } //////////////////////////////////////////////////////////////////////////////// @@ -36,7 +39,11 @@ ExtensionFunction::ResponseAction Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters.get()); - // TODO(khorimoto): Implement. + PasswordsPrivateDelegate* delegate = + PasswordsPrivateDelegateFactory::GetForBrowserContext(browser_context()); + delegate->RemoveSavedPassword( + parameters->login_pair.origin_url, + parameters->login_pair.username); return RespondNow(NoArguments()); } @@ -54,26 +61,36 @@ ExtensionFunction::ResponseAction Params::Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters.get()); - // TODO(khorimoto): Implement. + PasswordsPrivateDelegate* delegate = + PasswordsPrivateDelegateFactory::GetForBrowserContext(browser_context()); + delegate->RemovePasswordException(parameters->exception_url); return RespondNow(NoArguments()); } //////////////////////////////////////////////////////////////////////////////// -// PasswordsPrivateGetPlaintextPasswordFunction +// PasswordsPrivateRequestPlaintextPasswordFunction -PasswordsPrivateGetPlaintextPasswordFunction:: - ~PasswordsPrivateGetPlaintextPasswordFunction() {} +PasswordsPrivateRequestPlaintextPasswordFunction:: + ~PasswordsPrivateRequestPlaintextPasswordFunction() {} ExtensionFunction::ResponseAction - PasswordsPrivateGetPlaintextPasswordFunction::Run() { - scoped_ptr<api::passwords_private::GetPlaintextPassword::Params> - parameters = api::passwords_private::GetPlaintextPassword::Params:: + PasswordsPrivateRequestPlaintextPasswordFunction::Run() { + scoped_ptr<api::passwords_private::RequestPlaintextPassword::Params> + parameters = api::passwords_private::RequestPlaintextPassword::Params:: Create(*args_); EXTENSION_FUNCTION_VALIDATE(parameters.get()); - // TODO(khorimoto): Implement. + PasswordsPrivateDelegate* delegate = + PasswordsPrivateDelegateFactory::GetForBrowserContext(browser_context()); + + delegate->RequestShowPassword( + parameters->login_pair.origin_url, + parameters->login_pair.username, + render_view_host()); + // No response given from this API function; instead, listeners wait for the + // chrome.passwordsPrivate.onPlaintextPasswordRetrieved event to fire. return RespondNow(NoArguments()); } diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_api.h b/chrome/browser/extensions/api/passwords_private/passwords_private_api.h index 4cef191..0b8d985 100644 --- a/chrome/browser/extensions/api/passwords_private/passwords_private_api.h +++ b/chrome/browser/extensions/api/passwords_private/passwords_private_api.h @@ -8,6 +8,8 @@ #include <string> #include "base/macros.h" +#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h" +#include "chrome/browser/ui/passwords/password_manager_presenter.h" #include "extensions/browser/extension_function.h" namespace extensions { @@ -63,21 +65,21 @@ class PasswordsPrivateRemovePasswordExceptionFunction : DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateRemovePasswordExceptionFunction); }; -class PasswordsPrivateGetPlaintextPasswordFunction : +class PasswordsPrivateRequestPlaintextPasswordFunction : public UIThreadExtensionFunction { public: - PasswordsPrivateGetPlaintextPasswordFunction() {} - DECLARE_EXTENSION_FUNCTION("passwordsPrivate.GetPlaintextPassword", - PASSWORDSPRIVATE_GETPLAINTEXTPASSWORD); + PasswordsPrivateRequestPlaintextPasswordFunction() {} + DECLARE_EXTENSION_FUNCTION("passwordsPrivate.requestPlaintextPassword", + PASSWORDSPRIVATE_REQUESTPLAINTEXTPASSWORD); protected: - ~PasswordsPrivateGetPlaintextPasswordFunction() override; + ~PasswordsPrivateRequestPlaintextPasswordFunction() override; // ExtensionFunction overrides. ResponseAction Run() override; private: - DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateGetPlaintextPasswordFunction); + DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateRequestPlaintextPasswordFunction); }; } // namespace extensions diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc new file mode 100644 index 0000000..7c462fa --- /dev/null +++ b/chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc @@ -0,0 +1,198 @@ +// 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. + +#include <sstream> + +#include "base/command_line.h" +#include "base/memory/linked_ptr.h" +#include "base/observer_list_threadsafe.h" +#include "base/strings/utf_string_conversions.h" +#include "base/values.h" +#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h" +#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h" +#include "chrome/browser/extensions/extension_apitest.h" +#include "chrome/common/extensions/api/passwords_private.h" +#include "chrome/test/base/testing_profile.h" +#include "components/keyed_service/core/keyed_service.h" +#include "content/public/test/test_utils.h" +#include "extensions/common/switches.h" + +namespace extensions { + +namespace { + +static const size_t kNumMocks = 3; +static const int kNumCharactersInPassword = 10; +static const char kPlaintextPassword[] = "plaintext"; + +linked_ptr<api::passwords_private::PasswordUiEntry> CreateEntry(size_t num) { + api::passwords_private::PasswordUiEntry* entry = + new api::passwords_private::PasswordUiEntry(); + std::stringstream ss; + ss << "http://test" << num << ".com"; + entry->login_pair.origin_url = ss.str(); + ss.clear(); + ss << "testName" << num; + entry->login_pair.username = ss.str(); + entry->num_characters_in_password = kNumCharactersInPassword; + return make_linked_ptr(entry); +} + +std::string CreateException(size_t num) { + std::stringstream ss; + ss << "http://exception" << num << ".com"; + return ss.str(); +} + +// A test PasswordsPrivateDelegate implementation which uses mock data. +// TestDelegate starts out with kNumMocks mocks of each type (saved password +// and password exception) and removes one mock each time RemoveSavedPassword() +// or RemovePasswordException() is called. +class TestDelegate : public PasswordsPrivateDelegate { + public: + TestDelegate() : observers_(new base::ObserverListThreadSafe<Observer>()) { + // Create mock data. + for (size_t i = 0; i < kNumMocks; i++) { + current_entries_.push_back(CreateEntry(i));; + current_exceptions_.push_back(CreateException(i)); + } + } + ~TestDelegate() override {} + + void AddObserver(Observer* observer) override { + observers_->AddObserver(observer); + SendSavedPasswordsList(); + SendPasswordExceptionsList(); + } + + void RemoveObserver(Observer* observer) override { + observers_->RemoveObserver(observer); + } + + void RemoveSavedPassword( + const std::string& origin_url, const std::string& username) override { + if (!current_entries_.size()) + return; + + // Since this is just mock data, remove the first entry regardless of + // the data contained. + current_entries_.erase(current_entries_.begin()); + SendSavedPasswordsList(); + } + + void RemovePasswordException(const std::string& exception_url) override { + if (!current_exceptions_.size()) + return; + + // Since this is just mock data, remove the first entry regardless of + // the data contained. + current_exceptions_.erase(current_exceptions_.begin()); + SendPasswordExceptionsList(); + } + + void RequestShowPassword( + const std::string& origin_url, + const std::string& username, + const content::RenderViewHost* render_view_host) override { + // Return a mocked password value. + std::string plaintext_password(kPlaintextPassword); + observers_->Notify( + FROM_HERE, + &Observer::OnPlaintextPasswordFetched, + origin_url, + username, + plaintext_password); + } + + private: + void SendSavedPasswordsList() { + observers_->Notify( + FROM_HERE, + &Observer::OnSavedPasswordsListChanged, + current_entries_); + } + + void SendPasswordExceptionsList() { + observers_->Notify( + FROM_HERE, + &Observer::OnPasswordExceptionsListChanged, + current_exceptions_); + } + + // The current list of entries/exceptions. Cached here so that when new + // observers are added, this delegate can send the current lists without + // having to request them from |password_manager_presenter_| again. + std::vector<linked_ptr<api::passwords_private::PasswordUiEntry>> + current_entries_; + std::vector<std::string> current_exceptions_; + + // The observers. + scoped_refptr<base::ObserverListThreadSafe<Observer>> observers_; +}; + +class PasswordsPrivateApiTest : public ExtensionApiTest { + public: + PasswordsPrivateApiTest() { + if (!s_test_delegate_) { + s_test_delegate_ = new TestDelegate(); + } + } + ~PasswordsPrivateApiTest() override {} + + static scoped_ptr<KeyedService> GetPasswordsPrivateDelegate( + content::BrowserContext* profile) { + CHECK(s_test_delegate_); + return make_scoped_ptr(s_test_delegate_); + } + + void SetUpCommandLine(base::CommandLine* command_line) override { + ExtensionApiTest::SetUpCommandLine(command_line); + } + + void SetUp() override { + ExtensionApiTest::SetUp(); + } + + void SetUpOnMainThread() override { + ExtensionApiTest::SetUpOnMainThread(); + PasswordsPrivateDelegateFactory::GetInstance()->SetTestingFactory( + profile(), &PasswordsPrivateApiTest::GetPasswordsPrivateDelegate); + content::RunAllPendingInMessageLoop(); + } + + protected: + bool RunPasswordsSubtest(const std::string& subtest) { + return RunExtensionSubtest("passwords_private", + "main.html?" + subtest, + kFlagLoadAsComponent); + } + + private: + static TestDelegate* s_test_delegate_; + + DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateApiTest); +}; + +// static +TestDelegate* PasswordsPrivateApiTest::s_test_delegate_ = nullptr; + +} // namespace + +IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, CanPasswordAccountBeManaged) { + EXPECT_TRUE(RunPasswordsSubtest("canPasswordAccountBeManaged")) << message_; +} + +IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, RemoveSavedPassword) { + EXPECT_TRUE(RunPasswordsSubtest("removeSavedPassword")) << message_; +} + +IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, RemovePasswordException) { + EXPECT_TRUE(RunPasswordsSubtest("removePasswordException")) << message_; +} + +IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, RequestPlaintextPassword) { + EXPECT_TRUE(RunPasswordsSubtest("requestPlaintextPassword")) << message_; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h new file mode 100644 index 0000000..4683e28 --- /dev/null +++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h @@ -0,0 +1,95 @@ +// 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 CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_DELEGATE_H_ +#define CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_DELEGATE_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/observer_list_threadsafe.h" +#include "chrome/browser/ui/passwords/password_manager_presenter.h" +#include "chrome/browser/ui/passwords/password_ui_view.h" +#include "chrome/common/extensions/api/passwords_private.h" +#include "components/keyed_service/core/keyed_service.h" +#include "extensions/browser/extension_function.h" + +class Profile; + +namespace base { +class Value; +} + +namespace content { +class RenderViewHost; +} + +namespace extensions { + +// Delegate used by the chrome.passwordsPrivate API to facilitate removing saved +// passwords and password exceptions and to notify listeners when these values +// have changed. +class PasswordsPrivateDelegate : public KeyedService { + public: + ~PasswordsPrivateDelegate() override {} + + // An interface used to notify clients (observers) of this object that + // saved passwords, password exceptions, and plaintext passwords are ready to + // be consumed by the UI. Register an observer via + // PasswordsPrivateDelegate::AddObserver(). + class Observer { + public: + virtual void OnSavedPasswordsListChanged(const std::vector<linked_ptr< + api::passwords_private::PasswordUiEntry>>& entries) {} + virtual void OnPasswordExceptionsListChanged( + const std::vector<std::string>& exceptions) {} + virtual void OnPlaintextPasswordFetched( + const std::string& origin_url, + const std::string& username, + const std::string& plaintext_password) {} + + protected: + virtual ~Observer() {} + }; + + // Adds |observer| to be notified when password data changes. + virtual void AddObserver(Observer* observer) = 0; + + // Removes |observer| from the observer list. + virtual void RemoveObserver(Observer* observer) = 0; + + // Removes the saved password entry corresponding to |origin_url| and + // |username|. + // |origin_url| The human-readable origin for the URL where the password is + // used/ should be obtained using GetHumanReadableOrigin(). + // |username| The username used in conjunction with the saved password. + virtual void RemoveSavedPassword( + const std::string& origin_url, const std::string& username) = 0; + + // Removes the saved password exception entry corresponding to + // |exception_url|. + // |exception_url| The URL corresponding to the exception to remove; should + // be obtained using GetHumanReadableOrigin(). + virtual void RemovePasswordException(const std::string& exception_url) = 0; + + // Requests the plain text password for entry corresponding to |origin_url| + // and |username|. + // |origin_url| The human-readable origin for the URL where the password is + // used; should be obtained using GetHumanReadableOrigin(). + // |username| The username used in conjunction with the saved password. + // |native_window| The Chrome host window; will be used to show an OS-level + // authentication dialog if necessary. + virtual void RequestShowPassword( + const std::string& origin_url, + const std::string& username, + const content::RenderViewHost* render_view_host) = 0; +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_DELEGATE_H_ diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc new file mode 100644 index 0000000..c535df1 --- /dev/null +++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc @@ -0,0 +1,53 @@ +// 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. + +#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h" + +#include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h" +#include "chrome/browser/profiles/profile.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "extensions/browser/extension_system_provider.h" + +namespace extensions { + +using content::BrowserContext; + +// static +PasswordsPrivateDelegate* PasswordsPrivateDelegateFactory::GetForBrowserContext( + BrowserContext* browser_context) { + return static_cast<PasswordsPrivateDelegate*>( + GetInstance()->GetServiceForBrowserContext(browser_context, true)); +} + +// static +PasswordsPrivateDelegateFactory* + PasswordsPrivateDelegateFactory::GetInstance() { + return Singleton<PasswordsPrivateDelegateFactory>::get(); +} + +PasswordsPrivateDelegateFactory::PasswordsPrivateDelegateFactory() + : BrowserContextKeyedServiceFactory( + "PasswordsPrivateDelegate", + BrowserContextDependencyManager::GetInstance()) { +} + +PasswordsPrivateDelegateFactory::~PasswordsPrivateDelegateFactory() { +} + +KeyedService* PasswordsPrivateDelegateFactory::BuildServiceInstanceFor( + content::BrowserContext* profile) const { + return new PasswordsPrivateDelegateImpl(static_cast<Profile*>(profile)); +} + +bool PasswordsPrivateDelegateFactory:: + ServiceIsCreatedWithBrowserContext() const { + return false; +} + +bool PasswordsPrivateDelegateFactory::ServiceIsNULLWhileTesting() const { + return false; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h new file mode 100644 index 0000000..e7139a3 --- /dev/null +++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h @@ -0,0 +1,44 @@ +// 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 CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_DELEGATE_FACTORY_H_ +#define CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_DELEGATE_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace context { +class BrowserContext; +} + +namespace extensions { +class PasswordsPrivateDelegate; + +// Factory for creating PasswordPrivateDelegates. +class PasswordsPrivateDelegateFactory + : public BrowserContextKeyedServiceFactory { + public: + static PasswordsPrivateDelegate* GetForBrowserContext( + content::BrowserContext* browser_context); + + static PasswordsPrivateDelegateFactory* GetInstance(); + + private: + friend struct DefaultSingletonTraits<PasswordsPrivateDelegateFactory>; + + PasswordsPrivateDelegateFactory(); + ~PasswordsPrivateDelegateFactory() override; + + // BrowserContextKeyedBaseFactory implementation. + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* profile) const override; + bool ServiceIsCreatedWithBrowserContext() const override; + bool ServiceIsNULLWhileTesting() const override; + + DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateDelegateFactory); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_DELEGATE_FACTORY_H_ diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc new file mode 100644 index 0000000..d6ae163 --- /dev/null +++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc @@ -0,0 +1,263 @@ +// 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. + +#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h" + +#include "base/prefs/pref_service.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/passwords/manage_passwords_view_utils.h" +#include "chrome/common/pref_names.h" +#include "chrome/grit/generated_resources.h" +#include "components/password_manager/core/browser/affiliation_utils.h" +#include "content/public/browser/web_contents.h" +#include "ui/base/l10n/l10n_util.h" + +namespace { + +std::string LoginPairToMapKey( + const std::string& origin_url, const std::string& username) { + // Concatenate origin URL and username to form a unique key. + return origin_url + ',' + username; +} + +} + +namespace extensions { + +PasswordsPrivateDelegateImpl::PasswordsPrivateDelegateImpl(Profile* profile) + : profile_(profile), + password_manager_presenter_(new PasswordManagerPresenter(this)), + set_password_list_called_(false), + set_password_exception_list_called_(false), + is_initialized_(false), + languages_(profile->GetPrefs()->GetString(prefs::kAcceptLanguages)), + render_view_host_(nullptr), + observers_(new base::ObserverListThreadSafe<Observer>()) { + password_manager_presenter_->Initialize(); + password_manager_presenter_->UpdatePasswordLists(); +} + +PasswordsPrivateDelegateImpl::~PasswordsPrivateDelegateImpl() {} + +void PasswordsPrivateDelegateImpl::AddObserver(Observer* observer) { + observers_->AddObserver(observer); + + // Send the current cached lists to the new observer. + ExecuteFunction(base::Bind( + &PasswordsPrivateDelegateImpl::SendSavedPasswordsList, + base::Unretained(this))); + ExecuteFunction(base::Bind( + &PasswordsPrivateDelegateImpl::SendPasswordExceptionsList, + base::Unretained(this))); +} + +void PasswordsPrivateDelegateImpl::RemoveObserver(Observer* observer) { + observers_->RemoveObserver(observer); +} + +void PasswordsPrivateDelegateImpl::RemoveSavedPassword( + const std::string& origin_url, const std::string& username) { + ExecuteFunction(base::Bind( + &PasswordsPrivateDelegateImpl::RemoveSavedPasswordInternal, + base::Unretained(this), + origin_url, + username)); +} + +void PasswordsPrivateDelegateImpl::RemoveSavedPasswordInternal( + const std::string& origin_url, const std::string& username) { + std::string key = LoginPairToMapKey(origin_url, username); + if (login_pair_to_index_map_.find(key) == login_pair_to_index_map_.end()) { + // If the URL/username pair does not exist in the map, do nothing. + return; + } + + password_manager_presenter_->RemoveSavedPassword( + login_pair_to_index_map_[key]); +} + +void PasswordsPrivateDelegateImpl::RemovePasswordException( + const std::string& exception_url) { + ExecuteFunction(base::Bind( + &PasswordsPrivateDelegateImpl::RemovePasswordExceptionInternal, + base::Unretained(this), + exception_url)); +} + +void PasswordsPrivateDelegateImpl::RemovePasswordExceptionInternal( + const std::string& exception_url) { + if (exception_url_to_index_map_.find(exception_url) == + exception_url_to_index_map_.end()) { + // If the exception URL does not exist in the map, do nothing. + return; + } + + password_manager_presenter_->RemovePasswordException( + exception_url_to_index_map_[exception_url]); +} + +void PasswordsPrivateDelegateImpl::RequestShowPassword( + const std::string& origin_url, + const std::string& username, + const content::RenderViewHost* render_view_host) { + ExecuteFunction(base::Bind( + &PasswordsPrivateDelegateImpl::RequestShowPasswordInternal, + base::Unretained(this), + origin_url, + username, + render_view_host)); +} + +void PasswordsPrivateDelegateImpl::RequestShowPasswordInternal( + const std::string& origin_url, + const std::string& username, + const content::RenderViewHost* render_view_host) { + std::string key = LoginPairToMapKey(origin_url, username); + if (login_pair_to_index_map_.find(key) == login_pair_to_index_map_.end()) { + // If the URL/username pair does not exist in the map, do nothing. + return; + } + + // Save |render_view_host| so that the call to RequestShowPassword() below can + // call use this value by calling GetNativeWindow(). Note: This is safe + // because GetNativeWindow() will only be called immediately from + // RequestShowPassword(). + // TODO(stevenjb): Pass this directly to RequestShowPassword(); see + // crbug.com/495290. + render_view_host_ = render_view_host; + + // Request the password. When it is retrieved, ShowPassword() will be called. + password_manager_presenter_->RequestShowPassword( + login_pair_to_index_map_[key]); +} + +Profile* PasswordsPrivateDelegateImpl::GetProfile() { + return profile_; +} + +void PasswordsPrivateDelegateImpl::ShowPassword( + size_t index, + const std::string& origin_url, + const std::string& username, + const base::string16& password_value) { + observers_->Notify( + FROM_HERE, + &Observer::OnPlaintextPasswordFetched, + origin_url, + username, + base::UTF16ToUTF8(password_value)); +} + +void PasswordsPrivateDelegateImpl::SetPasswordList( + const ScopedVector<autofill::PasswordForm>& password_list, + bool show_passwords) { + // Rebuild |login_pair_to_index_map_| so that it reflects the contents of the + // new list. + login_pair_to_index_map_.clear(); + for (size_t i = 0; i < password_list.size(); i++) { + std::string key = LoginPairToMapKey( + password_manager::GetHumanReadableOrigin(*password_list[i], languages_), + base::UTF16ToUTF8(password_list[i]->username_value)); + login_pair_to_index_map_[key] = i; + } + + // Now, create a list of PasswordUiEntry objects to send to observers. + current_entries_.clear(); + for (const autofill::PasswordForm* form : password_list) { + linked_ptr<api::passwords_private::PasswordUiEntry> entry( + new api::passwords_private::PasswordUiEntry); + entry->login_pair.origin_url = + password_manager::GetHumanReadableOrigin(*form, languages_); + entry->login_pair.username = base::UTF16ToUTF8(form->username_value); + entry->num_characters_in_password = form->password_value.length(); + + const GURL& federation_url = form->federation_url; + if (!federation_url.is_empty()) { + entry->federation_text.reset(new std::string(l10n_util::GetStringFUTF8( + IDS_PASSWORDS_VIA_FEDERATION, + base::UTF8ToUTF16(federation_url.host())))); + } + + current_entries_.push_back(entry); + } + + SendSavedPasswordsList(); + + set_password_list_called_ = true; + InitializeIfNecessary(); +} + +void PasswordsPrivateDelegateImpl::SendSavedPasswordsList() { + observers_->Notify( + FROM_HERE, &Observer::OnSavedPasswordsListChanged, current_entries_); +} + +void PasswordsPrivateDelegateImpl::SetPasswordExceptionList( + const ScopedVector<autofill::PasswordForm>& password_exception_list) { + // Rebuild |exception_url_to_index_map_| so that it reflects the contents of + // the new list. + exception_url_to_index_map_.clear(); + for (size_t i = 0; i < password_exception_list.size(); i++) { + std::string key = password_manager::GetHumanReadableOrigin( + *password_exception_list[i], languages_); + exception_url_to_index_map_[key] = i; + } + + // Now, create a list of exceptions to send to observers. + current_exceptions_.clear(); + for (const autofill::PasswordForm* form : password_exception_list) { + current_exceptions_.push_back( + password_manager::GetHumanReadableOrigin(*form, languages_)); + } + + SendPasswordExceptionsList(); + + set_password_exception_list_called_ = true; + InitializeIfNecessary(); +} + +void PasswordsPrivateDelegateImpl::SendPasswordExceptionsList() { + observers_->Notify( + FROM_HERE, + &Observer::OnPasswordExceptionsListChanged, + current_exceptions_); +} + +#if !defined(OS_ANDROID) +gfx::NativeWindow PasswordsPrivateDelegateImpl::GetNativeWindow() const { + DCHECK(render_view_host_); + return content::WebContents::FromRenderViewHost(render_view_host_) + ->GetTopLevelNativeWindow(); +} +#endif + +void PasswordsPrivateDelegateImpl::Shutdown() { + password_manager_presenter_.reset(); +} + +void PasswordsPrivateDelegateImpl::ExecuteFunction( + const base::Callback<void()>& callback) { + if (is_initialized_) { + callback.Run(); + return; + } + + pre_initialization_callbacks_.push_back(callback); +} + +void PasswordsPrivateDelegateImpl::InitializeIfNecessary() { + if (is_initialized_ || + !set_password_list_called_ || + !set_password_exception_list_called_) + return; + + is_initialized_ = true; + + for (const base::Callback<void()>& callback : pre_initialization_callbacks_) { + callback.Run(); + } +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h new file mode 100644 index 0000000..5d94a63 --- /dev/null +++ b/chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h @@ -0,0 +1,136 @@ +// 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 CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_DELEGATE_IMPL_H_ +#define CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_DELEGATE_IMPL_H_ + +#include <map> +#include <string> +#include <vector> + +#include "base/callback.h" +#include "base/macros.h" +#include "base/memory/scoped_ptr.h" +#include "base/observer_list_threadsafe.h" +#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h" +#include "chrome/browser/ui/passwords/password_manager_presenter.h" +#include "chrome/browser/ui/passwords/password_ui_view.h" +#include "chrome/common/extensions/api/passwords_private.h" +#include "components/keyed_service/core/keyed_service.h" +#include "extensions/browser/extension_function.h" + +class Profile; + +namespace content { +class RenderViewHost; +} + +namespace extensions { + +// Concrete PasswordsPrivateDelegate implementation. +class PasswordsPrivateDelegateImpl : public PasswordsPrivateDelegate, + public PasswordUIView { + public: + explicit PasswordsPrivateDelegateImpl(Profile* profile); + ~PasswordsPrivateDelegateImpl() override; + + // PasswordsPrivateDelegate implementation. + void AddObserver(Observer* observer) override; + void RemoveObserver(Observer* observer) override; + void RemoveSavedPassword( + const std::string& origin_url, const std::string& username) override; + void RemovePasswordException(const std::string& exception_url) override; + void RequestShowPassword( + const std::string& origin_url, + const std::string& username, + const content::RenderViewHost* render_view_host) override; + + // PasswordUIView implementation. + Profile* GetProfile() override; + void ShowPassword( + size_t index, + const std::string& origin_url, + const std::string& username, + const base::string16& plaintext_password) override; + void SetPasswordList( + const ScopedVector<autofill::PasswordForm>& password_list, + bool show_passwords) override; + void SetPasswordExceptionList(const ScopedVector<autofill::PasswordForm>& + password_exception_list) override; +#if !defined(OS_ANDROID) + gfx::NativeWindow GetNativeWindow() const override; +#endif + + // KeyedService overrides: + void Shutdown() override; + + private: + // Called after the lists are fetched. Once both lists have been set, the + // class is considered initialized and any queued functions (which could + // not be executed immediately due to uninitialized data) are invoked. + void InitializeIfNecessary(); + + // Executes a given callback by either invoking it immediately if the class + // has been initialized or by deferring it until initialization has completed. + void ExecuteFunction(const base::Callback<void()>& callback); + + void RemoveSavedPasswordInternal( + const std::string& origin_url, const std::string& username); + void RemovePasswordExceptionInternal(const std::string& exception_url); + void RequestShowPasswordInternal( + const std::string& origin_url, + const std::string& username, + const content::RenderViewHost* render_view_host); + void SendSavedPasswordsList(); + void SendPasswordExceptionsList(); + + // Not owned by this class. + Profile* profile_; + + // Used to communicate with the password store. + scoped_ptr<PasswordManagerPresenter> password_manager_presenter_; + + // The current list of entries/exceptions. Cached here so that when new + // observers are added, this delegate can send the current lists without + // having to request them from |password_manager_presenter_| again. + std::vector<linked_ptr<api::passwords_private::PasswordUiEntry>> + current_entries_; + std::vector<std::string> current_exceptions_; + + // Whether SetPasswordList and SetPasswordExceptionList have been called, and + // whether this class has been initialized, meaning both have been called. + bool set_password_list_called_; + bool set_password_exception_list_called_; + bool is_initialized_; + + // Vector of callbacks which are queued up before the password store has been + // initialized. Once both SetPasswordList() and SetPasswordExceptionList() + // have been called, this class is considered initialized and can these + // callbacks are invoked. + std::vector<base::Callback<void()>> pre_initialization_callbacks_; + + // User pref for storing accept languages. + std::string languages_; + + // The RenderViewHost used when invoking this API. Used to fetch the + // NativeWindow for the window where the API was called. + const content::RenderViewHost* render_view_host_; + + // The observers. + scoped_refptr<base::ObserverListThreadSafe<Observer>> observers_; + + // Map from origin URL and username to the index of |password_list_| at which + // the corresponding entry resides. + std::map<std::string, size_t> login_pair_to_index_map_; + + // Map from password exception URL to the index of |password_exception_list_| + // at which the correponding entry resides. + std::map<std::string, size_t> exception_url_to_index_map_; + + DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateDelegateImpl); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_DELEGATE_IMPL_H_ diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.cc new file mode 100644 index 0000000..23f0f7f --- /dev/null +++ b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.cc @@ -0,0 +1,155 @@ +// 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. + +#include "chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h" + +#include <vector> + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/strings/utf_string_conversions.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/common/extensions/api/passwords_private.h" +#include "content/public/browser/browser_context.h" + +namespace extensions { + +PasswordsPrivateEventRouter::PasswordsPrivateEventRouter( + content::BrowserContext* context) + : context_(context), + event_router_(nullptr), + listening_(false) { + // Register with the event router so we know when renderers are listening to + // our events. We first check and see if there *is* an event router, because + // some unit tests try to create all context services, but don't initialize + // the event router first. + event_router_ = EventRouter::Get(context_); + if (!event_router_) + return; + + event_router_->RegisterObserver( + this, + api::passwords_private::OnSavedPasswordsListChanged::kEventName); + event_router_->RegisterObserver( + this, + api::passwords_private::OnPasswordExceptionsListChanged::kEventName); + event_router_->RegisterObserver( + this, + api::passwords_private::OnPlaintextPasswordRetrieved::kEventName); +} + +PasswordsPrivateEventRouter::~PasswordsPrivateEventRouter() {} + +void PasswordsPrivateEventRouter::Shutdown() { + if (event_router_) + event_router_->UnregisterObserver(this); + + PasswordsPrivateDelegate* delegate = + PasswordsPrivateDelegateFactory::GetForBrowserContext(context_); + delegate->RemoveObserver(this); +} + +void PasswordsPrivateEventRouter::OnListenerAdded( + const EventListenerInfo& details) { + // Start listening to change events and propagate the original lists to + // listeners. + StartOrStopListeningForChanges(); + SendSavedPasswordListToListeners(); + SendPasswordExceptionListToListeners(); +} + +void PasswordsPrivateEventRouter::OnListenerRemoved( + const EventListenerInfo& details) { + // Stop listening to events if there are no more listeners. + StartOrStopListeningForChanges(); +} + +void PasswordsPrivateEventRouter::OnSavedPasswordsListChanged( + const std::vector<linked_ptr< + api::passwords_private::PasswordUiEntry>>& entries) { + cached_saved_password_parameters_ = + api::passwords_private::OnSavedPasswordsListChanged::Create(entries); + SendSavedPasswordListToListeners(); +} + +void PasswordsPrivateEventRouter::SendSavedPasswordListToListeners() { + if (!cached_saved_password_parameters_.get()) + // If there is nothing to send, return early. + return; + + scoped_ptr<Event> extension_event(new Event( + api::passwords_private::OnSavedPasswordsListChanged::kEventName, + cached_saved_password_parameters_->CreateDeepCopy())); + event_router_->BroadcastEvent(extension_event.Pass()); +} + +void PasswordsPrivateEventRouter::OnPasswordExceptionsListChanged( + const std::vector<std::string>& exceptions) { + cached_password_exception_parameters_ = + api::passwords_private::OnPasswordExceptionsListChanged::Create( + exceptions); + SendPasswordExceptionListToListeners(); +} + +void PasswordsPrivateEventRouter::SendPasswordExceptionListToListeners() { + if (!cached_password_exception_parameters_.get()) + // If there is nothing to send, return early. + return; + + scoped_ptr<Event> extension_event(new Event( + api::passwords_private::OnPasswordExceptionsListChanged::kEventName, + cached_password_exception_parameters_->CreateDeepCopy())); + event_router_->BroadcastEvent(extension_event.Pass()); +} + +void PasswordsPrivateEventRouter::OnPlaintextPasswordFetched( + const std::string& origin_url, + const std::string& username, + const std::string& plaintext_password) { + api::passwords_private::PlaintextPasswordEventParameters params; + params.login_pair.origin_url = origin_url; + params.login_pair.username = username; + params.plaintext_password = plaintext_password; + + scoped_ptr<base::ListValue> event_value(new base::ListValue); + event_value->Append(params.ToValue()); + + scoped_ptr<Event> extension_event(new Event( + api::passwords_private::OnPlaintextPasswordRetrieved::kEventName, + event_value.Pass())); + event_router_->BroadcastEvent(extension_event.Pass()); +} + +void PasswordsPrivateEventRouter::StartOrStopListeningForChanges() { + bool should_listen_for_saved_password_changes = + event_router_->HasEventListener( + api::passwords_private::OnSavedPasswordsListChanged::kEventName); + bool should_listen_for_password_exception_changes = + event_router_->HasEventListener( + api::passwords_private::OnPasswordExceptionsListChanged::kEventName); + bool should_listen_for_plaintext_password_retrieval = + event_router_->HasEventListener( + api::passwords_private::OnPlaintextPasswordRetrieved::kEventName); + bool should_listen = should_listen_for_saved_password_changes || + should_listen_for_password_exception_changes || + should_listen_for_plaintext_password_retrieval; + + PasswordsPrivateDelegate* delegate = + PasswordsPrivateDelegateFactory::GetForBrowserContext(context_); + if (should_listen && !listening_) + delegate->AddObserver(this); + else if (!should_listen && listening_) + delegate->RemoveObserver(this); + + listening_ = should_listen; +} + +PasswordsPrivateEventRouter* PasswordsPrivateEventRouter::Create( + content::BrowserContext* context) { + return new PasswordsPrivateEventRouter(context); +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h new file mode 100644 index 0000000..188f1d1 --- /dev/null +++ b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h @@ -0,0 +1,76 @@ +// 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 CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_EVENT_ROUTER_H_ +#define CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_EVENT_ROUTER_H_ + +#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h" +#include "components/keyed_service/core/keyed_service.h" +#include "extensions/browser/event_router.h" + +namespace content { +class BrowserContext; +} + +namespace extensions { + +// An event router that observes changes to saved passwords and password +// exceptions and notifies listeners to the onSavedPasswordsListChanged and +// onPasswordExceptionsListChanged events of changes. +class PasswordsPrivateEventRouter : + public KeyedService, + public EventRouter::Observer, + public PasswordsPrivateDelegate::Observer { + public: + static PasswordsPrivateEventRouter* Create( + content::BrowserContext* browser_context); + ~PasswordsPrivateEventRouter() override; + + protected: + explicit PasswordsPrivateEventRouter(content::BrowserContext* context); + + // KeyedService overrides: + void Shutdown() override; + + // EventRouter::Observer overrides: + void OnListenerAdded(const EventListenerInfo& details) override; + void OnListenerRemoved(const EventListenerInfo& details) override; + + // PasswordsPrivateDelegate::Observer overrides: + void OnSavedPasswordsListChanged(const std::vector<linked_ptr< + api::passwords_private::PasswordUiEntry>>& entries) override; + void OnPasswordExceptionsListChanged( + const std::vector<std::string>& exceptions) override; + void OnPlaintextPasswordFetched( + const std::string& origin_url, + const std::string& username, + const std::string& plaintext_password) override; + + private: + // Either listens or unlistens for changes to saved passwords, password + // exceptions, or the retrieval of plaintext passwords, depending on whether + // clients are listening to the passwordsPrivate API events. + void StartOrStopListeningForChanges(); + + void SendSavedPasswordListToListeners(); + void SendPasswordExceptionListToListeners(); + + content::BrowserContext* context_; + + EventRouter* event_router_; + + // Cached parameters which are saved so that when new listeners are added, the + // most up-to-date lists can be sent to them immediately. + scoped_ptr<base::ListValue> cached_saved_password_parameters_; + scoped_ptr<base::ListValue> cached_password_exception_parameters_; + + // Whether this class is currently listening for changes to password changes. + bool listening_; + + DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateEventRouter); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_EVENT_ROUTER_H_ diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.cc b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.cc new file mode 100644 index 0000000..af54105 --- /dev/null +++ b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.cc @@ -0,0 +1,62 @@ +// 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. + +#include "chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h" + +#include "chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h" +#include "chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h" +#include "components/keyed_service/content/browser_context_dependency_manager.h" +#include "content/public/browser/browser_context.h" +#include "extensions/browser/extension_system_provider.h" +#include "extensions/browser/extensions_browser_client.h" + +namespace extensions { + +// static +PasswordsPrivateEventRouter* +PasswordsPrivateEventRouterFactory::GetForProfile( + content::BrowserContext* context) { + return static_cast<PasswordsPrivateEventRouter*>( + GetInstance()->GetServiceForBrowserContext(context, true)); +} + +// static +PasswordsPrivateEventRouterFactory* +PasswordsPrivateEventRouterFactory::GetInstance() { + return Singleton<PasswordsPrivateEventRouterFactory>::get(); +} + +PasswordsPrivateEventRouterFactory::PasswordsPrivateEventRouterFactory() + : BrowserContextKeyedServiceFactory( + "PasswordsPrivateEventRouter", + BrowserContextDependencyManager::GetInstance()) { + DependsOn(ExtensionsBrowserClient::Get()->GetExtensionSystemFactory()); + DependsOn(PasswordsPrivateDelegateFactory::GetInstance()); +} + +PasswordsPrivateEventRouterFactory:: + ~PasswordsPrivateEventRouterFactory() { +} + +KeyedService* PasswordsPrivateEventRouterFactory::BuildServiceInstanceFor( + content::BrowserContext* context) const { + return PasswordsPrivateEventRouter::Create(context); +} + +content::BrowserContext* +PasswordsPrivateEventRouterFactory::GetBrowserContextToUse( + content::BrowserContext* context) const { + return ExtensionsBrowserClient::Get()->GetOriginalContext(context); +} + +bool PasswordsPrivateEventRouterFactory:: + ServiceIsCreatedWithBrowserContext() const { + return true; +} + +bool PasswordsPrivateEventRouterFactory::ServiceIsNULLWhileTesting() const { + return true; +} + +} // namespace extensions diff --git a/chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h new file mode 100644 index 0000000..c9c03a7 --- /dev/null +++ b/chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h @@ -0,0 +1,51 @@ +// 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 CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_EVENT_ROUTER_FACTORY_H_ +#define CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_EVENT_ROUTER_FACTORY_H_ + +#include "base/memory/singleton.h" +#include "components/keyed_service/content/browser_context_keyed_service_factory.h" + +namespace extensions { + +class PasswordsPrivateEventRouter; + +// This is a factory class used by the BrowserContextDependencyManager +// to instantiate the passwordsPrivate event router per profile (since the +// extension event router is per profile). +class PasswordsPrivateEventRouterFactory + : public BrowserContextKeyedServiceFactory { + public: + // Returns the PasswordsPrivateEventRouter for |profile|, creating it if + // it is not yet created. + static PasswordsPrivateEventRouter* GetForProfile( + content::BrowserContext* context); + + // Returns the PasswordsPrivateEventRouterFactory instance. + static PasswordsPrivateEventRouterFactory* GetInstance(); + + protected: + // BrowserContextKeyedBaseFactory overrides: + content::BrowserContext* GetBrowserContextToUse( + content::BrowserContext* context) const override; + bool ServiceIsCreatedWithBrowserContext() const override; + bool ServiceIsNULLWhileTesting() const override; + + private: + friend struct DefaultSingletonTraits<PasswordsPrivateEventRouterFactory>; + + PasswordsPrivateEventRouterFactory(); + ~PasswordsPrivateEventRouterFactory() override; + + // BrowserContextKeyedServiceFactory: + KeyedService* BuildServiceInstanceFor( + content::BrowserContext* profile) const override; + + DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateEventRouterFactory); +}; + +} // namespace extensions + +#endif // CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_EVENT_ROUTER_FACTORY_H_ diff --git a/chrome/browser/extensions/browser_context_keyed_service_factories.cc b/chrome/browser/extensions/browser_context_keyed_service_factories.cc index 88bbd94..b2cfdf8 100644 --- a/chrome/browser/extensions/browser_context_keyed_service_factories.cc +++ b/chrome/browser/extensions/browser_context_keyed_service_factories.cc @@ -26,6 +26,7 @@ #include "chrome/browser/extensions/api/location/location_manager.h" #include "chrome/browser/extensions/api/mdns/mdns_api.h" #include "chrome/browser/extensions/api/omnibox/omnibox_api.h" +#include "chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h" #include "chrome/browser/extensions/api/preference/chrome_direct_setting_api.h" #include "chrome/browser/extensions/api/preference/preference_api.h" #include "chrome/browser/extensions/api/processes/processes_api.h" @@ -111,6 +112,7 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt() { #endif extensions::MenuManagerFactory::GetInstance(); extensions::OmniboxAPI::GetFactoryInstance(); + extensions::PasswordsPrivateEventRouterFactory::GetInstance(); #if defined(ENABLE_PLUGINS) extensions::PluginManager::GetFactoryInstance(); #endif // defined(ENABLE_PLUGINS) diff --git a/chrome/browser/ui/passwords/password_manager_presenter.cc b/chrome/browser/ui/passwords/password_manager_presenter.cc index e25c0bf..5b468a0 100644 --- a/chrome/browser/ui/passwords/password_manager_presenter.cc +++ b/chrome/browser/ui/passwords/password_manager_presenter.cc @@ -14,10 +14,13 @@ #include "chrome/browser/password_manager/password_store_factory.h" #include "chrome/browser/password_manager/sync_metrics.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/ui/passwords/manage_passwords_view_utils.h" #include "chrome/browser/ui/passwords/password_ui_view.h" #include "chrome/common/chrome_switches.h" +#include "chrome/common/pref_names.h" #include "chrome/common/url_constants.h" #include "components/autofill/core/common/password_form.h" +#include "components/password_manager/core/browser/affiliation_utils.h" #include "components/password_manager/core/browser/password_manager_util.h" #include "components/password_manager/core/common/password_manager_pref_names.h" #include "content/public/browser/user_metrics.h" @@ -60,6 +63,9 @@ void PasswordManagerPresenter::Initialize() { PasswordStore* store = GetPasswordStore(); if (store) store->AddObserver(this); + + languages_ = password_view_->GetProfile()->GetPrefs()-> + GetString(prefs::kAcceptLanguages); } void PasswordManagerPresenter::OnLoginsChanged( @@ -141,7 +147,13 @@ void PasswordManagerPresenter::RequestShowPassword(size_t index) { } // Call back the front end to reveal the password. - password_view_->ShowPassword(index, password_list_[index]->password_value); + std::string origin_url = password_manager::GetHumanReadableOrigin( + *password_list_[index], languages_); + password_view_->ShowPassword( + index, + origin_url, + base::UTF16ToUTF8(password_list_[index]->username_value), + password_list_[index]->password_value); #endif } diff --git a/chrome/browser/ui/passwords/password_manager_presenter.h b/chrome/browser/ui/passwords/password_manager_presenter.h index e3d7922..2997830 100644 --- a/chrome/browser/ui/passwords/password_manager_presenter.h +++ b/chrome/browser/ui/passwords/password_manager_presenter.h @@ -132,6 +132,9 @@ class PasswordManagerPresenter // UI view that owns this presenter. PasswordUIView* password_view_; + // User pref for storing accept languages. + std::string languages_; + DISALLOW_COPY_AND_ASSIGN(PasswordManagerPresenter); }; diff --git a/chrome/browser/ui/passwords/password_manager_presenter_unittest.cc b/chrome/browser/ui/passwords/password_manager_presenter_unittest.cc index 9669bb0..af7a87d 100644 --- a/chrome/browser/ui/passwords/password_manager_presenter_unittest.cc +++ b/chrome/browser/ui/passwords/password_manager_presenter_unittest.cc @@ -28,7 +28,8 @@ class MockPasswordUIView : public PasswordUIView { #if !defined(OS_ANDROID) gfx::NativeWindow GetNativeWindow() const override; #endif - MOCK_METHOD2(ShowPassword, void(size_t, const base::string16&)); + MOCK_METHOD4(ShowPassword, void( + size_t, const std::string&, const std::string&, const base::string16&)); MOCK_METHOD2(SetPasswordList, void(const ScopedVector<autofill::PasswordForm>&, bool)); MOCK_METHOD1(SetPasswordExceptionList, diff --git a/chrome/browser/ui/passwords/password_ui_view.h b/chrome/browser/ui/passwords/password_ui_view.h index 0420220..3bc7732 100644 --- a/chrome/browser/ui/passwords/password_ui_view.h +++ b/chrome/browser/ui/passwords/password_ui_view.h @@ -24,10 +24,15 @@ class PasswordUIView { // Returns the profile associated with the currently active profile. virtual Profile* GetProfile() = 0; - // Reveals the password for the saved password entry at |index| in the UI. + // Reveals the password for the saved password entry. // |index| the index of the saved password entry. + // |origin_url| the URL of the saved password entry; obtained via + // GetHumanReadableOrigin(). + // |username| the username of the saved password entry. // |password_value| the value of saved password entry at |index|. virtual void ShowPassword(size_t index, + const std::string& origin_url, + const std::string& username, const base::string16& password_value) = 0; // Updates the list of passwords in the UI. diff --git a/chrome/browser/ui/webui/options/password_manager_handler.cc b/chrome/browser/ui/webui/options/password_manager_handler.cc index e9cfe77..2b10eb8 100644 --- a/chrome/browser/ui/webui/options/password_manager_handler.cc +++ b/chrome/browser/ui/webui/options/password_manager_handler.cc @@ -184,6 +184,8 @@ void PasswordManagerHandler::HandleRequestShowPassword( void PasswordManagerHandler::ShowPassword( size_t index, + const std::string& origin_url, + const std::string& username, const base::string16& password_value) { // Call back the front end to reveal the password. web_ui()->CallJavascriptFunction( diff --git a/chrome/browser/ui/webui/options/password_manager_handler.h b/chrome/browser/ui/webui/options/password_manager_handler.h index a3dbd11..5fa0847d 100644 --- a/chrome/browser/ui/webui/options/password_manager_handler.h +++ b/chrome/browser/ui/webui/options/password_manager_handler.h @@ -30,8 +30,11 @@ class PasswordManagerHandler : public OptionsPageUIHandler, // PasswordUIView implementation. Profile* GetProfile() override; - void ShowPassword(size_t index, - const base::string16& password_value) override; + void ShowPassword( + size_t index, + const std::string& origin_url, + const std::string& username, + const base::string16& password_value) override; void SetPasswordList( const ScopedVector<autofill::PasswordForm>& password_list, bool show_passwords) override; diff --git a/chrome/chrome_browser_extensions.gypi b/chrome/chrome_browser_extensions.gypi index 37929c0..aa1ee420 100644 --- a/chrome/chrome_browser_extensions.gypi +++ b/chrome/chrome_browser_extensions.gypi @@ -380,6 +380,15 @@ 'browser/extensions/api/page_capture/page_capture_api.h', 'browser/extensions/api/passwords_private/passwords_private_api.cc', 'browser/extensions/api/passwords_private/passwords_private_api.h', + 'browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc', + 'browser/extensions/api/passwords_private/passwords_private_delegate_impl.h', + 'browser/extensions/api/passwords_private/passwords_private_delegate.h', + 'browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc', + 'browser/extensions/api/passwords_private/passwords_private_delegate_factory.h', + 'browser/extensions/api/passwords_private/passwords_private_event_router.cc', + 'browser/extensions/api/passwords_private/passwords_private_event_router.h', + 'browser/extensions/api/passwords_private/passwords_private_event_router_factory.cc', + 'browser/extensions/api/passwords_private/passwords_private_event_router_factory.h', 'browser/extensions/api/permissions/permissions_api.cc', 'browser/extensions/api/permissions/permissions_api.h', 'browser/extensions/api/permissions/permissions_api_helpers.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index ef05217..84f12f1 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -181,6 +181,7 @@ 'browser/extensions/api/notification_provider/notification_provider_apitest.cc', 'browser/extensions/api/omnibox/omnibox_api_browsertest.cc', 'browser/extensions/api/page_capture/page_capture_apitest.cc', + 'browser/extensions/api/passwords_private/passwords_private_apitest.cc', 'browser/extensions/api/permissions/permissions_apitest.cc', 'browser/extensions/api/platform_keys/platform_keys_apitest_nss.cc', 'browser/extensions/api/preference/preference_apitest.cc', diff --git a/chrome/common/extensions/api/passwords_private.idl b/chrome/common/extensions/api/passwords_private.idl index 7dea8b9..43855bc 100644 --- a/chrome/common/extensions/api/passwords_private.idl +++ b/chrome/common/extensions/api/passwords_private.idl @@ -27,8 +27,16 @@ namespace passwordsPrivate { DOMString? federationText; }; + // Dictionary passed to listeners for the onPlaintextPasswordRetrieved event. + dictionary PlaintextPasswordEventParameters { + // The LoginPair associated with the retrieved password. + LoginPair loginPair; + + // The password in plaintext. + DOMString plaintextPassword; + }; + callback CanAccountBeManagedCallback = void(boolean canAccountBeManaged); - callback PlaintextPasswordCallback = void(DOMString plaintextPassword); interface Functions { // Determines whether account's passwords can be managed via @@ -53,13 +61,12 @@ namespace passwordsPrivate { // Returns the plaintext password corresponding to |loginPair|. Note that on // some operating systems, this call may result in an OS-level - // reauthentication. + // reauthentication. Once the password has been fetched, it will be returned + // via the onPlaintextPasswordRetrieved event. // // |loginPair|: The LoginPair corresponding to the entry whose password // is to be returned. - // |callback|: Callback which will be passed the plaintext password. - static void getPlaintextPassword( - LoginPair loginPair, PlaintextPasswordCallback callback); + static void requestPlaintextPassword(LoginPair loginPair); }; interface Events { @@ -72,9 +79,17 @@ namespace passwordsPrivate { // Fired when the password exceptions list has changed, meaning that an // entry has been added or removed. Note that this event fires as soon as a - // listener is added. + // listener is added. // // |exceptions|: The updated list of password exceptions. static void onPasswordExceptionsListChanged(DOMString[] exceptions); + + // Fired when a plaintext password has been fetched in response to a call to + // chrome.passwordsPrivate.requestPlaintextPassword(). + // + // |loginPair|: The LoginPair whose password was found. + // |plaintextPassword|: The plaintext password which was retrieved. + static void onPlaintextPasswordRetrieved( + PlaintextPasswordEventParameters dict); }; }; diff --git a/chrome/test/data/extensions/api_test/passwords_private/main.html b/chrome/test/data/extensions/api_test/passwords_private/main.html new file mode 100644 index 0000000..040b1ff --- /dev/null +++ b/chrome/test/data/extensions/api_test/passwords_private/main.html @@ -0,0 +1,11 @@ +<!-- + * 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. +--> +<script src="test.js"></script> + +<html> +<head><title>passwordsPrivate component API interface test</title></head> +<body><h2>chrome.passwordsPrivate.* tests</h2></body> +</html>
\ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/passwords_private/manifest.json b/chrome/test/data/extensions/api_test/passwords_private/manifest.json new file mode 100644 index 0000000..76112f7 --- /dev/null +++ b/chrome/test/data/extensions/api_test/passwords_private/manifest.json @@ -0,0 +1,10 @@ +{ + "key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC74Vbx3EbhPc/FOvn6+HxCjMSml0HdPMiuRjj5a3b+MnRML1iJ9OAgbKUYJ/u3s25/cGq8pNB0NbyupHGEqvqAE7TcNr1mdgs0PWxh2IOI1GKrxlzxpqzQuFmxq5WHKr5RrwZ4/Xq0t/+e8JkvhZdW0jarz/28Jom0gkM5lorsewIDAQAB", + "name": "passwordsPrivate API interface test", + "version": "0.1", + "manifest_version": 2, + "description": "Test of chrome.passwordsPrivate interface", + "permissions": [ + "passwordsPrivate" + ] +}
\ No newline at end of file diff --git a/chrome/test/data/extensions/api_test/passwords_private/test.js b/chrome/test/data/extensions/api_test/passwords_private/test.js new file mode 100644 index 0000000..d0e8d7c --- /dev/null +++ b/chrome/test/data/extensions/api_test/passwords_private/test.js @@ -0,0 +1,81 @@ +// 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. + +// This just tests the interface. It does not test for specific results, only +// that callbacks are correctly invoked, expected parameters are correct, +// and failures are detected. + +var availableTests = [ + function canPasswordAccountBeManaged() { + var callback = function() { + // Ensure that the callback is invoked. + chrome.test.succeed(); + }; + + chrome.passwordsPrivate.canPasswordAccountBeManaged(callback); + }, + + function removeSavedPassword() { + var numCalls = 0; + var numSavedPasswords; + var callback = function(savedPasswordsList) { + numCalls++; + + if (numCalls == 1) { + numSavedPasswords = savedPasswordsList.length; + chrome.passwordsPrivate.removeSavedPassword({ + originUrl: savedPasswordsList[0].loginPair.originUrl, + username: savedPasswordsList[0].loginPair.username + }); + } else if (numCalls == 2) { + chrome.test.assertEq( + savedPasswordsList.length, numSavedPasswords - 1); + chrome.test.succeed(); + } else { + chrome.test.fail(); + } + }; + + chrome.passwordsPrivate.onSavedPasswordsListChanged.addListener(callback); + }, + + function removePasswordException() { + var numCalls = 0; + var numPasswordExceptions; + var callback = function(passwordExceptionsList) { + numCalls++; + + if (numCalls == 1) { + numPasswordExceptions = passwordExceptionsList.length; + chrome.passwordsPrivate.removePasswordException( + passwordExceptionsList[0]); + } else if (numCalls == 2) { + chrome.test.assertEq( + passwordExceptionsList.length, numPasswordExceptions - 1); + chrome.test.succeed(); + } else { + chrome.test.fail(); + } + }; + + chrome.passwordsPrivate.onPasswordExceptionsListChanged.addListener( + callback); + }, + + function requestPlaintextPassword() { + var callback = function() { + // Ensure that the callback is invoked. + chrome.test.succeed(); + }; + + chrome.passwordsPrivate.onPlaintextPasswordRetrieved.addListener(callback); + chrome.passwordsPrivate.requestPlaintextPassword( + {originUrl: 'http://www.test.com', username: 'test@test.com'}); + }, +]; + +var testToRun = window.location.search.substring(1); +chrome.test.runTests(availableTests.filter(function(op) { + return op.name == testToRun; +})); diff --git a/extensions/browser/extension_function_histogram_value.h b/extensions/browser/extension_function_histogram_value.h index 924cd1d..2ec0b34 100644 --- a/extensions/browser/extension_function_histogram_value.h +++ b/extensions/browser/extension_function_histogram_value.h @@ -1097,7 +1097,7 @@ enum HistogramValue { PASSWORDSPRIVATE_CANPASSWORDACCOUNTBEMANAGED, PASSWORDSPRIVATE_REMOVESAVEDPASSWORD, PASSWORDSPRIVATE_REMOVEPASSWORDEXCEPTION, - PASSWORDSPRIVATE_GETPLAINTEXTPASSWORD, + PASSWORDSPRIVATE_REQUESTPLAINTEXTPASSWORD, LAUNCHERPAGE_HIDE, PLATFORMKEYS_VERIFYTLSSERVERCERTIFICATE, DEVELOPERPRIVATE_SETSHORTCUTHANDLINGSUSPENDED, diff --git a/third_party/closure_compiler/externs/passwords_private.js b/third_party/closure_compiler/externs/passwords_private.js index 94a528b..0679980 100644 --- a/third_party/closure_compiler/externs/passwords_private.js +++ b/third_party/closure_compiler/externs/passwords_private.js @@ -29,6 +29,15 @@ var LoginPair; var PasswordUiEntry; /** + * @typedef {{ + * loginPair: LoginPair, + * plaintextPassword: string + * }} + * @see https://developer.chrome.com/extensions/passwordsPrivate#type-PlaintextPasswordEventParameters + */ +var PlaintextPasswordEventParameters; + +/** * Determines whether account's passwords can be managed via * https://passwords.google.com/settings/passwords. * @param {function(boolean):void} callback Callback which will be passed the @@ -58,13 +67,13 @@ chrome.passwordsPrivate.removePasswordException = function(exceptionUrl) {}; /** * Returns the plaintext password corresponding to |loginPair|. Note that on * some operating systems, this call may result in an OS-level reauthentication. + * Once the password has been fetched, it will be returned via the + * onPlaintextPasswordRetrieved event. * @param {LoginPair} loginPair The LoginPair corresponding to the entry whose * password is to be returned. - * @param {function(string):void} callback Callback which will be passed the - * plaintext password. - * @see https://developer.chrome.com/extensions/passwordsPrivate#method-getPlaintextPassword + * @see https://developer.chrome.com/extensions/passwordsPrivate#method-requestPlaintextPassword */ -chrome.passwordsPrivate.getPlaintextPassword = function(loginPair, callback) {}; +chrome.passwordsPrivate.requestPlaintextPassword = function(loginPair) {}; /** * Fired when the saved passwords list has changed, meaning that an entry has @@ -77,11 +86,17 @@ chrome.passwordsPrivate.onSavedPasswordsListChanged; /** * Fired when the password exceptions list has changed, meaning that an entry - * has been added or removed. Note that this event fires as soon as a listener + * has been added or removed. Note that this event fires as soon as a listener * is added. * @type {!ChromeEvent} * @see https://developer.chrome.com/extensions/passwordsPrivate#event-onPasswordExceptionsListChanged */ chrome.passwordsPrivate.onPasswordExceptionsListChanged; - +/** + * Fired when a plaintext password has been fetched in response to a call to + * chrome.passwordsPrivate.requestPlaintextPassword(). + * @type {!ChromeEvent} + * @see https://developer.chrome.com/extensions/passwordsPrivate#event-onPlaintextPasswordRetrieved + */ +chrome.passwordsPrivate.onPlaintextPasswordRetrieved; diff --git a/tools/metrics/histograms/histograms.xml b/tools/metrics/histograms/histograms.xml index 0fb09b9..da8aba4d 100644 --- a/tools/metrics/histograms/histograms.xml +++ b/tools/metrics/histograms/histograms.xml @@ -54391,7 +54391,7 @@ http://cs/file:chrome/histograms.xml - but prefer this file for new entries. <int value="1036" label="PASSWORDSPRIVATE_CANPASSWORDACCOUNTBEMANAGED"/> <int value="1037" label="PASSWORDSPRIVATE_REMOVESAVEDPASSWORD"/> <int value="1038" label="PASSWORDSPRIVATE_REMOVEPASSWORDEXCEPTION"/> - <int value="1039" label="PASSWORDSPRIVATE_GETPLAINTEXTPASSWORD"/> + <int value="1039" label="PASSWORDSPRIVATE_REQUESTPLAINTEXTPASSWORD"/> <int value="1040" label="LAUNCHERPAGE_HIDE"/> <int value="1041" label="PLATFORMKEYS_VERIFYTLSSERVERCERTIFICATE"/> <int value="1042" label="DEVELOPERPRIVATE_SETSHORTCUTHANDLINGSUSPENDED"/> |