Commit bc0d25e5 authored by Nathanael Alcock's avatar Nathanael Alcock Committed by Commit Bot

Add password import/export buttons in MD Password Manager settings

This change reimplements the import/export functionality to the PM
settings as it existed in the previous version, before the Material
redesign.

Bug: 674536
Cq-Include-Trybots: master.tryserver.chromium.linux:closure_compilation
Change-Id: I686dc1152e95bc74ad21c3137b5aebf226801aed
Reviewed-on: https://chromium-review.googlesource.com/663380
Commit-Queue: Nathanael Alcock <varkor@google.com>
Reviewed-by: default avatarKen Rockot <rockot@chromium.org>
Reviewed-by: default avatarMichael Wasserman <msw@chromium.org>
Reviewed-by: default avatarSteven Bennetts <stevenjb@chromium.org>
Reviewed-by: default avatarIlya Sherman <isherman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#504372}
parent ea69a621
...@@ -150,4 +150,34 @@ void PasswordsPrivateGetPasswordExceptionListFunction::GotList( ...@@ -150,4 +150,34 @@ void PasswordsPrivateGetPasswordExceptionListFunction::GotList(
entries))); entries)));
} }
////////////////////////////////////////////////////////////////////////////////
// PasswordsPrivateImportPasswordsFunction
PasswordsPrivateImportPasswordsFunction::
~PasswordsPrivateImportPasswordsFunction() {}
ExtensionFunction::ResponseAction
PasswordsPrivateImportPasswordsFunction::Run() {
PasswordsPrivateDelegate* delegate =
PasswordsPrivateDelegateFactory::GetForBrowserContext(browser_context(),
true /* create */);
delegate->ImportPasswords(GetAssociatedWebContents());
return RespondNow(NoArguments());
}
////////////////////////////////////////////////////////////////////////////////
// PasswordsPrivateExportPasswordsFunction
PasswordsPrivateExportPasswordsFunction::
~PasswordsPrivateExportPasswordsFunction() {}
ExtensionFunction::ResponseAction
PasswordsPrivateExportPasswordsFunction::Run() {
PasswordsPrivateDelegate* delegate =
PasswordsPrivateDelegateFactory::GetForBrowserContext(browser_context(),
true /* create */);
delegate->ExportPasswords(GetAssociatedWebContents());
return RespondNow(NoArguments());
}
} // namespace extensions } // namespace extensions
...@@ -105,6 +105,40 @@ class PasswordsPrivateGetPasswordExceptionListFunction ...@@ -105,6 +105,40 @@ class PasswordsPrivateGetPasswordExceptionListFunction
DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateGetPasswordExceptionListFunction); DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateGetPasswordExceptionListFunction);
}; };
class PasswordsPrivateImportPasswordsFunction
: public UIThreadExtensionFunction {
public:
PasswordsPrivateImportPasswordsFunction() {}
DECLARE_EXTENSION_FUNCTION("passwordsPrivate.importPasswords",
PASSWORDSPRIVATE_IMPORTPASSWORDS);
protected:
~PasswordsPrivateImportPasswordsFunction() override;
// ExtensionFunction overrides.
ResponseAction Run() override;
private:
DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateImportPasswordsFunction);
};
class PasswordsPrivateExportPasswordsFunction
: public UIThreadExtensionFunction {
public:
PasswordsPrivateExportPasswordsFunction() {}
DECLARE_EXTENSION_FUNCTION("passwordsPrivate.exportPasswords",
PASSWORDSPRIVATE_EXPORTPASSWORDS);
protected:
~PasswordsPrivateExportPasswordsFunction() override;
// ExtensionFunction overrides.
ResponseAction Run() override;
private:
DISALLOW_COPY_AND_ASSIGN(PasswordsPrivateExportPasswordsFunction);
};
} // namespace extensions } // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_API_H_ #endif // CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_API_H_
...@@ -126,6 +126,22 @@ class TestDelegate : public PasswordsPrivateDelegate { ...@@ -126,6 +126,22 @@ class TestDelegate : public PasswordsPrivateDelegate {
void SetProfile(Profile* profile) { profile_ = profile; } void SetProfile(Profile* profile) { profile_ = profile; }
void ImportPasswords(content::WebContents* web_contents) override {
// The testing of password importing itself should be handled via
// |PasswordManagerPorter|.
importPasswordsTriggered = true;
}
void ExportPasswords(content::WebContents* web_contents) override {
// The testing of password exporting itself should be handled via
// |PasswordManagerPorter|.
exportPasswordsTriggered = true;
}
// Flags for detecting whether import/export operations have been invoked.
bool importPasswordsTriggered = false;
bool exportPasswordsTriggered = false;
private: private:
// The current list of entries/exceptions. Cached here so that when new // The current list of entries/exceptions. Cached here so that when new
// observers are added, this delegate can send the current lists without // observers are added, this delegate can send the current lists without
...@@ -173,6 +189,14 @@ class PasswordsPrivateApiTest : public ExtensionApiTest { ...@@ -173,6 +189,14 @@ class PasswordsPrivateApiTest : public ExtensionApiTest {
kFlagLoadAsComponent); kFlagLoadAsComponent);
} }
bool importPasswordsWasTriggered() {
return s_test_delegate_->importPasswordsTriggered;
}
bool exportPasswordsWasTriggered() {
return s_test_delegate_->exportPasswordsTriggered;
}
private: private:
static TestDelegate* s_test_delegate_; static TestDelegate* s_test_delegate_;
...@@ -204,4 +228,16 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, GetPasswordExceptionList) { ...@@ -204,4 +228,16 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, GetPasswordExceptionList) {
EXPECT_TRUE(RunPasswordsSubtest("getPasswordExceptionList")) << message_; EXPECT_TRUE(RunPasswordsSubtest("getPasswordExceptionList")) << message_;
} }
IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, ImportPasswords) {
EXPECT_FALSE(importPasswordsWasTriggered());
EXPECT_TRUE(RunPasswordsSubtest("importPasswords")) << message_;
EXPECT_TRUE(importPasswordsWasTriggered());
}
IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, ExportPasswords) {
EXPECT_FALSE(exportPasswordsWasTriggered());
EXPECT_TRUE(RunPasswordsSubtest("exportPasswords")) << message_;
EXPECT_TRUE(exportPasswordsWasTriggered());
}
} // namespace extensions } // namespace extensions
...@@ -67,6 +67,14 @@ class PasswordsPrivateDelegate : public KeyedService { ...@@ -67,6 +67,14 @@ class PasswordsPrivateDelegate : public KeyedService {
// an OS-level authentication dialog if necessary. // an OS-level authentication dialog if necessary.
virtual void RequestShowPassword(size_t index, virtual void RequestShowPassword(size_t index,
content::WebContents* web_contents) = 0; content::WebContents* web_contents) = 0;
// Trigger the password import procedure, allowing the user to select a file
// containing passwords to import.
virtual void ImportPasswords(content::WebContents* web_contents) = 0;
// Trigger the password export procedure, allowing the user to save a file
// containing their passwords.
virtual void ExportPasswords(content::WebContents* web_contents) = 0;
}; };
} // namespace extensions } // namespace extensions
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h" #include "components/password_manager/core/browser/android_affiliation/affiliation_utils.h"
#include "components/password_manager/core/browser/password_ui_utils.h" #include "components/password_manager/core/browser/password_ui_utils.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
...@@ -196,6 +197,16 @@ void PasswordsPrivateDelegateImpl::SetPasswordExceptionList( ...@@ -196,6 +197,16 @@ void PasswordsPrivateDelegateImpl::SetPasswordExceptionList(
get_password_exception_list_callbacks_.clear(); get_password_exception_list_callbacks_.clear();
} }
void PasswordsPrivateDelegateImpl::ImportPasswords(
content::WebContents* web_contents) {
password_manager_presenter_->ImportPasswords(web_contents);
}
void PasswordsPrivateDelegateImpl::ExportPasswords(
content::WebContents* web_contents) {
password_manager_presenter_->ExportPasswords(web_contents);
}
#if !defined(OS_ANDROID) #if !defined(OS_ANDROID)
gfx::NativeWindow PasswordsPrivateDelegateImpl::GetNativeWindow() const { gfx::NativeWindow PasswordsPrivateDelegateImpl::GetNativeWindow() const {
DCHECK(web_contents_); DCHECK(web_contents_);
......
...@@ -47,6 +47,8 @@ class PasswordsPrivateDelegateImpl : public PasswordsPrivateDelegate, ...@@ -47,6 +47,8 @@ class PasswordsPrivateDelegateImpl : public PasswordsPrivateDelegate,
void RemovePasswordException(size_t index) override; void RemovePasswordException(size_t index) override;
void RequestShowPassword(size_t index, void RequestShowPassword(size_t index,
content::WebContents* web_contents) override; content::WebContents* web_contents) override;
void ImportPasswords(content::WebContents* web_contents) override;
void ExportPasswords(content::WebContents* web_contents) override;
// PasswordUIView implementation. // PasswordUIView implementation.
Profile* GetProfile() override; Profile* GetProfile() override;
......
...@@ -58,7 +58,18 @@ ...@@ -58,7 +58,18 @@
<span>$i18nRaw{managePasswordsLabel}</span> <span>$i18nRaw{managePasswordsLabel}</span>
</div> </div>
<div class="settings-box first"> <div class="settings-box first">
<h2>$i18n{savedPasswordsHeading}</h2> <h2 class="start">$i18n{savedPasswordsHeading}</h2>
<template is="dom-if" if="[[showImportExportPasswords_]]">
<paper-button class="secondary-button header-aligned-button"
on-tap="onImportTap_" id="import">
$i18n{import}
</paper-button>
<paper-button class="secondary-button header-aligned-button"
on-tap="onExportTap_" id="export"
disabled$="[[!hasSome_(savedPasswords)]]">
$i18n{export}
</paper-button>
</template>
</div> </div>
<div class="list-frame"> <div class="list-frame">
<div id="savedPasswordsHeading" class="list-item column-header" <div id="savedPasswordsHeading" class="list-item column-header"
......
...@@ -70,6 +70,16 @@ class PasswordManager { ...@@ -70,6 +70,16 @@ class PasswordManager {
* @param {function(!PasswordManager.PlaintextPasswordEvent):void} callback * @param {function(!PasswordManager.PlaintextPasswordEvent):void} callback
*/ */
getPlaintextPassword(index, callback) {} getPlaintextPassword(index, callback) {}
/**
* Triggers the dialogue for importing passwords.
*/
importPasswords() {}
/**
* Triggers the dialogue for exporting passwords.
*/
exportPasswords() {}
} }
/** @typedef {chrome.passwordsPrivate.PasswordUiEntry} */ /** @typedef {chrome.passwordsPrivate.PasswordUiEntry} */
...@@ -145,6 +155,16 @@ class PasswordManagerImpl { ...@@ -145,6 +155,16 @@ class PasswordManagerImpl {
chrome.passwordsPrivate.onPlaintextPasswordRetrieved.addListener(listener); chrome.passwordsPrivate.onPlaintextPasswordRetrieved.addListener(listener);
chrome.passwordsPrivate.requestPlaintextPassword(index); chrome.passwordsPrivate.requestPlaintextPassword(index);
} }
/** @override */
importPasswords() {
chrome.passwordsPrivate.importPasswords();
}
/** @override */
exportPasswords() {
chrome.passwordsPrivate.exportPasswords();
}
} }
cr.addSingletonGetter(PasswordManagerImpl); cr.addSingletonGetter(PasswordManagerImpl);
...@@ -194,6 +214,15 @@ Polymer({ ...@@ -194,6 +214,15 @@ Polymer({
*/ */
activePassword: Object, activePassword: Object,
/** @private */
showImportExportPasswords_: {
type: Boolean,
value: function() {
return loadTimeData.valueExists('showImportExportPasswords') &&
loadTimeData.getBoolean('showImportExportPasswords');
}
},
/** @private */ /** @private */
showPasswordEditDialog_: Boolean, showPasswordEditDialog_: Boolean,
...@@ -354,6 +383,22 @@ Polymer({ ...@@ -354,6 +383,22 @@ Polymer({
this.activeDialogAnchor_ = target; this.activeDialogAnchor_ = target;
}, },
/**
* Fires an event that should trigger the password import process.
* @private
*/
onImportTap_: function() {
this.passwordManager_.importPasswords();
},
/**
* Fires an event that should trigger the password export process.
* @private
*/
onExportTap_: function() {
this.passwordManager_.exportPasswords();
},
/** /**
* Returns true if the list exists and has items. * Returns true if the list exists and has items.
* @param {Array<Object>} list * @param {Array<Object>} list
......
...@@ -134,11 +134,7 @@ void PasswordManagerPorter::PresentFileSelector( ...@@ -134,11 +134,7 @@ void PasswordManagerPorter::PresentFileSelector(
void PasswordManagerPorter::FileSelected(const base::FilePath& path, void PasswordManagerPorter::FileSelected(const base::FilePath& path,
int index, int index,
void* params) { void* params) {
// We are unable to cast directly from void* to Type: reinterpret_cast will switch (reinterpret_cast<uintptr_t>(params)) {
// only convert between pointers and static_cast only from integral types to
// enums (for those types that are relevant to this example), which
// necessitates the use of two casts.
switch (static_cast<Type>(reinterpret_cast<uintptr_t>(params))) {
case PASSWORD_IMPORT: case PASSWORD_IMPORT:
ImportPasswordsFromPath(path); ImportPasswordsFromPath(path);
break; break;
......
...@@ -1172,7 +1172,8 @@ void AddPasswordsAndFormsStrings(content::WebUIDataSource* html_source) { ...@@ -1172,7 +1172,8 @@ void AddPasswordsAndFormsStrings(content::WebUIDataSource* html_source) {
{"noCreditCardsFound", IDS_SETTINGS_CREDIT_CARD_NONE}, {"noCreditCardsFound", IDS_SETTINGS_CREDIT_CARD_NONE},
{"noPasswordsFound", IDS_SETTINGS_PASSWORDS_NONE}, {"noPasswordsFound", IDS_SETTINGS_PASSWORDS_NONE},
{"noExceptionsFound", IDS_SETTINGS_PASSWORDS_EXCEPTIONS_NONE}, {"noExceptionsFound", IDS_SETTINGS_PASSWORDS_EXCEPTIONS_NONE},
}; {"import", IDS_PASSWORD_MANAGER_IMPORT_BUTTON},
{"export", IDS_PASSWORD_MANAGER_EXPORT_BUTTON}};
html_source->AddString( html_source->AddString(
"managePasswordsLabel", "managePasswordsLabel",
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "chrome/common/url_constants.h" #include "chrome/common/url_constants.h"
#include "chrome/grit/settings_resources.h" #include "chrome/grit/settings_resources.h"
#include "chrome/grit/settings_resources_map.h" #include "chrome/grit/settings_resources_map.h"
#include "components/password_manager/core/common/password_manager_features.h"
#include "components/pref_registry/pref_registry_syncable.h" #include "components/pref_registry/pref_registry_syncable.h"
#include "content/public/browser/navigation_handle.h" #include "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents.h"
...@@ -265,6 +266,11 @@ MdSettingsUI::MdSettingsUI(content::WebUI* web_ui) ...@@ -265,6 +266,11 @@ MdSettingsUI::MdSettingsUI(content::WebUI* web_ui)
} }
#endif #endif
html_source->AddBoolean(
"showImportExportPasswords",
base::FeatureList::IsEnabled(
password_manager::features::kPasswordImportExport));
AddSettingsPageUIHandler( AddSettingsPageUIHandler(
base::WrapUnique(AboutHandler::Create(html_source, profile))); base::WrapUnique(AboutHandler::Create(html_source, profile)));
AddSettingsPageUIHandler( AddSettingsPageUIHandler(
......
...@@ -88,6 +88,12 @@ namespace passwordsPrivate { ...@@ -88,6 +88,12 @@ namespace passwordsPrivate {
// Returns the list of password exceptions. // Returns the list of password exceptions.
// |callback|: Called with the list of password exceptions. // |callback|: Called with the list of password exceptions.
static void getPasswordExceptionList(ExceptionListCallback callback); static void getPasswordExceptionList(ExceptionListCallback callback);
// Triggers the Password Manager password import functionality.
static void importPasswords();
// Triggers the Password Manager password export functionality.
static void exportPasswords();
}; };
interface Events { interface Events {
......
...@@ -104,6 +104,16 @@ var availableTests = [ ...@@ -104,6 +104,16 @@ var availableTests = [
chrome.passwordsPrivate.getPasswordExceptionList(callback); chrome.passwordsPrivate.getPasswordExceptionList(callback);
}, },
function importPasswords() {
chrome.passwordsPrivate.importPasswords();
chrome.test.succeed();
},
function exportPasswords() {
chrome.passwordsPrivate.exportPasswords();
chrome.test.succeed();
},
]; ];
var testToRun = window.location.search.substring(1); var testToRun = window.location.search.substring(1);
......
...@@ -429,6 +429,7 @@ enum HistogramValue { ...@@ -429,6 +429,7 @@ enum HistogramValue {
WEB_VIEW_INTERNAL_ON_AUDIO_STATE_CHANGED, WEB_VIEW_INTERNAL_ON_AUDIO_STATE_CHANGED,
AUTOMATION_INTERNAL_ON_ACTION_RESULT, AUTOMATION_INTERNAL_ON_ACTION_RESULT,
OMNIBOX_ON_DELETE_SUGGESTION, OMNIBOX_ON_DELETE_SUGGESTION,
PASSWORDS_PRIVATE_ON_SHOW_IMPORT_EXPORT_BUTTONS,
// Last entry: Add new entries above, then run: // Last entry: Add new entries above, then run:
// python tools/metrics/histograms/update_extension_histograms.py // python tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY ENUM_BOUNDARY
......
...@@ -1254,6 +1254,8 @@ enum HistogramValue { ...@@ -1254,6 +1254,8 @@ enum HistogramValue {
AUTOTESTPRIVATE_SETMOUSEREVERSESCROLL, AUTOTESTPRIVATE_SETMOUSEREVERSESCROLL,
METRICSPRIVATE_RECORDSPARSEHASHABLE, METRICSPRIVATE_RECORDSPARSEHASHABLE,
NETWORKINGPRIVATE_SELECTCELLULARMOBILENETWORK, NETWORKINGPRIVATE_SELECTCELLULARMOBILENETWORK,
PASSWORDSPRIVATE_IMPORTPASSWORDS,
PASSWORDSPRIVATE_EXPORTPASSWORDS,
// Last entry: Add new entries above, then run: // Last entry: Add new entries above, then run:
// python tools/metrics/histograms/update_extension_histograms.py // python tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY ENUM_BOUNDARY
......
...@@ -107,6 +107,16 @@ chrome.passwordsPrivate.getSavedPasswordList = function(callback) {}; ...@@ -107,6 +107,16 @@ chrome.passwordsPrivate.getSavedPasswordList = function(callback) {};
*/ */
chrome.passwordsPrivate.getPasswordExceptionList = function(callback) {}; chrome.passwordsPrivate.getPasswordExceptionList = function(callback) {};
/**
* @see https://developer.chrome.com/extensions/passwordsPrivate#method-importPasswords
*/
chrome.passwordsPrivate.importPasswords = function() {};
/**
* @see https://developer.chrome.com/extensions/passwordsPrivate#method-exportPasswords
*/
chrome.passwordsPrivate.exportPasswords = function() {};
/** /**
* Fired when the saved passwords list has changed, meaning that an entry has * Fired when the saved passwords list has changed, meaning that an entry has
* been added or removed. * been added or removed.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment