Initial version of AuthenticationDialog.

Implement an initial MVP of the modal dialog that is shown during
WebAuthn requests. See the screenshot here: crbug.com/849323#c2.

The dialog is not yet interactive, even the `x` button does not yet
cancel the WebAuthn request (it does hide the dialog).

Bug: 849323
Change-Id: I3e1d9148f0727e33fb54e9bd1b747b84caa3c827
Reviewed-on: https://chromium-review.googlesource.com/964448
Commit-Queue: Balazs Engedy <[email protected]>
Reviewed-by: Trent Apted <[email protected]>
Cr-Commit-Position: refs/heads/master@{#565607}
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd
index bf7c0f3..0230a5d 100644
--- a/chrome/app/generated_resources.grd
+++ b/chrome/app/generated_resources.grd
@@ -10552,6 +10552,14 @@
         Got it
       </message>
     </if>
+
+    <!-- Web-modal dialog shown during Web Authenticaton API requests. -->
+    <message name="IDS_WEBAUTHN_DIALOG_TITLE" desc="Title of the dialog shown when a web site wants to register/verify a user's security key through the Web Authentication API.">
+      Verify your Security Key
+    </message>
+    <message name="IDS_WEBAUTHN_DIALOG_DESCRIPTION" desc="Contents of the dialog shown when a web site wants to register/verify a user's security key through the Web Authentication API.">
+      The site wants to verify your Security Key for added security for your account.
+    </message>
   </messages>
  </release>
 </grit>
diff --git a/chrome/browser/BUILD.gn b/chrome/browser/BUILD.gn
index fa2126cb..22d004e1 100644
--- a/chrome/browser/BUILD.gn
+++ b/chrome/browser/BUILD.gn
@@ -1545,6 +1545,8 @@
     "vr/vr_tab_helper.h",
     "web_data_service_factory.cc",
     "web_data_service_factory.h",
+    "webauthn/authenticator_request_dialog_model.cc",
+    "webauthn/authenticator_request_dialog_model.h",
     "webauthn/authenticator_request_scheduler.cc",
     "webauthn/authenticator_request_scheduler.h",
     "webauthn/chrome_authenticator_request_delegate.cc",
diff --git a/chrome/browser/ui/BUILD.gn b/chrome/browser/ui/BUILD.gn
index f4a26c4..b7ba043 100644
--- a/chrome/browser/ui/BUILD.gn
+++ b/chrome/browser/ui/BUILD.gn
@@ -739,6 +739,7 @@
     "uninstall_browser_prompt.h",
     "view_ids.h",
     "web_contents_sizer.h",
+    "webauthn/authenticator_request_dialog.h",
     "webui/about_ui.cc",
     "webui/about_ui.h",
     "webui/chrome_web_ui_controller_factory.cc",
@@ -2995,6 +2996,8 @@
       "views/translate/translate_bubble_view.h",
       "views/update_recommended_message_box.cc",
       "views/update_recommended_message_box.h",
+      "views/webauthn/authenticator_request_dialog_view.cc",
+      "views/webauthn/authenticator_request_dialog_view.h",
     ]
     deps += [
       "//chrome/browser/ui/views",
diff --git a/chrome/browser/ui/views/webauthn/OWNERS b/chrome/browser/ui/views/webauthn/OWNERS
new file mode 100644
index 0000000..8453a43
--- /dev/null
+++ b/chrome/browser/ui/views/webauthn/OWNERS
@@ -0,0 +1,5 @@
[email protected]
[email protected]
+
+# TEAM: [email protected]
+# COMPONENT: Blink>WebAuthentication
diff --git a/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.cc b/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.cc
new file mode 100644
index 0000000..6f8c4b7c
--- /dev/null
+++ b/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.cc
@@ -0,0 +1,93 @@
+// 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 "base/logging.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_typography.h"
+#include "chrome/grit/generated_resources.h"
+#include "components/constrained_window/constrained_window_views.h"
+#include "components/web_modal/web_contents_modal_dialog_manager.h"
+#include "ui/base/l10n/l10n_util.h"
+#include "ui/views/controls/label.h"
+#include "ui/views/layout/fill_layout.h"
+
+// static
+void ShowAuthenticatorRequestDialog(
+    content::WebContents* web_contents,
+    std::unique_ptr<AuthenticatorRequestDialogModel> model) {
+  // The authenticator request dialog will only be shown for common user-facing
+  // WebContents, which have a |manager|. Most other sources without managers,
+  // like service workers and extension background pages, do not allow WebAuthn
+  // requests to be issued in the first place.
+  // TODO(https://crbug.com/849323): There are some niche WebContents where the
+  // WebAuthn API is available, but there is no |manager| available. Currently,
+  // we will not be able to show a dialog, so the |model| will be immediately
+  // destroyed. The request may be able to still run to completion if it does
+  // not require any user input, otherise it will be blocked and time out. We
+  // should audit this.
+  auto* manager = web_modal::WebContentsModalDialogManager::FromWebContents(
+      constrained_window::GetTopLevelWebContents(web_contents));
+  if (!manager)
+    return;
+
+  auto dialog =
+      std::make_unique<AuthenticatorRequestDialogView>(std::move(model));
+  constrained_window::ShowWebModalDialogViews(dialog.release(), web_contents);
+}
+
+AuthenticatorRequestDialogView::AuthenticatorRequestDialogView(
+    std::unique_ptr<AuthenticatorRequestDialogModel> model)
+    : model_(std::move(model)) {
+  model_->AddObserver(this);
+  CreateContents();
+}
+
+AuthenticatorRequestDialogView::~AuthenticatorRequestDialogView() {
+  model_->RemoveObserver(this);
+}
+
+void AuthenticatorRequestDialogView::CreateContents() {
+  SetLayoutManager(std::make_unique<views::FillLayout>());
+  SetBorder(views::CreateEmptyBorder(
+      views::LayoutProvider::Get()->GetDialogInsetsForContentType(
+          views::TEXT, views::CONTROL)));
+  auto description_label = std::make_unique<views::Label>(
+      l10n_util::GetStringUTF16(IDS_WEBAUTHN_DIALOG_DESCRIPTION),
+      CONTEXT_BODY_TEXT_SMALL, STYLE_SECONDARY);
+  description_label->SetHorizontalAlignment(gfx::ALIGN_LEFT);
+  description_label->SetMultiLine(true);
+  description_label->SetMaximumWidth(
+      ChromeLayoutProvider::Get()->GetDistanceMetric(
+          DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH) -
+      margins().width());
+  AddChildView(description_label.release());
+}
+
+int AuthenticatorRequestDialogView::GetDialogButtons() const {
+  return ui::DIALOG_BUTTON_NONE;
+}
+
+ui::ModalType AuthenticatorRequestDialogView::GetModalType() const {
+  return ui::MODAL_TYPE_CHILD;
+}
+
+base::string16 AuthenticatorRequestDialogView::GetWindowTitle() const {
+  return l10n_util::GetStringUTF16(IDS_WEBAUTHN_DIALOG_TITLE);
+}
+
+void AuthenticatorRequestDialogView::OnModelDestroyed() {
+  NOTREACHED();
+}
+
+void AuthenticatorRequestDialogView::OnRequestComplete() {
+  if (!GetWidget())
+    return;
+  GetWidget()->Close();
+}
diff --git a/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.h b/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.h
new file mode 100644
index 0000000..63f4eba7
--- /dev/null
+++ b/chrome/browser/ui/views/webauthn/authenticator_request_dialog_view.h
@@ -0,0 +1,46 @@
+// 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_H_
+#define CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_VIEW_H_
+
+#include <memory>
+
+#include "base/macros.h"
+#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
+#include "ui/views/window/dialog_delegate.h"
+
+// 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
+// use to connect their security key (either USB, BLE, NFC, or internal), and
+// then guides them through the flow of setting up their security key using the
+// selecting transport protocol, and finally shows success/failure indications.
+class AuthenticatorRequestDialogView
+    : public views::DialogDelegateView,
+      public AuthenticatorRequestDialogModel::Observer {
+ public:
+  AuthenticatorRequestDialogView(
+      std::unique_ptr<AuthenticatorRequestDialogModel> model);
+  ~AuthenticatorRequestDialogView() override;
+
+ protected:
+  void CreateContents();
+
+  // views::DialogDelegateView:
+  int GetDialogButtons() const override;
+  ui::ModalType GetModalType() const override;
+  base::string16 GetWindowTitle() const override;
+
+  // AuthenticatorRequestDialogModel::Observer:
+  void OnModelDestroyed() override;
+  void OnRequestComplete() override;
+
+ private:
+  std::unique_ptr<AuthenticatorRequestDialogModel> model_;
+
+  DISALLOW_COPY_AND_ASSIGN(AuthenticatorRequestDialogView);
+};
+
+#endif  // CHROME_BROWSER_UI_VIEWS_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_VIEW_H_
diff --git a/chrome/browser/ui/webauthn/OWNERS b/chrome/browser/ui/webauthn/OWNERS
new file mode 100644
index 0000000..8453a43
--- /dev/null
+++ b/chrome/browser/ui/webauthn/OWNERS
@@ -0,0 +1,5 @@
[email protected]
[email protected]
+
+# TEAM: [email protected]
+# COMPONENT: Blink>WebAuthentication
diff --git a/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
new file mode 100644
index 0000000..efd8484
--- /dev/null
+++ b/chrome/browser/ui/webauthn/authenticator_dialog_browsertest.cc
@@ -0,0 +1,35 @@
+// 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 <memory>
+#include <utility>
+
+#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/webauthn/authenticator_request_dialog.h"
+#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
+
+class AuthenticatorDialogTest : public DialogBrowserTest {
+ public:
+  AuthenticatorDialogTest() = default;
+
+  // DialogBrowserTest:
+  void ShowUi(const std::string& unused_name) override {
+    auto model = std::make_unique<AuthenticatorRequestDialogModel>();
+    ShowAuthenticatorRequestDialog(
+        browser()->tab_strip_model()->GetActiveWebContents(), std::move(model));
+  }
+
+ private:
+  DISALLOW_COPY_AND_ASSIGN(AuthenticatorDialogTest);
+};
+
+// Run with:
+//   --gtest_filter=BrowserUiTest.Invoke --test-launcher-interactive \
+//   --ui=AuthenticatorDialogTest.InvokeUi_default
+IN_PROC_BROWSER_TEST_F(AuthenticatorDialogTest, InvokeUi_default) {
+  ShowAndVerifyUi();
+}
diff --git a/chrome/browser/ui/webauthn/authenticator_request_dialog.h b/chrome/browser/ui/webauthn/authenticator_request_dialog.h
new file mode 100644
index 0000000..c6608e3
--- /dev/null
+++ b/chrome/browser/ui/webauthn/authenticator_request_dialog.h
@@ -0,0 +1,21 @@
+// 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_DIALOG_H_
+#define CHROME_BROWSER_UI_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_H_
+
+#include <memory>
+
+class AuthenticatorRequestDialogModel;
+
+namespace content {
+class WebContents;
+}
+
+// Creates and shows the dialog for a given WebContents.
+void ShowAuthenticatorRequestDialog(
+    content::WebContents* web_contents,
+    std::unique_ptr<AuthenticatorRequestDialogModel> model);
+
+#endif  // CHROME_BROWSER_UI_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_H_
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.cc b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
new file mode 100644
index 0000000..a273f01
--- /dev/null
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.cc
@@ -0,0 +1,24 @@
+// 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/webauthn/authenticator_request_dialog_model.h"
+
+AuthenticatorRequestDialogModel::AuthenticatorRequestDialogModel() = default;
+AuthenticatorRequestDialogModel::~AuthenticatorRequestDialogModel() {
+  for (auto& observer : observers_)
+    observer.OnModelDestroyed();
+}
+
+void AuthenticatorRequestDialogModel::AddObserver(Observer* observer) {
+  observers_.AddObserver(observer);
+}
+
+void AuthenticatorRequestDialogModel::RemoveObserver(Observer* observer) {
+  observers_.RemoveObserver(observer);
+}
+
+void AuthenticatorRequestDialogModel::OnRequestComplete() {
+  for (auto& observer : observers_)
+    observer.OnRequestComplete();
+}
diff --git a/chrome/browser/webauthn/authenticator_request_dialog_model.h b/chrome/browser/webauthn/authenticator_request_dialog_model.h
new file mode 100644
index 0000000..149441a
--- /dev/null
+++ b/chrome/browser/webauthn/authenticator_request_dialog_model.h
@@ -0,0 +1,45 @@
+// 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_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_MODEL_H_
+#define CHROME_BROWSER_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_MODEL_H_
+
+#include "base/observer_list.h"
+
+// Encapsulates the model behind the Web Authentication request dialog.
+//
+// Ultimately, this will become an observer of the AuthenticatorRequest, and
+// contain the logic to figure out which steps the user needs to take, in which
+// order, to complete the authentication flow.
+class AuthenticatorRequestDialogModel {
+ public:
+  // Implemented by the dialog to observe this model and show the UI panels
+  // appropriate for the current step.
+  class Observer {
+   public:
+    // Called just before the model is destructed.
+    virtual void OnModelDestroyed() = 0;
+
+    // Called when the authentication request completes (successfully or not).
+    virtual void OnRequestComplete() {}
+  };
+
+  AuthenticatorRequestDialogModel();
+  ~AuthenticatorRequestDialogModel();
+
+  // The |observer| must either outlive the object, or unregister itself on its
+  // destruction.
+  void AddObserver(Observer* observer);
+  void RemoveObserver(Observer* observer);
+
+  // To be called when the Web Authentication request is complete.
+  void OnRequestComplete();
+
+ private:
+  base::ObserverList<Observer> observers_;
+
+  DISALLOW_COPY_AND_ASSIGN(AuthenticatorRequestDialogModel);
+};
+
+#endif  // CHROME_BROWSER_WEBAUTHN_AUTHENTICATOR_REQUEST_DIALOG_MODEL_H_
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
index c9993444..900c004 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.cc
@@ -17,12 +17,17 @@
 #include "chrome/browser/permissions/permission_request_manager.h"
 #include "chrome/browser/profiles/profile.h"
 #include "chrome/browser/ui/browser.h"
+#include "chrome/browser/ui/browser_dialogs.h"
 #include "chrome/browser/ui/browser_list.h"
 #include "chrome/browser/ui/browser_window.h"
 #include "chrome/browser/ui/tabs/tab_strip_model.h"
+#include "chrome/browser/ui/webauthn/authenticator_request_dialog.h"
+#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
+#include "chrome/common/chrome_switches.h"
 #include "chrome/common/pref_names.h"
 #include "components/prefs/pref_service.h"
 #include "content/public/browser/render_frame_host.h"
+#include "content/public/browser/web_contents.h"
 
 namespace {
 
@@ -49,8 +54,30 @@
 
 ChromeAuthenticatorRequestDelegate::ChromeAuthenticatorRequestDelegate(
     content::RenderFrameHost* render_frame_host)
-    : render_frame_host_(render_frame_host), weak_ptr_factory_(this) {}
-ChromeAuthenticatorRequestDelegate::~ChromeAuthenticatorRequestDelegate() {}
+    : render_frame_host_(render_frame_host), weak_ptr_factory_(this) {
+#if !defined(OS_ANDROID)
+  if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
+          switches::kWebAuthenticationUI)) {
+    return;
+  }
+  auto dialog_model = std::make_unique<AuthenticatorRequestDialogModel>();
+  weak_dialog_model_ = dialog_model.get();
+  weak_dialog_model_->AddObserver(this);
+  ShowAuthenticatorRequestDialog(
+      content::WebContents::FromRenderFrameHost(render_frame_host),
+      std::move(dialog_model));
+#endif
+}
+
+ChromeAuthenticatorRequestDelegate::~ChromeAuthenticatorRequestDelegate() {
+  // Currently, completion of the request is indicated by //content destroying
+  // this delegate.
+  if (weak_dialog_model_) {
+    // The dialog model may be destroyed after the OnRequestComplete call.
+    weak_dialog_model_->RemoveObserver(this);
+    weak_dialog_model_->OnRequestComplete();
+  }
+}
 
 base::WeakPtr<ChromeAuthenticatorRequestDelegate>
 ChromeAuthenticatorRequestDelegate::AsWeakPtr() {
@@ -130,3 +157,8 @@
   return false;
 #endif
 }
