Commit 974429f1 authored by Yicheng Li's avatar Yicheng Li Committed by Commit Bot

ash: Add a "Need help?" button to in-session auth dialog

When clicked, the help button will open a help article in a new
browser window, so that it's on top of the dialog but the user can
return to the dialog.

Bug: b:156258540, b:144861739
Change-Id: I1ff8f49e9926ed57f27f2eaca307d5180d574091
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2518442
Commit-Queue: Yicheng Li <yichengli@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#824669}
parent b964189a
...@@ -2492,6 +2492,9 @@ This file contains the strings for ash. ...@@ -2492,6 +2492,9 @@ This file contains the strings for ash.
<message name="IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_ACCESSIBLE_DISABLED_FROM_ATTEMPTS" desc="Accessibility text read by chromevox when the user has made too many unsuccessful fingerprint unlock attempts; fingerprint is now disabled until the user authenticates with a different authentication method"> <message name="IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_ACCESSIBLE_DISABLED_FROM_ATTEMPTS" desc="Accessibility text read by chromevox when the user has made too many unsuccessful fingerprint unlock attempts; fingerprint is now disabled until the user authenticates with a different authentication method">
Too many fingerprint attempts Too many fingerprint attempts
</message> </message>
<message name="IDS_ASH_IN_SESSION_AUTH_HELP" desc="Label of a button in the auth dialog to open a help article">
Need help?
</message>
<!-- Multi-profiles intro dialog --> <!-- Multi-profiles intro dialog -->
<message name="IDS_ASH_MULTIPROFILES_INTRO_HEADLINE" desc="Describes which feature multi-profiles intro dialog presents."> <message name="IDS_ASH_MULTIPROFILES_INTRO_HEADLINE" desc="Describes which feature multi-profiles intro dialog presents.">
......
06bb45e3d732edafb1eab45781c08fdfdddeef78
\ No newline at end of file
...@@ -526,9 +526,21 @@ void AuthDialogContentsView::AddActionButtonsView() { ...@@ -526,9 +526,21 @@ void AuthDialogContentsView::AddActionButtonsView() {
std::make_unique<views::BoxLayout>( std::make_unique<views::BoxLayout>(
views::BoxLayout::Orientation::kHorizontal)); views::BoxLayout::Orientation::kHorizontal));
buttons_layout->set_main_axis_alignment( buttons_layout->set_main_axis_alignment(
views::BoxLayout::MainAxisAlignment::kEnd); views::BoxLayout::MainAxisAlignment::kStart);
help_button_ =
action_view_container_->AddChildView(std::make_unique<views::LabelButton>(
base::BindRepeating(&AuthDialogContentsView::OnNeedHelpButtonPressed,
base::Unretained(this)),
l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_HELP),
views::style::CONTEXT_BUTTON));
help_button_->SetEnabledTextColors(SK_ColorDKGRAY);
action_view_container_->SetPreferredSize(
gfx::Size(kContainerPreferredWidth, help_button_->height()));
}
// TODO(b/156258540): Add a "Need help?" button that links to a HC article. void AuthDialogContentsView::OnNeedHelpButtonPressed(const ui::Event& event) {
InSessionAuthDialogController::Get()->OpenInSessionAuthHelpPage();
} }
void AuthDialogContentsView::OnAuthSubmit(const base::string16& pin) { void AuthDialogContentsView::OnAuthSubmit(const base::string16& pin) {
......
...@@ -14,6 +14,7 @@ ...@@ -14,6 +14,7 @@
namespace views { namespace views {
class BoxLayout; class BoxLayout;
class Label; class Label;
class LabelButton;
} // namespace views } // namespace views
namespace ash { namespace ash {
...@@ -112,6 +113,9 @@ class AuthDialogContentsView : public views::View { ...@@ -112,6 +113,9 @@ class AuthDialogContentsView : public views::View {
void OnFingerprintAuthComplete(bool success, void OnFingerprintAuthComplete(bool success,
FingerprintState fingerprint_state); FingerprintState fingerprint_state);
// Called when the "Need help?" button is pressed.
void OnNeedHelpButtonPressed(const ui::Event& event);
// Debug container which holds the entire debug UI. // Debug container which holds the entire debug UI.
views::View* container_ = nullptr; views::View* container_ = nullptr;
...@@ -144,6 +148,9 @@ class AuthDialogContentsView : public views::View { ...@@ -144,6 +148,9 @@ class AuthDialogContentsView : public views::View {
FingerprintView* fingerprint_view_ = nullptr; FingerprintView* fingerprint_view_ = nullptr;
// A button to show a help center article.
views::LabelButton* help_button_ = nullptr;
// Flags of auth methods that should be visible. // Flags of auth methods that should be visible.
uint32_t auth_methods_ = 0u; uint32_t auth_methods_ = 0u;
......
...@@ -39,7 +39,7 @@ void InSessionAuthDialogControllerImpl::ShowAuthenticationDialog( ...@@ -39,7 +39,7 @@ void InSessionAuthDialogControllerImpl::ShowAuthenticationDialog(
// Concurrent requests are not supported. // Concurrent requests are not supported.
DCHECK(!dialog_); DCHECK(!dialog_);
window_tracker_.Add(source_window); source_window_tracker_.Add(source_window);
finish_callback_ = std::move(finish_callback); finish_callback_ = std::move(finish_callback);
AccountId account_id = AccountId account_id =
...@@ -97,7 +97,7 @@ void InSessionAuthDialogControllerImpl::OnPinCanAuthenticate( ...@@ -97,7 +97,7 @@ void InSessionAuthDialogControllerImpl::OnPinCanAuthenticate(
return; return;
} }
if (!window_tracker_.Contains(source_window)) { if (!source_window_tracker_.Contains(source_window)) {
LOG(ERROR) << "Source window is no longer available."; LOG(ERROR) << "Source window is no longer available.";
Cancel(); Cancel();
return; return;
...@@ -116,7 +116,7 @@ void InSessionAuthDialogControllerImpl::OnPinCanAuthenticate( ...@@ -116,7 +116,7 @@ void InSessionAuthDialogControllerImpl::OnPinCanAuthenticate(
AuthDialogContentsView::AuthMethodsMetadata auth_metadata; AuthDialogContentsView::AuthMethodsMetadata auth_metadata;
auth_metadata.autosubmit_pin_length = auth_metadata.autosubmit_pin_length =
user_manager::known_user::GetUserPinLength(account_id); user_manager::known_user::GetUserPinLength(account_id);
window_tracker_.Remove(source_window); source_window_tracker_.Remove(source_window);
Shell::Get()->focus_controller()->AddObserver(this); Shell::Get()->focus_controller()->AddObserver(this);
dialog_ = std::make_unique<InSessionAuthDialog>( dialog_ = std::make_unique<InSessionAuthDialog>(
auth_methods, source_window, origin_name, auth_metadata, avatar); auth_methods, source_window, origin_name, auth_metadata, avatar);
...@@ -131,7 +131,7 @@ void InSessionAuthDialogControllerImpl::DestroyAuthenticationDialog() { ...@@ -131,7 +131,7 @@ void InSessionAuthDialogControllerImpl::DestroyAuthenticationDialog() {
client_->EndFingerprintAuthSession(); client_->EndFingerprintAuthSession();
dialog_.reset(); dialog_.reset();
window_tracker_.RemoveAll(); source_window_tracker_.RemoveAll();
Shell::Get()->focus_controller()->RemoveObserver(this); Shell::Get()->focus_controller()->RemoveObserver(this);
} }
...@@ -197,9 +197,25 @@ void InSessionAuthDialogControllerImpl::Cancel() { ...@@ -197,9 +197,25 @@ void InSessionAuthDialogControllerImpl::Cancel() {
void InSessionAuthDialogControllerImpl::OnWindowFocused( void InSessionAuthDialogControllerImpl::OnWindowFocused(
aura::Window* gained_focus, aura::Window* gained_focus,
aura::Window* lost_focus) { aura::Window* lost_focus) {
if (dialog_ && lost_focus == dialog_->widget()->GetNativeWindow()) { if (should_ignore_focus_change_)
Cancel(); return;
if (!dialog_)
return;
// No-op if focus moved to the help page or back to the dialog.
if (help_window_tracker_.Contains(gained_focus) ||
gained_focus == dialog_->widget()->GetNativeWindow()) {
return;
} }
Cancel();
}
void InSessionAuthDialogControllerImpl::OpenInSessionAuthHelpPage() {
DCHECK(client_);
base::AutoReset<bool> scoped_ignore_focus(&should_ignore_focus_change_, true);
help_window_tracker_.Add(client_->OpenInSessionAuthHelpPage());
} }
} // namespace ash } // namespace ash
...@@ -48,6 +48,7 @@ class InSessionAuthDialogControllerImpl ...@@ -48,6 +48,7 @@ class InSessionAuthDialogControllerImpl
OnAuthenticateCallback callback) override; OnAuthenticateCallback callback) override;
void AuthenticateUserWithFingerprint( void AuthenticateUserWithFingerprint(
base::OnceCallback<void(bool, FingerprintState)> callback) override; base::OnceCallback<void(bool, FingerprintState)> callback) override;
void OpenInSessionAuthHelpPage() override;
void Cancel() override; void Cancel() override;
// aura::client::FocusChangeObserver overrides // aura::client::FocusChangeObserver overrides
...@@ -85,10 +86,15 @@ class InSessionAuthDialogControllerImpl ...@@ -85,10 +86,15 @@ class InSessionAuthDialogControllerImpl
std::unique_ptr<InSessionAuthDialog> dialog_; std::unique_ptr<InSessionAuthDialog> dialog_;
aura::WindowTracker window_tracker_; aura::WindowTracker source_window_tracker_;
// Tracks windows that show the help article about in-session auth.
aura::WindowTracker help_window_tracker_;
std::unique_ptr<WebAuthnRequestRegistrarImpl> webauthn_request_registrar_; std::unique_ptr<WebAuthnRequestRegistrarImpl> webauthn_request_registrar_;
bool should_ignore_focus_change_ = false;
base::WeakPtrFactory<InSessionAuthDialogControllerImpl> weak_factory_{this}; base::WeakPtrFactory<InSessionAuthDialogControllerImpl> weak_factory_{this};
}; };
......
...@@ -50,6 +50,8 @@ class MockInSessionAuthDialogClient : public InSessionAuthDialogClient { ...@@ -50,6 +50,8 @@ class MockInSessionAuthDialogClient : public InSessionAuthDialogClient {
AuthenticateUserWithFingerprint, AuthenticateUserWithFingerprint,
(base::OnceCallback<void(bool, FingerprintState)> callback), (base::OnceCallback<void(bool, FingerprintState)> callback),
(override)); (override));
MOCK_METHOD(aura::Window*, OpenInSessionAuthHelpPage, (), (const override));
}; };
} // namespace ash } // namespace ash
......
...@@ -12,6 +12,10 @@ ...@@ -12,6 +12,10 @@
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "components/account_id/account_id.h" #include "components/account_id/account_id.h"
namespace aura {
class Window;
}
namespace ash { namespace ash {
// An interface that allows Ash to trigger authentication steps that ChromeOS // An interface that allows Ash to trigger authentication steps that ChromeOS
...@@ -46,6 +50,9 @@ class ASH_PUBLIC_EXPORT InSessionAuthDialogClient { ...@@ -46,6 +50,9 @@ class ASH_PUBLIC_EXPORT InSessionAuthDialogClient {
virtual void AuthenticateUserWithFingerprint( virtual void AuthenticateUserWithFingerprint(
base::OnceCallback<void(bool, FingerprintState)> callback) = 0; base::OnceCallback<void(bool, FingerprintState)> callback) = 0;
// Open a help article in a new window and return the window.
virtual aura::Window* OpenInSessionAuthHelpPage() const = 0;
protected: protected:
virtual ~InSessionAuthDialogClient() = default; virtual ~InSessionAuthDialogClient() = default;
}; };
......
...@@ -50,6 +50,9 @@ class ASH_PUBLIC_EXPORT InSessionAuthDialogController { ...@@ -50,6 +50,9 @@ class ASH_PUBLIC_EXPORT InSessionAuthDialogController {
virtual void AuthenticateUserWithFingerprint( virtual void AuthenticateUserWithFingerprint(
base::OnceCallback<void(bool, FingerprintState)> callback) = 0; base::OnceCallback<void(bool, FingerprintState)> callback) = 0;
// Opens a help article in Chrome.
virtual void OpenInSessionAuthHelpPage() = 0;
// Cancels all operations and destroys the dialog. // Cancels all operations and destroys the dialog.
virtual void Cancel() = 0; virtual void Cancel() = 0;
......
...@@ -15,6 +15,11 @@ ...@@ -15,6 +15,11 @@
#include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h" #include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h"
#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h"
#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h" #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_navigator.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/browser_window.h"
#include "components/account_id/account_id.h" #include "components/account_id/account_id.h"
#include "components/user_manager/user_manager.h" #include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_task_traits.h" #include "content/public/browser/browser_task_traits.h"
...@@ -26,7 +31,14 @@ using chromeos::Key; ...@@ -26,7 +31,14 @@ using chromeos::Key;
using chromeos::UserContext; using chromeos::UserContext;
namespace { namespace {
// TODO(b/156258540): Replace with correct URL once the article is uploaded.
// This URL is an irrelevant article just for validating functionality.
const char kInSessionAuthHelpPageUrl[] =
"https://support.google.com/chrome/?p=settings_sign_in";
InSessionAuthDialogClient* g_auth_dialog_client_instance = nullptr; InSessionAuthDialogClient* g_auth_dialog_client_instance = nullptr;
} // namespace } // namespace
InSessionAuthDialogClient::InSessionAuthDialogClient() { InSessionAuthDialogClient::InSessionAuthDialogClient() {
...@@ -207,6 +219,23 @@ void InSessionAuthDialogClient::OnFingerprintAuthDone( ...@@ -207,6 +219,23 @@ void InSessionAuthDialogClient::OnFingerprintAuthDone(
} }
} }
aura::Window* InSessionAuthDialogClient::OpenInSessionAuthHelpPage() const {
// TODO(b/156258540): Use the profile of the source browser window.
Profile* profile = ProfileManager::GetActiveUserProfile();
// Create new browser window because the auth dialog is a child of the
// existing one.
NavigateParams params(profile, GURL(kInSessionAuthHelpPageUrl),
ui::PAGE_TRANSITION_AUTO_BOOKMARK);
params.disposition = WindowOpenDisposition::NEW_POPUP;
params.trusted_source = true;
params.window_action = NavigateParams::SHOW_WINDOW;
params.user_gesture = true;
params.path_behavior = NavigateParams::IGNORE_AND_NAVIGATE;
Navigate(&params);
return params.browser->window()->GetNativeWindow();
}
// AuthStatusConsumer: // AuthStatusConsumer:
void InSessionAuthDialogClient::OnAuthFailure( void InSessionAuthDialogClient::OnAuthFailure(
const chromeos::AuthFailure& error) { const chromeos::AuthFailure& error) {
......
...@@ -14,6 +14,10 @@ ...@@ -14,6 +14,10 @@
#include "chromeos/login/auth/extended_authenticator.h" #include "chromeos/login/auth/extended_authenticator.h"
#include "chromeos/login/auth/user_context.h" #include "chromeos/login/auth/user_context.h"
namespace aura {
class Window;
}
class AccountId; class AccountId;
// Handles method calls sent from Ash to ChromeOS. // Handles method calls sent from Ash to ChromeOS.
...@@ -46,6 +50,7 @@ class InSessionAuthDialogClient : public ash::InSessionAuthDialogClient, ...@@ -46,6 +50,7 @@ class InSessionAuthDialogClient : public ash::InSessionAuthDialogClient,
base::OnceCallback<void(bool)> callback) override; base::OnceCallback<void(bool)> callback) override;
void AuthenticateUserWithFingerprint( void AuthenticateUserWithFingerprint(
base::OnceCallback<void(bool, ash::FingerprintState)> callback) override; base::OnceCallback<void(bool, ash::FingerprintState)> callback) override;
aura::Window* OpenInSessionAuthHelpPage() const override;
// AuthStatusConsumer: // AuthStatusConsumer:
void OnAuthFailure(const chromeos::AuthFailure& error) override; void OnAuthFailure(const chromeos::AuthFailure& error) override;
......
...@@ -49,6 +49,7 @@ class FakeInSessionAuthDialogController ...@@ -49,6 +49,7 @@ class FakeInSessionAuthDialogController
void AuthenticateUserWithFingerprint( void AuthenticateUserWithFingerprint(
base::OnceCallback<void(bool, ash::FingerprintState)> callback) override { base::OnceCallback<void(bool, ash::FingerprintState)> callback) override {
} }
void OpenInSessionAuthHelpPage() override {}
void Cancel() override {} void Cancel() override {}
}; };
......
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