auth_dialog_contents_view.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690
  1. // Copyright 2020 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/in_session_auth/auth_dialog_contents_view.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "ash/login/resources/grit/login_resources.h"
  8. #include "ash/login/ui/horizontal_image_sequence_animation_decoder.h"
  9. #include "ash/login/ui/login_password_view.h"
  10. #include "ash/login/ui/login_pin_input_view.h"
  11. #include "ash/login/ui/login_pin_view.h"
  12. #include "ash/login/ui/non_accessible_view.h"
  13. #include "ash/login/ui/views_utils.h"
  14. #include "ash/public/cpp/webauthn_dialog_controller.h"
  15. #include "ash/resources/vector_icons/vector_icons.h"
  16. #include "ash/strings/grit/ash_strings.h"
  17. #include "base/bind.h"
  18. #include "base/callback_helpers.h"
  19. #include "base/strings/utf_string_conversions.h"
  20. #include "base/timer/timer.h"
  21. #include "ui/accessibility/ax_enums.mojom.h"
  22. #include "ui/accessibility/ax_node_data.h"
  23. #include "ui/base/l10n/l10n_util.h"
  24. #include "ui/base/resource/resource_bundle.h"
  25. #include "ui/compositor/layer.h"
  26. #include "ui/gfx/paint_vector_icon.h"
  27. #include "ui/views/background.h"
  28. #include "ui/views/border.h"
  29. #include "ui/views/bubble/bubble_border.h"
  30. #include "ui/views/controls/button/md_text_button.h"
  31. #include "ui/views/controls/label.h"
  32. #include "ui/views/layout/box_layout.h"
  33. #include "ui/views/layout/fill_layout.h"
  34. namespace ash {
  35. namespace {
  36. constexpr int kContainerPreferredWidth = 340;
  37. constexpr int kBorderTopDp = 36;
  38. constexpr int kBorderLeftDp = 24;
  39. constexpr int kBorderBottomDp = 20;
  40. constexpr int kBorderRightDp = 24;
  41. constexpr int kCornerRadius = 12;
  42. constexpr int kTitleFontSizeDeltaDp = 4;
  43. constexpr int kOriginNameLineHeight = 18;
  44. constexpr int kSpacingAfterAvatar = 18;
  45. constexpr int kSpacingAfterTitle = 8;
  46. constexpr int kSpacingAfterOriginName = 32;
  47. constexpr int kSpacingAfterInputField = 16;
  48. constexpr int kAvatarSizeDp = 36;
  49. constexpr int kFingerprintIconSizeDp = 28;
  50. constexpr int kSpacingBetweenPinPadAndFingerprintIcon = 24;
  51. constexpr int kSpacingBetweenPasswordAndFingerprintIcon = 24;
  52. constexpr int kSpacingBetweenFingerprintIconAndLabelDp = 15;
  53. constexpr int kFingerprintViewWidthDp = 204;
  54. constexpr int kFingerprintFailedAnimationNumFrames = 45;
  55. constexpr base::TimeDelta kResetToDefaultIconDelay = base::Milliseconds(1300);
  56. constexpr base::TimeDelta kResetToDefaultMessageDelay =
  57. base::Milliseconds(3000);
  58. constexpr base::TimeDelta kFingerprintFailedAnimationDuration =
  59. base::Milliseconds(700);
  60. // 38% opacity.
  61. constexpr SkColor kDisabledFingerprintIconColor =
  62. SkColorSetA(gfx::kGoogleGrey900, 97);
  63. constexpr SkColor kBackgroundColor = SK_ColorWHITE;
  64. constexpr SkColor kTextColorSecondary = gfx::kGoogleGrey700;
  65. constexpr SkColor kTextColorPrimary = gfx::kGoogleGrey900;
  66. constexpr SkColor kErrorColor = gfx::kGoogleRed600;
  67. constexpr int kSpacingBeforeButtons = 32;
  68. } // namespace
  69. // Consists of fingerprint icon view and a label.
  70. class AuthDialogContentsView::FingerprintView : public views::View {
  71. public:
  72. // Use a subclass that inherit views::Label so that GetAccessibleNodeData
  73. // override is respected.
  74. class FingerprintLabel : public views::Label {
  75. public:
  76. // views::View
  77. void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
  78. node_data->role = ax::mojom::Role::kStaticText;
  79. node_data->SetName(accessible_name_);
  80. }
  81. void SetAccessibleName(const std::u16string& name) {
  82. accessible_name_ = name;
  83. NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged,
  84. true /*send_native_event*/);
  85. }
  86. private:
  87. std::u16string accessible_name_;
  88. };
  89. FingerprintView() {
  90. auto* layout = SetLayoutManager(std::make_unique<views::BoxLayout>(
  91. views::BoxLayout::Orientation::kVertical, gfx::Insets(),
  92. kSpacingBetweenFingerprintIconAndLabelDp));
  93. layout->set_main_axis_alignment(
  94. views::BoxLayout::MainAxisAlignment::kCenter);
  95. icon_ = AddChildView(std::make_unique<AnimatedRoundedImageView>(
  96. gfx::Size(kFingerprintIconSizeDp, kFingerprintIconSizeDp),
  97. 0 /*corner_radius*/));
  98. label_ = AddChildView(std::make_unique<FingerprintLabel>());
  99. label_->SetSubpixelRenderingEnabled(false);
  100. label_->SetAutoColorReadabilityEnabled(false);
  101. label_->SetEnabledColor(kTextColorPrimary);
  102. label_->SetMultiLine(true);
  103. label_->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
  104. DisplayCurrentState();
  105. }
  106. FingerprintView(const FingerprintView&) = delete;
  107. FingerprintView& operator=(const FingerprintView&) = delete;
  108. ~FingerprintView() override = default;
  109. void SetState(FingerprintState state) {
  110. if (state_ == state)
  111. return;
  112. state_ = state;
  113. DisplayCurrentState();
  114. }
  115. void SetCanUsePin(bool can_use_pin) {
  116. if (can_use_pin_ == can_use_pin)
  117. return;
  118. can_use_pin_ = can_use_pin;
  119. DisplayCurrentState();
  120. }
  121. // Notify the user of the fingerprint auth result. Should be called after
  122. // SetState. If fingerprint auth failed and retry is allowed, reset to
  123. // default state after animation.
  124. void NotifyFingerprintAuthResult(bool success) {
  125. reset_state_.Stop();
  126. if (state_ == FingerprintState::DISABLED_FROM_ATTEMPTS) {
  127. label_->SetText(l10n_util::GetStringUTF16(
  128. IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_DISABLED_FROM_ATTEMPTS));
  129. label_->SetAccessibleName(l10n_util::GetStringUTF16(
  130. IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_ACCESSIBLE_DISABLED_FROM_ATTEMPTS));
  131. } else if (success) {
  132. label_->SetText(l10n_util::GetStringUTF16(
  133. IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_SUCCESS));
  134. label_->SetAccessibleName(l10n_util::GetStringUTF16(
  135. IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_ACCESSIBLE_SUCCESS));
  136. } else {
  137. label_->SetText(l10n_util::GetStringUTF16(
  138. IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_FAILED));
  139. label_->SetAccessibleName(l10n_util::GetStringUTF16(
  140. IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_ACCESSIBLE_FAILED));
  141. }
  142. if (!success) {
  143. // This is just to display the "fingerprint auth failure" animation. It
  144. // does not necessarily mean |state_| is DISABLED_FROM_ATTEMPTS.
  145. SetIcon(FingerprintState::DISABLED_FROM_ATTEMPTS);
  146. // base::Unretained is safe because reset_state_ is owned by |this|.
  147. reset_state_.Start(FROM_HERE, kResetToDefaultIconDelay,
  148. base::BindOnce(&FingerprintView::DisplayCurrentState,
  149. base::Unretained(this)));
  150. label_->NotifyAccessibilityEvent(ax::mojom::Event::kAlert,
  151. true /*send_native_event*/);
  152. }
  153. }
  154. // views::View:
  155. gfx::Size CalculatePreferredSize() const override {
  156. gfx::Size size = views::View::CalculatePreferredSize();
  157. size.set_width(kFingerprintViewWidthDp);
  158. return size;
  159. }
  160. // views::View:
  161. void OnGestureEvent(ui::GestureEvent* event) override {
  162. if (event->type() != ui::ET_GESTURE_TAP)
  163. return;
  164. if (state_ == FingerprintState::AVAILABLE_DEFAULT ||
  165. state_ == FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING) {
  166. SetState(FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING);
  167. reset_state_.Start(
  168. FROM_HERE, kResetToDefaultMessageDelay,
  169. base::BindOnce(&FingerprintView::SetState, base::Unretained(this),
  170. FingerprintState::AVAILABLE_DEFAULT));
  171. }
  172. }
  173. private:
  174. void DisplayCurrentState() {
  175. SetVisible(state_ != FingerprintState::UNAVAILABLE);
  176. SetIcon(state_);
  177. if (state_ != FingerprintState::UNAVAILABLE) {
  178. std::u16string fingerprint_text =
  179. l10n_util::GetStringUTF16(GetTextIdFromState());
  180. label_->SetText(fingerprint_text);
  181. label_->SetAccessibleName(
  182. state_ == FingerprintState::DISABLED_FROM_ATTEMPTS
  183. ? l10n_util::GetStringUTF16(
  184. IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_ACCESSIBLE_DISABLED_FROM_ATTEMPTS)
  185. : fingerprint_text);
  186. }
  187. }
  188. void SetIcon(FingerprintState state) {
  189. const SkColor color =
  190. (state == FingerprintState::AVAILABLE_DEFAULT ||
  191. state == FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING
  192. ? kTextColorPrimary
  193. : kDisabledFingerprintIconColor);
  194. switch (state) {
  195. case FingerprintState::UNAVAILABLE:
  196. case FingerprintState::AVAILABLE_DEFAULT:
  197. case FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING:
  198. case FingerprintState::DISABLED_FROM_TIMEOUT:
  199. icon_->SetImage(gfx::CreateVectorIcon(kLockScreenFingerprintIcon,
  200. kFingerprintIconSizeDp, color));
  201. break;
  202. case FingerprintState::DISABLED_FROM_ATTEMPTS:
  203. icon_->SetAnimationDecoder(
  204. std::make_unique<HorizontalImageSequenceAnimationDecoder>(
  205. *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
  206. IDR_LOGIN_FINGERPRINT_UNLOCK_SPINNER),
  207. kFingerprintFailedAnimationDuration,
  208. kFingerprintFailedAnimationNumFrames),
  209. AnimatedRoundedImageView::Playback::kSingle);
  210. break;
  211. }
  212. }
  213. int GetTextIdFromState() const {
  214. switch (state_) {
  215. case FingerprintState::AVAILABLE_DEFAULT:
  216. return IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_AVAILABLE;
  217. case FingerprintState::AVAILABLE_WITH_TOUCH_SENSOR_WARNING:
  218. return IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_TOUCH_SENSOR;
  219. case FingerprintState::DISABLED_FROM_ATTEMPTS:
  220. return IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_DISABLED_FROM_ATTEMPTS;
  221. case FingerprintState::DISABLED_FROM_TIMEOUT:
  222. if (can_use_pin_)
  223. return IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_PIN_OR_PASSWORD_REQUIRED;
  224. return IDS_ASH_IN_SESSION_AUTH_FINGERPRINT_PASSWORD_REQUIRED;
  225. case FingerprintState::UNAVAILABLE:
  226. NOTREACHED();
  227. return 0;
  228. }
  229. }
  230. FingerprintLabel* label_ = nullptr;
  231. AnimatedRoundedImageView* icon_ = nullptr;
  232. FingerprintState state_ = FingerprintState::AVAILABLE_DEFAULT;
  233. bool can_use_pin_ = false;
  234. base::OneShotTimer reset_state_;
  235. };
  236. class AuthDialogContentsView::TitleLabel : public views::Label {
  237. public:
  238. TitleLabel() {
  239. SetSubpixelRenderingEnabled(false);
  240. SetAutoColorReadabilityEnabled(false);
  241. SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
  242. const gfx::FontList& base_font_list = views::Label::GetDefaultFontList();
  243. SetFontList(base_font_list.Derive(kTitleFontSizeDeltaDp,
  244. gfx::Font::FontStyle::NORMAL,
  245. gfx::Font::Weight::MEDIUM));
  246. SetMaximumWidthSingleLine(kContainerPreferredWidth);
  247. SetElideBehavior(gfx::ElideBehavior::ELIDE_TAIL);
  248. SetPreferredSize(gfx::Size(kContainerPreferredWidth,
  249. GetHeightForWidth(kContainerPreferredWidth)));
  250. SetHorizontalAlignment(gfx::ALIGN_CENTER);
  251. }
  252. bool IsShowingError() const { return is_showing_error_; }
  253. void ShowTitle() {
  254. std::u16string title =
  255. l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_TITLE);
  256. SetText(title);
  257. SetEnabledColor(kTextColorPrimary);
  258. is_showing_error_ = false;
  259. SetAccessibleName(title);
  260. }
  261. void ShowError(const std::u16string& error_text) {
  262. SetText(error_text);
  263. SetEnabledColor(kErrorColor);
  264. is_showing_error_ = true;
  265. SetAccessibleName(error_text);
  266. NotifyAccessibilityEvent(ax::mojom::Event::kAlert,
  267. true /*send_native_event*/);
  268. }
  269. // views::View
  270. void GetAccessibleNodeData(ui::AXNodeData* node_data) override {
  271. node_data->role = ax::mojom::Role::kStaticText;
  272. node_data->SetName(accessible_name_);
  273. }
  274. private:
  275. void SetAccessibleName(const std::u16string& name) {
  276. accessible_name_ = name;
  277. NotifyAccessibilityEvent(ax::mojom::Event::kTextChanged,
  278. true /*send_native_event*/);
  279. }
  280. bool is_showing_error_ = false;
  281. std::u16string accessible_name_;
  282. };
  283. AuthDialogContentsView::AuthDialogContentsView(
  284. uint32_t auth_methods,
  285. const std::string& origin_name,
  286. const AuthMethodsMetadata& auth_metadata,
  287. const UserAvatar& avatar)
  288. : auth_methods_(auth_methods),
  289. origin_name_(origin_name),
  290. auth_metadata_(auth_metadata) {
  291. SetLayoutManager(std::make_unique<views::FillLayout>());
  292. auto border = std::make_unique<views::BubbleBorder>(
  293. views::BubbleBorder::FLOAT, views::BubbleBorder::STANDARD_SHADOW);
  294. border->SetColor(kBackgroundColor);
  295. border->SetCornerRadius(kCornerRadius);
  296. SetBackground(std::make_unique<views::BubbleBackground>(border.get()));
  297. SetBorder(std::move(border));
  298. container_ = AddChildView(std::make_unique<NonAccessibleView>());
  299. container_->SetBorder(views::CreateEmptyBorder(gfx::Insets::TLBR(
  300. kBorderTopDp, kBorderLeftDp, kBorderBottomDp, kBorderRightDp)));
  301. main_layout_ =
  302. container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
  303. views::BoxLayout::Orientation::kVertical));
  304. main_layout_->set_main_axis_alignment(
  305. views::BoxLayout::MainAxisAlignment::kStart);
  306. main_layout_->set_cross_axis_alignment(
  307. views::BoxLayout::CrossAxisAlignment::kCenter);
  308. AddAvatarView(avatar);
  309. AddVerticalSpacing(kSpacingAfterAvatar);
  310. AddTitleView();
  311. AddVerticalSpacing(kSpacingAfterTitle);
  312. AddOriginNameView();
  313. AddVerticalSpacing(kSpacingAfterOriginName);
  314. if (auth_methods_ & kAuthPin) {
  315. if (LoginPinInputView::IsAutosubmitSupported(
  316. auth_metadata_.autosubmit_pin_length)) {
  317. pin_autosubmit_on_ = true;
  318. AddPinDigitInputView();
  319. } else {
  320. pin_autosubmit_on_ = false;
  321. AddPinTextInputView();
  322. }
  323. AddVerticalSpacing(kSpacingAfterInputField);
  324. // PIN pad is always visible regardless of PIN autosubmit status.
  325. AddPinPadView();
  326. } else if (auth_methods & kAuthPassword) {
  327. AddPasswordView();
  328. }
  329. if (auth_methods_ & kAuthFingerprint) {
  330. if (pin_pad_view_) {
  331. AddVerticalSpacing(kSpacingBetweenPinPadAndFingerprintIcon);
  332. } else if (password_view_) {
  333. AddVerticalSpacing(kSpacingBetweenPasswordAndFingerprintIcon);
  334. }
  335. fingerprint_view_ =
  336. container_->AddChildView(std::make_unique<FingerprintView>());
  337. fingerprint_view_->SetCanUsePin(auth_methods_ & kAuthPin);
  338. }
  339. AddVerticalSpacing(kSpacingBeforeButtons);
  340. AddActionButtonsView();
  341. }
  342. AuthDialogContentsView::~AuthDialogContentsView() = default;
  343. void AuthDialogContentsView::GetAccessibleNodeData(ui::AXNodeData* node_data) {
  344. views::View::GetAccessibleNodeData(node_data);
  345. node_data->role = ax::mojom::Role::kDialog;
  346. node_data->SetName(
  347. l10n_util::GetStringFUTF16(IDS_ASH_IN_SESSION_AUTH_ACCESSIBLE_TITLE,
  348. base::UTF8ToUTF16(origin_name_)));
  349. }
  350. void AuthDialogContentsView::RequestFocus() {
  351. if (auth_methods_ == kAuthFingerprint) {
  352. // There's no PIN input field, so let the focus be on the cancel button
  353. // (instead of the help button) because it is more often used.
  354. cancel_button_->RequestFocus();
  355. return;
  356. }
  357. // For other cases, the base method correctly sets focus to the input field.
  358. views::View::RequestFocus();
  359. }
  360. void AuthDialogContentsView::AddedToWidget() {
  361. if (auth_methods_ & kAuthFingerprint) {
  362. // Inject a callback from the contents view so that we can show retry
  363. // prompt.
  364. WebAuthNDialogController::Get()->AuthenticateUserWithFingerprint(
  365. base::BindOnce(&AuthDialogContentsView::OnFingerprintAuthComplete,
  366. weak_factory_.GetWeakPtr()));
  367. }
  368. }
  369. void AuthDialogContentsView::AddAvatarView(const UserAvatar& avatar) {
  370. avatar_view_ =
  371. container_->AddChildView(std::make_unique<AnimatedRoundedImageView>(
  372. gfx::Size(kAvatarSizeDp, kAvatarSizeDp),
  373. kAvatarSizeDp / 2 /*corner_radius*/));
  374. avatar_view_->SetImage(avatar.image);
  375. }
  376. void AuthDialogContentsView::AddTitleView() {
  377. title_ = container_->AddChildView(std::make_unique<TitleLabel>());
  378. title_->ShowTitle();
  379. }
  380. void AuthDialogContentsView::AddOriginNameView() {
  381. origin_name_view_ =
  382. container_->AddChildView(std::make_unique<views::Label>());
  383. origin_name_view_->SetEnabledColor(kTextColorSecondary);
  384. origin_name_view_->SetSubpixelRenderingEnabled(false);
  385. origin_name_view_->SetAutoColorReadabilityEnabled(false);
  386. origin_name_view_->SetFocusBehavior(FocusBehavior::ACCESSIBLE_ONLY);
  387. origin_name_view_->SetText(
  388. l10n_util::GetStringFUTF16(IDS_ASH_IN_SESSION_AUTH_ORIGIN_NAME_PROMPT,
  389. base::UTF8ToUTF16(origin_name_)));
  390. origin_name_view_->SetMultiLine(true);
  391. origin_name_view_->SetMaximumWidth(kContainerPreferredWidth);
  392. origin_name_view_->SetLineHeight(kOriginNameLineHeight);
  393. origin_name_view_->SetPreferredSize(gfx::Size(
  394. kContainerPreferredWidth,
  395. origin_name_view_->GetHeightForWidth(kContainerPreferredWidth)));
  396. origin_name_view_->SetHorizontalAlignment(gfx::ALIGN_CENTER);
  397. }
  398. void AuthDialogContentsView::AddPinTextInputView() {
  399. pin_text_input_view_ =
  400. container_->AddChildView(std::make_unique<LoginPasswordView>(palette_));
  401. pin_text_input_view_->SetPaintToLayer();
  402. pin_text_input_view_->layer()->SetFillsBoundsOpaquely(false);
  403. pin_text_input_view_->SetDisplayPasswordButtonVisible(true);
  404. pin_text_input_view_->SetEnabledOnEmptyPassword(false);
  405. pin_text_input_view_->SetFocusEnabledForTextfield(true);
  406. pin_text_input_view_->SetPlaceholderText(
  407. l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_PIN_PLACEHOLDER));
  408. }
  409. void AuthDialogContentsView::AddPasswordView() {
  410. password_view_ =
  411. container_->AddChildView(std::make_unique<LoginPasswordView>(palette_));
  412. password_view_->SetPaintToLayer();
  413. password_view_->layer()->SetFillsBoundsOpaquely(false);
  414. password_view_->SetDisplayPasswordButtonVisible(true);
  415. password_view_->SetEnabledOnEmptyPassword(false);
  416. password_view_->SetFocusEnabledForTextfield(true);
  417. password_view_->SetPlaceholderText(
  418. l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_PASSWORD_PLACEHOLDER));
  419. password_view_->Init(
  420. base::BindRepeating(&AuthDialogContentsView::OnAuthSubmit,
  421. base::Unretained(this),
  422. /*authenticated_by_pin=*/false),
  423. base::BindRepeating(&AuthDialogContentsView::OnInputTextChanged,
  424. base::Unretained(this)),
  425. base::DoNothing(), views::Button::PressedCallback());
  426. }
  427. void AuthDialogContentsView::AddPinPadView() {
  428. DCHECK(auth_methods_ & kAuthPin);
  429. if (pin_autosubmit_on_) {
  430. pin_pad_view_ = container_->AddChildView(std::make_unique<LoginPinView>(
  431. LoginPinView::Style::kAlphanumeric, palette_,
  432. base::BindRepeating(&AuthDialogContentsView::OnInsertDigitFromPinPad,
  433. base::Unretained(this)),
  434. base::BindRepeating(&AuthDialogContentsView::OnBackspaceFromPinPad,
  435. base::Unretained(this))));
  436. pin_digit_input_view_->Init(
  437. base::BindRepeating(&AuthDialogContentsView::OnAuthSubmit,
  438. base::Unretained(this),
  439. /*authenticated_by_pin=*/true),
  440. base::BindRepeating(&AuthDialogContentsView::OnInputTextChanged,
  441. base::Unretained(this)));
  442. } else {
  443. pin_pad_view_ = container_->AddChildView(std::make_unique<LoginPinView>(
  444. LoginPinView::Style::kAlphanumeric, palette_,
  445. base::BindRepeating(&AuthDialogContentsView::OnInsertDigitFromPinPad,
  446. base::Unretained(this)),
  447. base::BindRepeating(&AuthDialogContentsView::OnBackspaceFromPinPad,
  448. base::Unretained(this)),
  449. base::BindRepeating(&LoginPasswordView::SubmitPassword,
  450. base::Unretained(pin_text_input_view_))));
  451. pin_text_input_view_->Init(
  452. base::BindRepeating(&AuthDialogContentsView::OnAuthSubmit,
  453. base::Unretained(this),
  454. /*authenticated_by_pin=*/true),
  455. base::BindRepeating(&AuthDialogContentsView::OnInputTextChanged,
  456. base::Unretained(this)),
  457. base::DoNothing(), views::Button::PressedCallback());
  458. }
  459. pin_pad_view_->SetVisible(true);
  460. }
  461. void AuthDialogContentsView::AddPinDigitInputView() {
  462. pin_digit_input_view_ =
  463. container_->AddChildView(std::make_unique<LoginPinInputView>(palette_));
  464. pin_digit_input_view_->UpdateLength(auth_metadata_.autosubmit_pin_length);
  465. pin_digit_input_view_->SetVisible(true);
  466. }
  467. void AuthDialogContentsView::AddVerticalSpacing(int height) {
  468. auto* spacing =
  469. container_->AddChildView(std::make_unique<NonAccessibleView>());
  470. spacing->SetPreferredSize(gfx::Size(kContainerPreferredWidth, height));
  471. }
  472. void AuthDialogContentsView::AddActionButtonsView() {
  473. action_view_container_ =
  474. container_->AddChildView(std::make_unique<NonAccessibleView>());
  475. auto* buttons_layout = action_view_container_->SetLayoutManager(
  476. std::make_unique<views::BoxLayout>(
  477. views::BoxLayout::Orientation::kHorizontal));
  478. buttons_layout->set_main_axis_alignment(
  479. views::BoxLayout::MainAxisAlignment::kStart);
  480. help_button_ =
  481. action_view_container_->AddChildView(std::make_unique<views::LabelButton>(
  482. base::BindRepeating(&AuthDialogContentsView::OnNeedHelpButtonPressed,
  483. base::Unretained(this)),
  484. l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_HELP),
  485. views::style::CONTEXT_BUTTON));
  486. help_button_->SetEnabledTextColors(kTextColorPrimary);
  487. auto* spacing = action_view_container_->AddChildView(
  488. std::make_unique<NonAccessibleView>());
  489. buttons_layout->SetFlexForView(spacing, 1);
  490. cancel_button_ = action_view_container_->AddChildView(
  491. std::make_unique<views::MdTextButton>(
  492. base::BindRepeating(&AuthDialogContentsView::OnCancelButtonPressed,
  493. base::Unretained(this)),
  494. l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_CANCEL)));
  495. action_view_container_->SetPreferredSize(
  496. gfx::Size(kContainerPreferredWidth, cancel_button_->height()));
  497. }
  498. void AuthDialogContentsView::OnInsertDigitFromPinPad(int digit) {
  499. // Ignore anything if reached max attempts.
  500. if (pin_locked_out_)
  501. return;
  502. if (title_->IsShowingError())
  503. title_->ShowTitle();
  504. if (pin_autosubmit_on_) {
  505. pin_digit_input_view_->InsertDigit(digit);
  506. } else {
  507. pin_text_input_view_->InsertNumber(digit);
  508. }
  509. }
  510. void AuthDialogContentsView::OnBackspaceFromPinPad() {
  511. // Ignore anything if reached max attempts.
  512. if (pin_locked_out_)
  513. return;
  514. if (title_->IsShowingError())
  515. title_->ShowTitle();
  516. if (pin_autosubmit_on_) {
  517. pin_digit_input_view_->Backspace();
  518. } else {
  519. pin_text_input_view_->Backspace();
  520. }
  521. }
  522. void AuthDialogContentsView::OnInputTextChanged(bool is_empty) {
  523. // If the user is interacting with the input field, restore the title (clear
  524. // error message).
  525. //
  526. // If |is_empty| is true, this call may come from resetting
  527. // |pin_text_input_view_| or |pin_digit_input_view_|, when the error message
  528. // hasn't been shown and read yet. In this case we don't restore the title.
  529. if (title_->IsShowingError() && !is_empty)
  530. title_->ShowTitle();
  531. if (pin_pad_view_) {
  532. pin_pad_view_->OnPasswordTextChanged(is_empty);
  533. }
  534. }
  535. void AuthDialogContentsView::OnAuthSubmit(bool authenticated_by_pin,
  536. const std::u16string& password) {
  537. if (authenticated_by_pin) {
  538. if (pin_autosubmit_on_) {
  539. pin_digit_input_view_->SetReadOnly(true);
  540. } else {
  541. pin_text_input_view_->SetReadOnly(true);
  542. }
  543. } else {
  544. password_view_->SetReadOnly(true);
  545. }
  546. WebAuthNDialogController::Get()->AuthenticateUserWithPasswordOrPin(
  547. base::UTF16ToUTF8(password), authenticated_by_pin,
  548. base::BindOnce(&AuthDialogContentsView::OnPasswordOrPinAuthComplete,
  549. weak_factory_.GetWeakPtr(), authenticated_by_pin));
  550. }
  551. void AuthDialogContentsView::OnPasswordOrPinAuthComplete(
  552. bool authenticated_by_pin,
  553. bool success,
  554. bool can_use_pin) {
  555. // On success, do nothing, and the dialog will dismiss.
  556. if (success)
  557. return;
  558. std::u16string error_text;
  559. if (authenticated_by_pin) {
  560. pin_locked_out_ = !can_use_pin;
  561. error_text =
  562. pin_locked_out_
  563. ? l10n_util::GetStringUTF16(
  564. IDS_ASH_IN_SESSION_AUTH_PIN_TOO_MANY_ATTEMPTS)
  565. : l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_PIN_INCORRECT);
  566. } else {
  567. error_text =
  568. l10n_util::GetStringUTF16(IDS_ASH_IN_SESSION_AUTH_PASSWORD_INCORRECT);
  569. }
  570. title_->ShowError(error_text);
  571. if (!authenticated_by_pin) {
  572. password_view_->Reset();
  573. password_view_->SetReadOnly(false);
  574. } else if (can_use_pin) {
  575. if (pin_autosubmit_on_) {
  576. pin_digit_input_view_->Reset();
  577. pin_digit_input_view_->SetReadOnly(false);
  578. } else {
  579. pin_text_input_view_->Reset();
  580. pin_text_input_view_->SetReadOnly(false);
  581. }
  582. }
  583. }
  584. void AuthDialogContentsView::OnFingerprintAuthComplete(
  585. bool success,
  586. FingerprintState fingerprint_state) {
  587. fingerprint_view_->SetState(fingerprint_state);
  588. // Prepare for the next fingerprint scan.
  589. if (!success && fingerprint_state == FingerprintState::AVAILABLE_DEFAULT) {
  590. WebAuthNDialogController::Get()->AuthenticateUserWithFingerprint(
  591. base::BindOnce(&AuthDialogContentsView::OnFingerprintAuthComplete,
  592. weak_factory_.GetWeakPtr()));
  593. }
  594. fingerprint_view_->NotifyFingerprintAuthResult(success);
  595. }
  596. void AuthDialogContentsView::OnCancelButtonPressed(const ui::Event& event) {
  597. WebAuthNDialogController::Get()->Cancel();
  598. }
  599. void AuthDialogContentsView::OnNeedHelpButtonPressed(const ui::Event& event) {
  600. WebAuthNDialogController::Get()->OpenInSessionAuthHelpPage();
  601. }
  602. } // namespace ash