summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorkhorimoto <khorimoto@chromium.org>2015-06-12 20:31:01 -0700
committerCommit bot <commit-bot@chromium.org>2015-06-13 03:32:25 +0000
commit03daaabaec0b367ca2a61ecebc33572ea79e4cf9 (patch)
tree0d286270ddab9d74c07cce5c362f08630f74a5e2
parent6f840fc5d6482ef18945e501f729f8eb1c954f84 (diff)
downloadchromium_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}
-rw-r--r--chrome/browser/android/password_ui_view_android.cc5
-rw-r--r--chrome/browser/android/password_ui_view_android.h7
-rw-r--r--chrome/browser/extensions/api/passwords_private/passwords_private_api.cc41
-rw-r--r--chrome/browser/extensions/api/passwords_private/passwords_private_api.h14
-rw-r--r--chrome/browser/extensions/api/passwords_private/passwords_private_apitest.cc198
-rw-r--r--chrome/browser/extensions/api/passwords_private/passwords_private_delegate.h95
-rw-r--r--chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.cc53
-rw-r--r--chrome/browser/extensions/api/passwords_private/passwords_private_delegate_factory.h44
-rw-r--r--chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.cc263
-rw-r--r--chrome/browser/extensions/api/passwords_private/passwords_private_delegate_impl.h136
-rw-r--r--chrome/browser/extensions/api/passwords_private/passwords_private_event_router.cc155
-rw-r--r--chrome/browser/extensions/api/passwords_private/passwords_private_event_router.h76
-rw-r--r--chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.cc62
-rw-r--r--chrome/browser/extensions/api/passwords_private/passwords_private_event_router_factory.h51
-rw-r--r--chrome/browser/extensions/browser_context_keyed_service_factories.cc2
-rw-r--r--chrome/browser/ui/passwords/password_manager_presenter.cc14
-rw-r--r--chrome/browser/ui/passwords/password_manager_presenter.h3
-rw-r--r--chrome/browser/ui/passwords/password_manager_presenter_unittest.cc3
-rw-r--r--chrome/browser/ui/passwords/password_ui_view.h7
-rw-r--r--chrome/browser/ui/webui/options/password_manager_handler.cc2
-rw-r--r--chrome/browser/ui/webui/options/password_manager_handler.h7
-rw-r--r--chrome/chrome_browser_extensions.gypi9
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--chrome/common/extensions/api/passwords_private.idl27
-rw-r--r--chrome/test/data/extensions/api_test/passwords_private/main.html11
-rw-r--r--chrome/test/data/extensions/api_test/passwords_private/manifest.json10
-rw-r--r--chrome/test/data/extensions/api_test/passwords_private/test.js81
-rw-r--r--extensions/browser/extension_function_histogram_value.h2
-rw-r--r--third_party/closure_compiler/externs/passwords_private.js27
-rw-r--r--tools/metrics/histograms/histograms.xml2
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"/>