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(
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
......@@ -105,6 +105,40 @@ class 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
#endif // CHROME_BROWSER_EXTENSIONS_API_PASSWORDS_PRIVATE_PASSWORDS_PRIVATE_API_H_
......@@ -126,6 +126,22 @@ class TestDelegate : public PasswordsPrivateDelegate {
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:
// The current list of entries/exceptions. Cached here so that when new
// observers are added, this delegate can send the current lists without
......@@ -173,6 +189,14 @@ class PasswordsPrivateApiTest : public ExtensionApiTest {
kFlagLoadAsComponent);
}
bool importPasswordsWasTriggered() {
return s_test_delegate_->importPasswordsTriggered;
}
bool exportPasswordsWasTriggered() {
return s_test_delegate_->exportPasswordsTriggered;
}
private:
static TestDelegate* s_test_delegate_;
......@@ -204,4 +228,16 @@ IN_PROC_BROWSER_TEST_F(PasswordsPrivateApiTest, GetPasswordExceptionList) {
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
......@@ -67,6 +67,14 @@ class PasswordsPrivateDelegate : public KeyedService {
// an OS-level authentication dialog if necessary.
virtual void RequestShowPassword(size_t index,
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
......
......@@ -20,6 +20,7 @@
#include "chrome/grit/generated_resources.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/common/password_manager_features.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h"
......@@ -196,6 +197,16 @@ void PasswordsPrivateDelegateImpl::SetPasswordExceptionList(
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)
gfx::NativeWindow PasswordsPrivateDelegateImpl::GetNativeWindow() const {
DCHECK(web_contents_);
......
......@@ -47,6 +47,8 @@ class PasswordsPrivateDelegateImpl : public PasswordsPrivateDelegate,
void RemovePasswordException(size_t index) override;
void RequestShowPassword(size_t index,
content::WebContents* web_contents) override;
void ImportPasswords(content::WebContents* web_contents) override;
void ExportPasswords(content::WebContents* web_contents) override;
// PasswordUIView implementation.
Profile* GetProfile() override;
......
......@@ -58,7 +58,18 @@
<span>$i18nRaw{managePasswordsLabel}</span>
</div>
<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 class="list-frame">
<div id="savedPasswordsHeading" class="list-item column-header"
......
......@@ -70,6 +70,16 @@ class PasswordManager {
* @param {function(!PasswordManager.PlaintextPasswordEvent):void} callback
*/
getPlaintextPassword(index, callback) {}
/**
* Triggers the dialogue for importing passwords.
*/
importPasswords() {}
/**
* Triggers the dialogue for exporting passwords.
*/
exportPasswords() {}
}
/** @typedef {chrome.passwordsPrivate.PasswordUiEntry} */
......@@ -145,6 +155,16 @@ class PasswordManagerImpl {
chrome.passwordsPrivate.onPlaintextPasswordRetrieved.addListener(listener);
chrome.passwordsPrivate.requestPlaintextPassword(index);
}
/** @override */
importPasswords() {
chrome.passwordsPrivate.importPasswords();
}
/** @override */
exportPasswords() {
chrome.passwordsPrivate.exportPasswords();
}
}
cr.addSingletonGetter(PasswordManagerImpl);
......@@ -194,6 +214,15 @@ Polymer({
*/
activePassword: Object,
/** @private */
showImportExportPasswords_: {
type: Boolean,
value: function() {
return loadTimeData.valueExists('showImportExportPasswords') &&
loadTimeData.getBoolean('showImportExportPasswords');
}
},
/** @private */
showPasswordEditDialog_: Boolean,
......@@ -354,6 +383,22 @@ Polymer({
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.
* @param {Array<Object>} list
......
......@@ -134,11 +134,7 @@ void PasswordManagerPorter::PresentFileSelector(
void PasswordManagerPorter::FileSelected(const base::FilePath& path,
int index,
void* params) {
// We are unable to cast directly from void* to Type: reinterpret_cast will
// 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))) {
switch (reinterpret_cast<uintptr_t>(params)) {
case PASSWORD_IMPORT:
ImportPasswordsFromPath(path);
break;
......
......@@ -1172,7 +1172,8 @@ void AddPasswordsAndFormsStrings(content::WebUIDataSource* html_source) {
{"noCreditCardsFound", IDS_SETTINGS_CREDIT_CARD_NONE},
{"noPasswordsFound", IDS_SETTINGS_PASSWORDS_NONE},
{"noExceptionsFound", IDS_SETTINGS_PASSWORDS_EXCEPTIONS_NONE},
};
{"import", IDS_PASSWORD_MANAGER_IMPORT_BUTTON},
{"export", IDS_PASSWORD_MANAGER_EXPORT_BUTTON}};
html_source->AddString(
"managePasswordsLabel",
......
......@@ -40,6 +40,7 @@
#include "chrome/common/url_constants.h"
#include "chrome/grit/settings_resources.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 "content/public/browser/navigation_handle.h"
#include "content/public/browser/web_contents.h"
......@@ -265,6 +266,11 @@ MdSettingsUI::MdSettingsUI(content::WebUI* web_ui)
}
#endif
html_source->AddBoolean(
"showImportExportPasswords",
base::FeatureList::IsEnabled(
password_manager::features::kPasswordImportExport));
AddSettingsPageUIHandler(
base::WrapUnique(AboutHandler::Create(html_source, profile)));
AddSettingsPageUIHandler(
......
......@@ -88,6 +88,12 @@ namespace passwordsPrivate {
// Returns the list of password exceptions.
// |callback|: Called with the list of password exceptions.
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 {
......
......@@ -104,6 +104,16 @@ var availableTests = [
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);
......
......@@ -429,6 +429,7 @@ enum HistogramValue {
WEB_VIEW_INTERNAL_ON_AUDIO_STATE_CHANGED,
AUTOMATION_INTERNAL_ON_ACTION_RESULT,
OMNIBOX_ON_DELETE_SUGGESTION,
PASSWORDS_PRIVATE_ON_SHOW_IMPORT_EXPORT_BUTTONS,
// Last entry: Add new entries above, then run:
// python tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY
......
......@@ -1254,6 +1254,8 @@ enum HistogramValue {
AUTOTESTPRIVATE_SETMOUSEREVERSESCROLL,
METRICSPRIVATE_RECORDSPARSEHASHABLE,
NETWORKINGPRIVATE_SELECTCELLULARMOBILENETWORK,
PASSWORDSPRIVATE_IMPORTPASSWORDS,
PASSWORDSPRIVATE_EXPORTPASSWORDS,
// Last entry: Add new entries above, then run:
// python tools/metrics/histograms/update_extension_histograms.py
ENUM_BOUNDARY
......
......@@ -107,6 +107,16 @@ chrome.passwordsPrivate.getSavedPasswordList = 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
* 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