Commit 9a920cc1 authored by vasilii's avatar vasilii Committed by Commit bot

Credential Manager API pops up an auto-signin toast.

BUG=400674
TBR=isherman@chromium.org

Review URL: https://codereview.chromium.org/924733003

Cr-Commit-Position: refs/heads/master@{#317022}
parent 3027da7a
......@@ -13512,6 +13512,9 @@ Some features may be unavailable. Please check that the profile exists and you
<message name="IDS_MANAGE_PASSWORDS_IDENTITY_PROVIDER" desc="The description of an identity provider in the Account Chooser dialog.">
via <ph name="PROVIDER">$1<ex>facebook.com</ex></ph>
</message>
<message name="IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE" desc="The title of the auto-signin toast.">
Signing in with Google Smart Lock as '<ph name="FULL_NAME">$1<ex>Brad Pitt</ex></ph>'
</message>
<message name="IDS_ALLOW_AUTOFILL_SYNC_CREDENTIAL" desc="The text for the choice to allow autofilling in about:flags">
Allow
......
......@@ -182,6 +182,8 @@ void ManagePasswordsBubbleModel::OnBubbleShown(
metrics_util::AUTOMATIC_GENERATED_PASSWORD_CONFIRMATION;
} else if (state_ == password_manager::ui::CREDENTIAL_REQUEST_STATE) {
display_disposition_ = metrics_util::AUTOMATIC_CREDENTIAL_REQUEST;
} else if (state_ == password_manager::ui::AUTO_SIGNIN_STATE) {
display_disposition_ = metrics_util::AUTOMATIC_SIGNIN_TOAST;
} else {
display_disposition_ = metrics_util::AUTOMATIC_WITH_PASSWORD_PENDING;
}
......@@ -230,13 +232,13 @@ void ManagePasswordsBubbleModel::OnBubbleHidden() {
metrics_util::LogUIDismissalReason(dismissal_reason_);
// Other use cases have been reported in the callbacks like OnSaveClicked().
if (dismissal_reason_ == metrics_util::NO_DIRECT_INTERACTION)
if (state_ == password_manager::ui::PENDING_PASSWORD_STATE &&
dismissal_reason_ == metrics_util::NO_DIRECT_INTERACTION)
RecordExperimentStatistics(web_contents(), dismissal_reason_);
}
void ManagePasswordsBubbleModel::OnCollectURLClicked(const std::string& url) {
dismissal_reason_ = metrics_util::CLICKED_COLLECT_URL;
RecordExperimentStatistics(web_contents(), dismissal_reason_);
// User interaction with bubble has happened, do not need to show bubble
// in case it was before transition to another page.
state_ = password_manager::ui::ASK_USER_REPORT_URL_BUBBLE_SHOWN_STATE;
......@@ -249,7 +251,6 @@ void ManagePasswordsBubbleModel::OnCollectURLClicked(const std::string& url) {
void ManagePasswordsBubbleModel::OnDoNotCollectURLClicked() {
dismissal_reason_ = metrics_util::CLICKED_DO_NOT_COLLECT_URL;
RecordExperimentStatistics(web_contents(), dismissal_reason_);
// User interaction with bubble has happened, do not need to show bubble
// in case it was before transition to another page.
state_ = password_manager::ui::ASK_USER_REPORT_URL_BUBBLE_SHOWN_STATE;
......@@ -314,6 +315,10 @@ void ManagePasswordsBubbleModel::OnManageLinkClicked() {
->NavigateToPasswordManagerSettingsPage();
}
void ManagePasswordsBubbleModel::OnAutoSignInToastTimeout() {
dismissal_reason_ = metrics_util::AUTO_SIGNIN_TOAST_TIMEOUT;
}
void ManagePasswordsBubbleModel::OnPasswordAction(
const autofill::PasswordForm& password_form,
PasswordAction action) {
......
......@@ -80,6 +80,9 @@ class ManagePasswordsBubbleModel : public content::WebContentsObserver {
// Called by the view code when the manage link is clicked by the user.
void OnManageLinkClicked();
// Called by the view code when the auto-signin toast is about to close.
void OnAutoSignInToastTimeout();
// Called by the view code to delete or add a password form to the
// PasswordStore.
void OnPasswordAction(const autofill::PasswordForm& password_form,
......
......@@ -79,6 +79,12 @@ class ManagePasswordsBubbleModelTest : public testing::Test {
controller()->SetState(password_manager::ui::CREDENTIAL_REQUEST_STATE);
}
void PretendAutoSigningIn() {
model_->set_state(password_manager::ui::AUTO_SIGNIN_STATE);
model_->OnBubbleShown(ManagePasswordsBubble::AUTOMATIC);
controller()->SetState(password_manager::ui::AUTO_SIGNIN_STATE);
}
void PretendManagingPasswords() {
model_->set_state(password_manager::ui::MANAGE_STATE);
model_->OnBubbleShown(ManagePasswordsBubble::USER_ACTION);
......@@ -402,3 +408,17 @@ TEST_F(ManagePasswordsBubbleModelTest, DismissCredential) {
password_manager::metrics_util::NO_DIRECT_INTERACTION,
1);
}
TEST_F(ManagePasswordsBubbleModelTest, PopupAutoSigninToast) {
base::HistogramTester histogram_tester;
PretendAutoSigningIn();
model_->OnAutoSignInToastTimeout();
model_->OnBubbleHidden();
EXPECT_EQ(model_->dismissal_reason(),
password_manager::metrics_util::AUTO_SIGNIN_TOAST_TIMEOUT);
histogram_tester.ExpectUniqueSample(
kUIDismissalReasonMetric,
password_manager::metrics_util::AUTO_SIGNIN_TOAST_TIMEOUT,
1);
}
......@@ -392,6 +392,10 @@ void ManagePasswordsUIController::ShowBubbleWithoutUserInteraction() {
Browser* browser = chrome::FindBrowserWithWebContents(web_contents());
if (!browser || browser->toolbar_model()->input_in_progress())
return;
if (state_ == password_manager::ui::PENDING_PASSWORD_STATE &&
!password_bubble_experiment::ShouldShowBubble(
browser->profile()->GetPrefs()))
return;
CommandUpdater* updater = browser->command_controller()->command_updater();
updater->ExecuteCommand(IDC_MANAGE_PASSWORDS_FOR_PAGE);
#endif
......
......@@ -30,14 +30,25 @@ gfx::Size GetTextLabelsSize(const views::Label* upper_label,
upper_label_size.height() + lower_label_size.height());
}
// Returns either full name or username if the former is empty.
const base::string16& GetUpperLabelText(const autofill::PasswordForm& form) {
return form.display_name.empty() ? form.username_value : form.display_name;
// Returns the bold upper text for the button.
base::string16 GetUpperLabelText(const autofill::PasswordForm& form,
CredentialsItemView::Style style) {
const base::string16& name = form.display_name.empty() ? form.username_value
: form.display_name;
switch (style) {
case CredentialsItemView::ACCOUNT_CHOOSER:
return name;
case CredentialsItemView::AUTO_SIGNIN:
return l10n_util::GetStringFUTF16(IDS_MANAGE_PASSWORDS_AUTO_SIGNIN_TITLE,
name);
}
NOTREACHED();
return base::string16();
}
// Returns IDP information for federated credentials and username or empty
// string for non-federated ones.
base::string16 GetLowerLabelText(const autofill::PasswordForm& form) {
// Returns the lower text for the button.
base::string16 GetLowerLabelText(const autofill::PasswordForm& form,
CredentialsItemView::Style style) {
if (!form.federation_url.is_empty()) {
return l10n_util::GetStringFUTF16(
IDS_MANAGE_PASSWORDS_IDENTITY_PROVIDER,
......@@ -75,6 +86,7 @@ CredentialsItemView::CredentialsItemView(
views::ButtonListener* button_listener,
const autofill::PasswordForm& form,
password_manager::CredentialType credential_type,
Style style,
net::URLRequestContextGetter* request_context)
: LabelButton(button_listener, base::string16()),
form_(form),
......@@ -102,11 +114,12 @@ CredentialsItemView::CredentialsItemView(
ui::ResourceBundle* rb = &ui::ResourceBundle::GetSharedInstance();
upper_label_ = new views::Label(
GetUpperLabelText(form_), rb->GetFontList(ui::ResourceBundle::BoldFont));
GetUpperLabelText(form_, style),
rb->GetFontList(ui::ResourceBundle::BoldFont));
upper_label_->SetHorizontalAlignment(gfx::ALIGN_LEFT);
AddChildView(upper_label_);
base::string16 lower_text = GetLowerLabelText(form_);
base::string16 lower_text = GetLowerLabelText(form_, style);
if (!lower_text.empty()) {
lower_label_ = new views::Label(
lower_text, rb->GetFontList(ui::ResourceBundle::SmallFont));
......
......@@ -30,9 +30,15 @@ class Label;
class CredentialsItemView : public AccountAvatarFetcherDelegate,
public views::LabelButton {
public:
enum Style {
ACCOUNT_CHOOSER,
AUTO_SIGNIN,
};
CredentialsItemView(views::ButtonListener* button_listener,
const autofill::PasswordForm& form,
password_manager::CredentialType credential_type,
Style style,
net::URLRequestContextGetter* request_context);
~CredentialsItemView() override;
......
......@@ -4,6 +4,7 @@
#include "chrome/browser/ui/views/passwords/manage_passwords_bubble_view.h"
#include "base/timer/timer.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
......@@ -39,6 +40,7 @@
namespace {
const int kAutoSigninToastTimeout = 5;
const int kDesiredBubbleWidth = 370;
enum ColumnSetType {
......@@ -246,8 +248,9 @@ void ManagePasswordsBubbleView::AccountChooserView::AddCredentialItemsWithType(
for (autofill::PasswordForm* form : password_forms) {
// Add the title to the layout with appropriate padding.
layout->StartRow(0, SINGLE_VIEW_COLUMN_SET);
layout->AddView(
new CredentialsItemView(this, *form, type, request_context));
layout->AddView(new CredentialsItemView(
this, *form, type, CredentialsItemView::ACCOUNT_CHOOSER,
request_context));
}
}
......@@ -265,6 +268,55 @@ void ManagePasswordsBubbleView::AccountChooserView::ButtonPressed(
parent_->Close();
}
// ManagePasswordsBubbleView::AutoSigninView ----------------------------------
// A view containing just one credential that was used for for automatic signing
// in.
class ManagePasswordsBubbleView::AutoSigninView
: public views::View,
public views::ButtonListener {
public:
explicit AutoSigninView(ManagePasswordsBubbleView* parent);
private:
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
void OnTimer();
base::OneShotTimer<AutoSigninView> timer_;
ManagePasswordsBubbleView* parent_;
};
ManagePasswordsBubbleView::AutoSigninView::AutoSigninView(
ManagePasswordsBubbleView* parent)
: parent_(parent) {
SetLayoutManager(new views::FillLayout);
CredentialsItemView* credential = new CredentialsItemView(
this,
parent_->model()->pending_password(),
password_manager::CredentialType::CREDENTIAL_TYPE_LOCAL,
CredentialsItemView::AUTO_SIGNIN,
parent_->model()->GetProfile()->GetRequestContext());
AddChildView(credential);
// TODO(vasilii): enable the button to switch to the "Managed" state.
credential->SetEnabled(false);
parent_->set_initially_focused_view(credential);
timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(kAutoSigninToastTimeout),
this, &AutoSigninView::OnTimer);
}
void ManagePasswordsBubbleView::AutoSigninView::ButtonPressed(
views::Button* sender, const ui::Event& event) {
// TODO(vasilii): close the toast and switch to the "Managed" state.
}
void ManagePasswordsBubbleView::AutoSigninView::OnTimer() {
parent_->model()->OnAutoSignInToastTimeout();
parent_->Close();
}
// ManagePasswordsBubbleView::AskUserToSubmitURLView -------------------------
// Asks users if they want to report the URL when the password manager failed
......@@ -974,6 +1026,8 @@ void ManagePasswordsBubbleView::Refresh() {
} else if (model()->state() ==
password_manager::ui::CREDENTIAL_REQUEST_STATE) {
AddChildView(new AccountChooserView(this));
} else if (model()->state() == password_manager::ui::AUTO_SIGNIN_STATE) {
AddChildView(new AutoSigninView(this));
} else {
AddChildView(new ManageView(this));
}
......
......@@ -52,6 +52,7 @@ class ManagePasswordsBubbleView : public ManagePasswordsBubble,
private:
class AskUserToSubmitURLView;
class AccountChooserView;
class AutoSigninView;
class BlacklistedView;
class ConfirmNeverView;
class ManageView;
......
......@@ -30,6 +30,7 @@ enum UIDisplayDisposition {
MANUAL_BLACKLISTED,
AUTOMATIC_GENERATED_PASSWORD_CONFIRMATION,
AUTOMATIC_CREDENTIAL_REQUEST,
AUTOMATIC_SIGNIN_TOAST,
NUM_DISPLAY_DISPOSITIONS
};
......@@ -48,6 +49,7 @@ enum UIDismissalReason {
CLICKED_CREDENTIAL,
CLICKED_COLLECT_URL,
CLICKED_DO_NOT_COLLECT_URL,
AUTO_SIGNIN_TOAST_TIMEOUT,
NUM_UI_RESPONSES,
// If we add the omnibox icon _without_ intending to display the bubble,
......
......@@ -54402,6 +54402,7 @@ To add a new entry, add it with any value and run test to compute valid value.
label="Opened automatically / Confirming generated password saved"/>
<int value="5"
label="Opened automatically / Offering a credential to choose"/>
<int value="6" label="Opened automatically / Auto-signin toast"/>
</enum>
<enum name="PasswordGenerationEvent" type="int">
......@@ -54885,6 +54886,7 @@ To add a new entry, add it with any value and run test to compute valid value.
<int value="8" label="Clicked on a credential"/>
<int value="9" label="Clicked collect URL"/>
<int value="10" label="Clicked do not collect URL"/>
<int value="11" label="Auto-signin toast timeout"/>
</enum>
<enum name="PeerConnectionCounters" type="int">
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