+
+void ChromeAuthenticatorRequestDelegate::OnModelDestroyed() {
+  DCHECK(weak_dialog_model_);
+  weak_dialog_model_ = nullptr;
+}
diff --git a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
index 6aff67f..4cec9cb 100644
--- a/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
+++ b/chrome/browser/webauthn/chrome_authenticator_request_delegate.h
@@ -6,14 +6,18 @@
 #define CHROME_BROWSER_WEBAUTHN_CHROME_AUTHENTICATOR_REQUEST_DELEGATE_H_
 
 #include "base/memory/weak_ptr.h"
+#include "chrome/browser/webauthn/authenticator_request_dialog_model.h"
 #include "content/public/browser/authenticator_request_client_delegate.h"
 
 namespace content {
 class RenderFrameHost;
 }
 
+class AuthenticatorRequestDialogModel;
+
 class ChromeAuthenticatorRequestDelegate
-    : public content::AuthenticatorRequestClientDelegate {
+    : public content::AuthenticatorRequestClientDelegate,
+      public AuthenticatorRequestDialogModel::Observer {
  public:
   // The |render_frame_host| must outlive this instance.
   explicit ChromeAuthenticatorRequestDelegate(
@@ -35,7 +39,12 @@
       base::OnceCallback<void(bool)> callback) override;
   bool IsFocused() override;
 
+  // AuthenticatorRequestDialogModel::Observer:
+  void OnModelDestroyed() override;
+
   content::RenderFrameHost* const render_frame_host_;
+  AuthenticatorRequestDialogModel* weak_dialog_model_ = nullptr;
+
   base::WeakPtrFactory<ChromeAuthenticatorRequestDelegate> weak_ptr_factory_;
 
   DISALLOW_COPY_AND_ASSIGN(ChromeAuthenticatorRequestDelegate);
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index eba6dd66..a1d192a 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -708,6 +708,10 @@
 // Prints version information and quits.
 const char kVersion[]                       = "version";
 
+// Enables the work-in-progress modal dialog shown for pending WebAuthn
+// requests.
+const char kWebAuthenticationUI[] = "enable-web-authentication-ui";
+
 // Allows privileged JS applications to trigger event logging for peer
 // connections, and to later upload those logs to a remote server.
 // * If "disable" or "disabled", remote-logging will be disabled.
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 1eaeab0..8112233 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -202,6 +202,7 @@
 extern const char kUserDataDir[];
 extern const char kValidateCrx[];
 extern const char kVersion[];
+extern const char kWebAuthenticationUI[];
 extern const char kWebRtcRemoteEventLog[];
 extern const char kWebRtcRemoteEventLogProactivePruningDelta[];
 extern const char kWebRtcRemoteEventLogUploadNoSuppression[];
diff --git a/chrome/test/BUILD.gn b/chrome/test/BUILD.gn
index 5c82dda..2148227f 100644
--- a/chrome/test/BUILD.gn
+++ b/chrome/test/BUILD.gn
@@ -851,6 +851,7 @@
       "../browser/ui/views/content_setting_bubble_contents_browsertest.cc",
       "../browser/ui/views/device_chooser_browsertest.cc",
       "../browser/ui/views/try_chrome_dialog_win/try_chrome_dialog_browsertest.cc",
+      "../browser/ui/webauthn/authenticator_dialog_browsertest.cc",
       "../browser/ui/webui/chrome_url_data_manager_browsertest.cc",
       "../browser/ui/webui/chromeos/bluetooth_pairing_dialog_browsertest-inl.h",
       "../browser/ui/webui/constrained_web_dialog_ui_browsertest.cc",