Commit 77a6bb63 authored by Balazs Engedy's avatar Balazs Engedy Committed by Commit Bot

Introduce AuthenticatorRequestSheets.

AuthenticatorRequestSheetView defines the basic structure of sheets
shown in the authenticator request dialog. Each sheet corresponds to a
certain step of the authentication flow, and encapsulates the controls
above the Ok/Cancel buttons, namely:
 -- an optional `back icon`,
 -- the title of the current step,
 -- the description of the current step, and
 -- a view with step-specific content, filling the rest of the space.

AuthenticatorRequestSheetModel will be subclassed for each step of
the authenticator request UX flow, and, for each step, the subclass is
going to encapsulate:

 (1) knowledge of the set of actions possible to the user at that step,

 (2) pieces of data required by views to visualise the sheet:
   (a) strings to use on labels/buttons rendered by the SheetView and
       the AuthenticatorRequestDialogView, and the state of the buttons,
   (b) data for additional step-specific contents rendered by SheetView
       subclasses, if any,

 (3) logic to handle user interactions with:
   (a) the `Back`, `Accept`, `Cancel`, buttons, even though the latter
       two are actually rendered by the AuthenticatorRequestDialogView,
   (b) the step-specific contents, if any.

For now, only a sheet with the original static content is shown.

Bug: 849323
Change-Id: I8b92f180a3dc3abc901dcb2ad0200e45271416a3
Reviewed-on: https://chromium-review.googlesource.com/1097321
Commit-Queue: Balazs Engedy <engedy@chromium.org>
Reviewed-by: default avatarTrent Apted <tapted@chromium.org>
Reviewed-by: default avatarKim Paulhamus <kpaulhamus@chromium.org>
Cr-Commit-Position: refs/heads/master@{#567973}
parent c4662e1a
...@@ -2994,6 +2994,9 @@ split_static_library("ui") { ...@@ -2994,6 +2994,9 @@ split_static_library("ui") {
"views/update_recommended_message_box.h", "views/update_recommended_message_box.h",
"views/webauthn/authenticator_request_dialog_view.cc", "views/webauthn/authenticator_request_dialog_view.cc",
"views/webauthn/authenticator_request_dialog_view.h", "views/webauthn/authenticator_request_dialog_view.h",
"views/webauthn/authenticator_request_sheet_view.cc",
"views/webauthn/authenticator_request_sheet_view.h",
"webauthn/authenticator_request_sheet_model.h",
] ]
deps += [ deps += [
"//chrome/browser/ui/views", "//chrome/browser/ui/views",
...@@ -4004,7 +4007,10 @@ static_library("test_support") { ...@@ -4004,7 +4007,10 @@ static_library("test_support") {
] ]
if (toolkit_views) { if (toolkit_views) {
deps += [ "//ui/views:test_support" ] deps += [
"//components/constrained_window:constrained_window",
"//ui/views:test_support",
]
sources += [ sources += [
"../../test/views/scoped_macviews_browser_mode.cc", "../../test/views/scoped_macviews_browser_mode.cc",
"../../test/views/scoped_macviews_browser_mode.h", "../../test/views/scoped_macviews_browser_mode.h",
...@@ -4012,6 +4018,8 @@ static_library("test_support") { ...@@ -4012,6 +4018,8 @@ static_library("test_support") {
"views/payments/test_chrome_payment_request_delegate.cc", "views/payments/test_chrome_payment_request_delegate.cc",
"views/payments/test_chrome_payment_request_delegate.h", "views/payments/test_chrome_payment_request_delegate.h",
"views/toolbar/browser_action_test_util_views_mac.mm", "views/toolbar/browser_action_test_util_views_mac.mm",
"views/webauthn/authenticator_request_dialog_view_test_api.cc",
"views/webauthn/authenticator_request_dialog_view_test_api.h",
] ]
if (is_mac) { if (is_mac) {
sources += [ sources += [
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.h"
#include <memory>
#include <utility>
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/test/test_browser_dialog.h"
#include "chrome/browser/ui/views/webauthn/authenticator_request_dialog_view_test_api.h"
#include "chrome/browser/ui/webauthn/authenticator_request_dialog.h"
#include "chrome/browser/ui/webauthn/authenticator_request_sheet_model.h"
#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
#include "ui/views/controls/label.h"
namespace {
class TestSheetModel : public AuthenticatorRequestSheetModel {
public:
TestSheetModel() = default;
~TestSheetModel() override = default;
// Getters for data on step specific content:
base::string16 GetStepSpecificLabelText() {
return base::ASCIIToUTF16("Test Label");
}
private:
// AuthenticatorRequestSheetModel:
bool IsBackButtonVisible() const override { return true; }
bool IsCancelButtonVisible() const override { return true; }
base::string16 GetCancelButtonLabel() const override {
return base::ASCIIToUTF16("Test Cancel");
}
bool IsAcceptButtonVisible() const override { return true; }
bool IsAcceptButtonEnabled() const override { return true; }
base::string16 GetAcceptButtonLabel() const override {
return base::ASCIIToUTF16("Test OK");
}
base::string16 GetStepTitle() const override {
return base::ASCIIToUTF16("Test Title");
}
base::string16 GetStepDescription() const override {
return base::ASCIIToUTF16(
"Test Description That Is Super Long So That It No Longer Fits On One "
"Line Because Life Would Be Just Too Simple That Way");
}
void OnBack() override {}
void OnAccept() override {}
void OnCancel() override {}
DISALLOW_COPY_AND_ASSIGN(TestSheetModel);
};
class TestSheetView : public AuthenticatorRequestSheetView {
public:
explicit TestSheetView(std::unique_ptr<TestSheetModel> model)
: AuthenticatorRequestSheetView(std::move(model)) {
InitChildViews();
}
~TestSheetView() override = default;
private:
TestSheetModel* test_sheet_model() {
return static_cast<TestSheetModel*>(model());
}
// AuthenticatorRequestSheetView:
std::unique_ptr<views::View> BuildStepSpecificContent() override {
return std::make_unique<views::Label>(
test_sheet_model()->GetStepSpecificLabelText());
}
DISALLOW_COPY_AND_ASSIGN(TestSheetView);
};
} // namespace
class AuthenticatorDialogViewTest : public DialogBrowserTest {
public:
AuthenticatorDialogViewTest() = default;
// DialogBrowserTest:
void ShowUi(const std::string& name) override {
content::WebContents* const web_contents =
browser()->tab_strip_model()->GetActiveWebContents();
auto dialog_model = std::make_unique<AuthenticatorRequestDialogModel>();
auto dialog = std::make_unique<AuthenticatorRequestDialogView>(
web_contents, std::move(dialog_model));
auto sheet_model = std::make_unique<TestSheetModel>();
auto sheet = std::make_unique<TestSheetView>(std::move(sheet_model));
test::AuthenticatorRequestDialogViewTestApi::ReplaceCurrentSheet(
dialog.get(), std::move(sheet));
test::AuthenticatorRequestDialogViewTestApi::Show(web_contents,
std::move(dialog));
}
private:
DISALLOW_COPY_AND_ASSIGN(AuthenticatorDialogViewTest);
};
// Test the dialog with a custom delegate.
IN_PROC_BROWSER_TEST_F(AuthenticatorDialogViewTest, InvokeUi_default) {
ShowAndVerifyUi();
}
...@@ -6,18 +6,57 @@ ...@@ -6,18 +6,57 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h" #include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
#include "chrome/browser/ui/views/harmony/chrome_typography.h" #include "chrome/browser/ui/views/harmony/chrome_typography.h"
#include "chrome/browser/ui/webauthn/authenticator_request_sheet_model.h"
#include "chrome/grit/generated_resources.h" #include "chrome/grit/generated_resources.h"
#include "components/constrained_window/constrained_window_views.h" #include "components/constrained_window/constrained_window_views.h"
#include "components/strings/grit/components_strings.h"
#include "components/web_modal/web_contents_modal_dialog_manager.h" #include "components/web_modal/web_contents_modal_dialog_manager.h"
#include "components/web_modal/web_contents_modal_dialog_manager_delegate.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/fill_layout.h" #include "ui/views/layout/fill_layout.h"
namespace {
// Defines a static sheet with no additional step-specific content and no button
// enabled other than `Cancel`.
class SimpleStaticSheetModel : public AuthenticatorRequestSheetModel {
public:
SimpleStaticSheetModel() = default;
private:
// AuthenticatorRequestSheetModel:
bool IsBackButtonVisible() const override { return false; }
bool IsCancelButtonVisible() const override { return true; }
base::string16 GetCancelButtonLabel() const override {
return l10n_util::GetStringUTF16(IDS_CANCEL);
}
bool IsAcceptButtonVisible() const override { return false; }
bool IsAcceptButtonEnabled() const override { return false; }
base::string16 GetAcceptButtonLabel() const override {
return base::string16();
}
base::string16 GetStepTitle() const override {
return l10n_util::GetStringUTF16(IDS_WEBAUTHN_DIALOG_TITLE);
}
base::string16 GetStepDescription() const override {
return l10n_util::GetStringUTF16(IDS_WEBAUTHN_DIALOG_DESCRIPTION);
}
void OnBack() override { NOTREACHED(); }
void OnAccept() override { NOTREACHED(); }
void OnCancel() override {}
DISALLOW_COPY_AND_ASSIGN(SimpleStaticSheetModel);
};
} // namespace
// static // static
void ShowAuthenticatorRequestDialog( void ShowAuthenticatorRequestDialog(
content::WebContents* web_contents, content::WebContents* web_contents,
...@@ -37,49 +76,122 @@ void ShowAuthenticatorRequestDialog( ...@@ -37,49 +76,122 @@ void ShowAuthenticatorRequestDialog(
if (!manager) if (!manager)
return; return;
auto dialog = // Keep this logic in sync with AuthenticatorRequestDialogViewTestApi::Show.
std::make_unique<AuthenticatorRequestDialogView>(std::move(model)); auto dialog = std::make_unique<AuthenticatorRequestDialogView>(
web_contents, std::move(model));
constrained_window::ShowWebModalDialogViews(dialog.release(), web_contents); constrained_window::ShowWebModalDialogViews(dialog.release(), web_contents);
} }
// TODO(engedy): Create the sheet based on the model here.
AuthenticatorRequestDialogView::AuthenticatorRequestDialogView( AuthenticatorRequestDialogView::AuthenticatorRequestDialogView(
content::WebContents* web_contents,
std::unique_ptr<AuthenticatorRequestDialogModel> model) std::unique_ptr<AuthenticatorRequestDialogModel> model)
: model_(std::move(model)) { : content::WebContentsObserver(web_contents), model_(std::move(model)) {
model_->AddObserver(this); model_->AddObserver(this);
CreateContents();
// Currently, all sheets have a label on top and controls at the bottom.
// Consider moving this to AuthenticatorRequestSheetView if this changes.
SetBorder(views::CreateEmptyBorder(
views::LayoutProvider::Get()->GetDialogInsetsForContentType(
views::TEXT, views::CONTROL)));
SetLayoutManager(std::make_unique<views::FillLayout>());
auto sheet_view = std::make_unique<AuthenticatorRequestSheetView>(
std::make_unique<SimpleStaticSheetModel>());
sheet_view->InitChildViews();
sheet_ = sheet_view.get();
AddChildView(sheet_view.release());
} }
AuthenticatorRequestDialogView::~AuthenticatorRequestDialogView() { AuthenticatorRequestDialogView::~AuthenticatorRequestDialogView() {
model_->RemoveObserver(this); model_->RemoveObserver(this);
// AuthenticatorRequestDialogView is a WidgetDelegate, owned by views::Widget.
// It's only destroyed by Widget::OnNativeWidgetDestroyed() invoking
// DeleteDelegate(), and because WIDGET_OWNS_NATIVE_WIDGET, ~Widget() is
// invoked straight after, which destroys child views. views::View subclasses
// shouldn't be doing anything interesting in their destructors, so it should
// be okay to destroy the |sheet_| immediately after this line.
} }
void AuthenticatorRequestDialogView::CreateContents() { gfx::Size AuthenticatorRequestDialogView::CalculatePreferredSize() const {
SetLayoutManager(std::make_unique<views::FillLayout>()); const int width = ChromeLayoutProvider::Get()->GetDistanceMetric(
SetBorder(views::CreateEmptyBorder( DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH);
views::LayoutProvider::Get()->GetDialogInsetsForContentType( return gfx::Size(width, GetHeightForWidth(width));
views::TEXT, views::CONTROL))); }
auto description_label = std::make_unique<views::Label>(
l10n_util::GetStringUTF16(IDS_WEBAUTHN_DIALOG_DESCRIPTION), bool AuthenticatorRequestDialogView::Accept() {
CONTEXT_BODY_TEXT_SMALL, STYLE_SECONDARY); sheet()->model()->OnAccept();
description_label->SetHorizontalAlignment(gfx::ALIGN_LEFT); return false;
description_label->SetMultiLine(true); }
description_label->SetMaximumWidth(
ChromeLayoutProvider::Get()->GetDistanceMetric( bool AuthenticatorRequestDialogView::Cancel() {
DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH) - sheet()->model()->OnCancel();
margins().width()); return false;
AddChildView(description_label.release()); }
bool AuthenticatorRequestDialogView::Close() {
return true;
} }
int AuthenticatorRequestDialogView::GetDialogButtons() const { int AuthenticatorRequestDialogView::GetDialogButtons() const {
int button_mask = 0;
if (sheet()->model()->IsAcceptButtonVisible())
button_mask |= ui::DIALOG_BUTTON_OK;
if (sheet()->model()->IsCancelButtonVisible())
button_mask |= ui::DIALOG_BUTTON_CANCEL;
return button_mask;
}
int AuthenticatorRequestDialogView::GetDefaultDialogButton() const {
// The default button is either the `Ok` button or nothing.
if (sheet()->model()->IsAcceptButtonVisible())
return ui::DIALOG_BUTTON_OK;
return ui::DIALOG_BUTTON_NONE; return ui::DIALOG_BUTTON_NONE;
} }
base::string16 AuthenticatorRequestDialogView::GetDialogButtonLabel(
ui::DialogButton button) const {
switch (button) {
case ui::DIALOG_BUTTON_NONE:
break;
case ui::DIALOG_BUTTON_OK:
return sheet()->model()->GetAcceptButtonLabel();
case ui::DIALOG_BUTTON_CANCEL:
return sheet()->model()->GetCancelButtonLabel();
}
NOTREACHED();
return base::string16();
}
bool AuthenticatorRequestDialogView::IsDialogButtonEnabled(
ui::DialogButton button) const {
switch (button) {
case ui::DIALOG_BUTTON_NONE:
break;
case ui::DIALOG_BUTTON_OK:
return sheet()->model()->IsAcceptButtonEnabled();
case ui::DIALOG_BUTTON_CANCEL:
return true; // Cancel is always enabled if visible.
}
NOTREACHED();
return false;
}
ui::ModalType AuthenticatorRequestDialogView::GetModalType() const { ui::ModalType AuthenticatorRequestDialogView::GetModalType() const {
return ui::MODAL_TYPE_CHILD; return ui::MODAL_TYPE_CHILD;
} }
base::string16 AuthenticatorRequestDialogView::GetWindowTitle() const { base::string16 AuthenticatorRequestDialogView::GetWindowTitle() const {
return l10n_util::GetStringUTF16(IDS_WEBAUTHN_DIALOG_TITLE); return sheet()->model()->GetStepTitle();
}
bool AuthenticatorRequestDialogView::ShouldShowWindowTitle() const {
return false;
}
bool AuthenticatorRequestDialogView::ShouldShowCloseButton() const {
return false;
} }
void AuthenticatorRequestDialogView::OnModelDestroyed() { void AuthenticatorRequestDialogView::OnModelDestroyed() {
...@@ -91,3 +203,44 @@ void AuthenticatorRequestDialogView::OnRequestComplete() { ...@@ -91,3 +203,44 @@ void AuthenticatorRequestDialogView::OnRequestComplete() {
return; return;
GetWidget()->Close(); GetWidget()->Close();
} }
void AuthenticatorRequestDialogView::ReplaceCurrentSheetWith(
std::unique_ptr<AuthenticatorRequestSheetView> new_sheet) {
delete sheet_;
DCHECK(!has_children());
sheet_ = new_sheet.get();
AddChildView(new_sheet.release());
// The dialog button configuration is delegated to the |sheet_|, and the new
// sheet likely wants to provide a new configuration.
DialogModelChanged();
// The accessibility title is also sourced from the |sheet_|'s step title, so
// update it unless the widget is not yet shown or already being torn down.
if (!GetWidget())
return;
GetWidget()->UpdateWindowTitle();
// TODO(https://crbug.com/849323): Investigate how a web-modal dialog's
// lifetime compares to that of the parent WebContents. Take a conservative
// approach for now.
if (!web_contents())
return;
// The |dialog_manager| might temporarily be unavailable while te tab is being
// dragged from one browser window to the other.
auto* dialog_manager =
web_modal::WebContentsModalDialogManager::FromWebContents(web_contents());
if (!dialog_manager)
return;
// Update the dialog size and position, as the preferred size of the sheet
// might have changed.
constrained_window::UpdateWebContentsModalDialogPosition(
GetWidget(),
web_modal::WebContentsModalDialogManager::FromWebContents(web_contents())
->delegate()
->GetWebContentsModalDialogHost());
}
...@@ -8,9 +8,19 @@ ...@@ -8,9 +8,19 @@
#include <memory> #include <memory>
#include "base/macros.h" #include "base/macros.h"
#include "chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h"
#include "chrome/browser/webauthn/authenticator_request_dialog_model.h" #include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
#include "content/public/browser/web_contents_observer.h"
#include "ui/views/window/dialog_delegate.h" #include "ui/views/window/dialog_delegate.h"
namespace content {
class WebContents;
}
namespace test {
class AuthenticatorRequestDialogViewTestApi;
}
// A tab-modal dialog shown while a Web Authentication API request is active. // A tab-modal dialog shown while a Web Authentication API request is active.
// //
// This UI first allows the user the select the transport protocol they wish to // This UI first allows the user the select the transport protocol they wish to
...@@ -19,26 +29,46 @@ ...@@ -19,26 +29,46 @@
// selecting transport protocol, and finally shows success/failure indications. // selecting transport protocol, and finally shows success/failure indications.
class AuthenticatorRequestDialogView class AuthenticatorRequestDialogView
: public views::DialogDelegateView, : public views::DialogDelegateView,
public AuthenticatorRequestDialogModel::Observer { public AuthenticatorRequestDialogModel::Observer,
public content::WebContentsObserver {
public: public:
AuthenticatorRequestDialogView( AuthenticatorRequestDialogView(
content::WebContents* web_contents,
std::unique_ptr<AuthenticatorRequestDialogModel> model); std::unique_ptr<AuthenticatorRequestDialogModel> model);
~AuthenticatorRequestDialogView() override; ~AuthenticatorRequestDialogView() override;
protected: protected:
void CreateContents(); // Replaces the |sheet_| currently being shown in the dialog with |new_sheet|,
// destroying the old sheet. Also triggers updating the dialog buttons, window
// title (using the data provided by the new sheet), and size/position.
void ReplaceCurrentSheetWith(
std::unique_ptr<AuthenticatorRequestSheetView> new_sheet);
AuthenticatorRequestSheetView* sheet() const { return sheet_; }
// views::DialogDelegateView: // views::DialogDelegateView:
gfx::Size CalculatePreferredSize() const override;
bool Accept() override;
bool Cancel() override;
bool Close() override;
int GetDialogButtons() const override; int GetDialogButtons() const override;
int GetDefaultDialogButton() const override;
base::string16 GetDialogButtonLabel(ui::DialogButton button) const override;
bool IsDialogButtonEnabled(ui::DialogButton button) const override;
ui::ModalType GetModalType() const override; ui::ModalType GetModalType() const override;
base::string16 GetWindowTitle() const override; base::string16 GetWindowTitle() const override;
bool ShouldShowWindowTitle() const override;
bool ShouldShowCloseButton() const override;
// AuthenticatorRequestDialogModel::Observer: // AuthenticatorRequestDialogModel::Observer:
void OnModelDestroyed() override; void OnModelDestroyed() override;
void OnRequestComplete() override; void OnRequestComplete() override;
private: private:
friend class test::AuthenticatorRequestDialogViewTestApi;
std::unique_ptr<AuthenticatorRequestDialogModel> model_; std::unique_ptr<AuthenticatorRequestDialogModel> model_;
AuthenticatorRequestSheetView* sheet_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(AuthenticatorRequestDialogView); DISALLOW_COPY_AND_ASSIGN(AuthenticatorRequestDialogView);
}; };
......
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/webauthn/authenticator_request_dialog_view_test_api.h"
#include "chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.h"
#include "chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h"
#include "components/constrained_window/constrained_window_views.h"
namespace test {
// static
void AuthenticatorRequestDialogViewTestApi::Show(
content::WebContents* web_contents,
std::unique_ptr<AuthenticatorRequestDialogView> dialog) {
constrained_window::ShowWebModalDialogViews(dialog.release(), web_contents);
}
// static
void AuthenticatorRequestDialogViewTestApi::ReplaceCurrentSheet(
AuthenticatorRequestDialogView* dialog,
std::unique_ptr<AuthenticatorRequestSheetView> new_sheet) {
dialog->ReplaceCurrentSheetWith(std::move(new_sheet));
}
} // namespace test
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_VIEW_TEST_API_H_
#define CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_VIEW_TEST_API_H_
#include <memory>
class AuthenticatorRequestDialogView;
class AuthenticatorRequestSheetView;
namespace content {
class WebContents;
}
namespace test {
class AuthenticatorRequestDialogViewTestApi {
public:
// Shows a prepared |dialog| for testing; instead of automatically
// constructing it based on a given model.
static void Show(content::WebContents* web_contents,
std::unique_ptr<AuthenticatorRequestDialogView> dialog);
// Replaces the current sheet on |dialog| with |new_sheet|.
static void ReplaceCurrentSheet(
AuthenticatorRequestDialogView* dialog,
std::unique_ptr<AuthenticatorRequestSheetView> new_sheet);
};
} // namespace test
#endif // CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_VIEW_TEST_API_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/views/webauthn/authenticator_request_sheet_view.h"
#include "chrome/browser/ui/views/harmony/chrome_layout_provider.h"
#include "chrome/browser/ui/views/harmony/chrome_typography.h"
#include "chrome/browser/ui/webauthn/authenticator_request_sheet_model.h"
#include "components/strings/grit/components_strings.h"
#include "components/vector_icons/vector_icons.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/paint_vector_icon.h"
#include "ui/views/controls/button/image_button.h"
#include "ui/views/controls/button/image_button_factory.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
using views::BoxLayout;
AuthenticatorRequestSheetView::AuthenticatorRequestSheetView(
std::unique_ptr<AuthenticatorRequestSheetModel> model)
: model_(std::move(model)) {}
AuthenticatorRequestSheetView::~AuthenticatorRequestSheetView() = default;
void AuthenticatorRequestSheetView::InitChildViews() {
BoxLayout* box_layout = SetLayoutManager(std::make_unique<BoxLayout>(
BoxLayout::kVertical, gfx::Insets(),
views::LayoutProvider::Get()->GetDistanceMetric(
views::DISTANCE_UNRELATED_CONTROL_VERTICAL)));
std::unique_ptr<views::View> header_row = CreateHeaderRow();
AddChildView(header_row.release());
auto description_label = std::make_unique<views::Label>(
model()->GetStepDescription(), CONTEXT_BODY_TEXT_LARGE,
views::style::STYLE_PRIMARY);
description_label->SetMultiLine(true);
description_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
AddChildView(description_label.release());
std::unique_ptr<views::View> content_view = BuildStepSpecificContent();
if (content_view) {
auto* content_view_ptr = content_view.get();
AddChildView(content_view.release());
box_layout->SetFlexForView(content_view_ptr, 1);
}
}
std::unique_ptr<views::View>
AuthenticatorRequestSheetView::BuildStepSpecificContent() {
return nullptr;
}
void AuthenticatorRequestSheetView::ButtonPressed(views::Button* sender,
const ui::Event& event) {
DCHECK_EQ(sender, back_arrow_button_);
model()->OnBack();
}
std::unique_ptr<views::View> AuthenticatorRequestSheetView::CreateHeaderRow() {
auto header_row = std::make_unique<views::View>();
header_row->SetLayoutManager(std::make_unique<BoxLayout>(
BoxLayout::kHorizontal, gfx::Insets(),
views::LayoutProvider::Get()->GetDistanceMetric(
views::DISTANCE_RELATED_CONTROL_HORIZONTAL)));
auto title_label = std::make_unique<views::Label>(
model()->GetStepTitle(), views::style::CONTEXT_DIALOG_TITLE,
views::style::STYLE_PRIMARY);
title_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
if (model()->IsBackButtonVisible()) {
std::unique_ptr<views::ImageButton> back_arrow(
views::CreateVectorImageButton(this));
back_arrow->SetFocusForPlatform();
back_arrow->SetAccessibleName(l10n_util::GetStringUTF16(IDS_BACK_BUTTON));
views::SetImageFromVectorIcon(
back_arrow.get(), vector_icons::kBackArrowIcon,
color_utils::DeriveDefaultIconColor(title_label->enabled_color()));
back_arrow_button_ = back_arrow.get();
header_row->AddChildView(back_arrow.release());
}
header_row->AddChildView(title_label.release());
return header_row;
}
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_REQUEST_SHEET_VIEW_H_
#define CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_REQUEST_SHEET_VIEW_H_
#include <memory>
#include "base/macros.h"
#include "base/strings/string16.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/view.h"
class AuthenticatorRequestSheetModel;
// Defines the basic structure of sheets shown in the authenticator request
// dialog. Each sheet corresponds to a given step of the authentication flow,
// and encapsulates the controls above the Ok/Cancel buttons, namely:
// -- an optional `back icon`,
// -- the title of the current step,
// -- the description of the current step, and
// -- an optional view with step-specific content, added by subclasses, filling
// the rest of the space.
//
// +-------------------------------------------------+
// | (<-) Title of the current step |
// | |
// | Description text explaining to the user what |
// | this step is all about. |
// | |
// | +---------------------------------------------+ |
// | | | |
// | | Step-specific content view | |
// | | | |
// | | | |
// | +---------------------------------------------+ |
// +-------------------------------------------------+
// | OK CANCEL | <- Not part of this view.
// +-------------------------------------------------+
//
// TODO(https://crbug.com/852352): The Web Authentication and Web Payment APIs
// both use the concept of showing multiple "sheets" in a single dialog. To
// avoid code duplication, consider factoring out common parts.
class AuthenticatorRequestSheetView : public views::View,
public views::ButtonListener {
public:
explicit AuthenticatorRequestSheetView(
std::unique_ptr<AuthenticatorRequestSheetModel> model);
~AuthenticatorRequestSheetView() override;
AuthenticatorRequestSheetModel* model() { return model_.get(); }
// Creates the standard child views on this sheet, potentially including
// step-specific content if any.
void InitChildViews();
protected:
// Returns the step-specific view the derived sheet wishes to provide, if any.
virtual std::unique_ptr<views::View> BuildStepSpecificContent();
// views::ButtonListener:
void ButtonPressed(views::Button* sender, const ui::Event& event) override;
private:
// Creates the header row of the sheet, containing an optional back arrow,
// followed by the title of the sheet.
std::unique_ptr<views::View> CreateHeaderRow();
std::unique_ptr<AuthenticatorRequestSheetModel> model_;
views::Button* back_arrow_button_ = nullptr;
DISALLOW_COPY_AND_ASSIGN(AuthenticatorRequestSheetView);
};
#endif // CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_REQUEST_SHEET_VIEW_H_
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_WEBAUTHN_AUTHENTICATOR_REQUEST_SHEET_MODEL_H_
#define CHROME_BROWSER_UI_WEBAUTHN_AUTHENTICATOR_REQUEST_SHEET_MODEL_H_
#include <memory>
#include "base/strings/string16.h"
// The basic interface of models backing a given UI sheet shown in the WebAuthn
// request dialog; where each sheet, in turn, corresponds to one of `steps`
// defined by AuthenticatorRequestDialogModel.
//
// For each step, the model implementation encapsulates:
//
// (1) knowledge of the set of actions possible to the user at that step,
//
// (2) pieces of data required by views to visualise the sheet:
// (a) strings to use on labels/buttons rendered by the SheetView and the
// AuthenticatorRequestDialogView, and the state of the buttons,
// (b) data for additional step-specific contents rendered by SheetView
// subclasses, if any,
//
// (3) logic to handle user interactions with:
// (a) the `Back`, `Accept`, `Cancel`, buttons, even though the latter two
// are actually rendered by the AuthenticatorRequestDialogView,
// (b) the step-specific contents, if any.
//
class AuthenticatorRequestSheetModel {
public:
virtual ~AuthenticatorRequestSheetModel() {}
virtual bool IsBackButtonVisible() const = 0;
virtual bool IsCancelButtonVisible() const = 0;
virtual base::string16 GetCancelButtonLabel() const = 0;
virtual bool IsAcceptButtonVisible() const = 0;
virtual bool IsAcceptButtonEnabled() const = 0;
virtual base::string16 GetAcceptButtonLabel() const = 0;
virtual base::string16 GetStepTitle() const = 0;
virtual base::string16 GetStepDescription() const = 0;
virtual void OnBack() = 0;
virtual void OnAccept() = 0;
virtual void OnCancel() = 0;
};
#endif // CHROME_BROWSER_UI_WEBAUTHN_AUTHENTICATOR_REQUEST_SHEET_MODEL_H_
...@@ -852,6 +852,7 @@ test("browser_tests") { ...@@ -852,6 +852,7 @@ test("browser_tests") {
"../browser/ui/views/content_setting_bubble_contents_browsertest.cc", "../browser/ui/views/content_setting_bubble_contents_browsertest.cc",
"../browser/ui/views/device_chooser_browsertest.cc", "../browser/ui/views/device_chooser_browsertest.cc",
"../browser/ui/views/try_chrome_dialog_win/try_chrome_dialog_browsertest.cc", "../browser/ui/views/try_chrome_dialog_win/try_chrome_dialog_browsertest.cc",
"../browser/ui/views/webauthn/authenticator_dialog_view_browsertest.cc",
"../browser/ui/webauthn/authenticator_dialog_browsertest.cc", "../browser/ui/webauthn/authenticator_dialog_browsertest.cc",
"../browser/ui/webui/chrome_url_data_manager_browsertest.cc", "../browser/ui/webui/chrome_url_data_manager_browsertest.cc",
"../browser/ui/webui/chromeos/bluetooth_pairing_dialog_browsertest-inl.h", "../browser/ui/webui/chromeos/bluetooth_pairing_dialog_browsertest-inl.h",
......
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