// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_ASH_HTTP_AUTH_DIALOG_H_
#define CHROME_BROWSER_ASH_HTTP_AUTH_DIALOG_H_
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "content/public/browser/login_delegate.h"
#include "content/public/browser/web_contents.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_delegate.h"
namespace ash {
// HTTP authentication is a feature that gates network resources behind a
// plaintext username/password ACL. This is typically implemented by
// web-browsers by showing a dialog to users part-way through a navigation with
// username/password textfields.
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Authentication
//
// There are some pieces of UI in ash-chrome unrelated to web-browsing that
// expect fine grained control over the dialog that is shown. This class
// provides a mechanism to do so.
class HttpAuthDialog : public content::LoginDelegate {
public:
// There are two ways for this class to be deleted.
// (1) The //content layer can decide the class is no longer necessary, in
// which case the destructor will be directly invoked. In this case, clear
// `callback_` and close the widget.
// (2) The widget can be closed by the user. In this case, invoke the
// `callback_`. This will cause (1) to trigger.
~HttpAuthDialog() override;
class ScopedEnabler {
public:
ScopedEnabler();
~ScopedEnabler();
ScopedEnabler(const ScopedEnabler&) = delete;
ScopedEnabler& operator=(const ScopedEnabler&) = delete;
};
// Prior to shipping Lacros, ash-chrome needs to handle both browser-based
// http-auth dialogs, and OS-based http-auth dialogs. Classes that need the
// latter should call this method and keep the returned ScopedEnabler alive.
// This forces the latter use-case.
// After shipping Lacros, this method will be unnecessary as the OS-based
// http-auth dialog will be the only remaining use case.
static std::unique_ptr<ScopedEnabler> Enable();
static bool IsEnabled();
static std::unique_ptr<HttpAuthDialog> Create(
const net::AuthChallengeInfo& auth_info,
content::WebContents* web_contents,
const GURL& url,
LoginAuthRequiredCallback auth_required_callback);
class Observer : public base::CheckedObserver {
public:
// Called when the dialog is shown.
virtual void HttpAuthDialogShown(content::WebContents* web_contents) = 0;
// Called when the dialog is cancelled. This can happen if the user presses
// the "cancel" button, or if the network request is cancelled.
virtual void HttpAuthDialogCancelled(
content::WebContents* web_contents) = 0;
// Called when the user presses the "continue" button. There is no guarantee
// that the username/password are valid.
virtual void HttpAuthDialogSupplied(content::WebContents* web_contents) = 0;
};
static void AddObserver(Observer* observer);
static void RemoveObserver(Observer* observer);
// Exposed for testing.
static std::vector<HttpAuthDialog*> GetAllDialogsForTest();
void SupplyCredentialsForTest(std::u16string_view username,
std::u16string_view password);
void CancelForTest();
private:
// A basic view with username/password text fields.
class DialogView : public views::View {
public:
DialogView(std::u16string_view authority, std::u16string_view explanation);
~DialogView() override;
// Intentionally return by copy so that the return value can be used even if
// DialogView is destroyed.
std::u16string GetUsername() const;
std::u16string GetPassword() const;
void SetCredentialsForTest(std::u16string_view username,
std::u16string_view password);
views::View* GetInitiallyFocusedView();
private:
// Non-owning refs to the input text fields.
raw_ptr<views::Textfield> username_field_;
raw_ptr<views::Textfield> password_field_;
};
// The constructor creates and shows a dialog.
HttpAuthDialog(const net::AuthChallengeInfo& auth_info,
content::WebContents* web_contents,
const GURL& url,
LoginAuthRequiredCallback auth_required_callback);
// In the production use-case, this method is called by views when the user
// clicks the OK button. The dialog is in the process of closing. This method
// merely needs to invoke `callback_`.
// When this method is called from tests, the dialog is not in the process of
// closing. Calling this method will invoke `callback_`, which will result in
// destruction of this object, which will close the dialog.
void SupplyCredentials(std::u16string_view username,
std::u16string_view password);
// Similar to `SupplyCredentials` except this is the path for clicking the
// cancel button or otherwise dismissing the dialog.
void Cancel();
static void NotifyShownAsync(content::WebContents* web_contents);
static void NotifySuppliedAsync(content::WebContents* web_contents);
static void NotifyCancelledAsync(content::WebContents* web_contents);
net::AuthChallengeInfo auth_info_;
// This class is owned by the //content layer. The only way to delete this
// class is to invoke this callback.
LoginAuthRequiredCallback callback_;
// Handles configuration and callbacks from the dialog.
views::DialogDelegate dialog_delegate_;
// `dialog_view_` is owned by the views framework. The dialog is showing if
// and only if these members are not `nullptr`. dialog_widget_ is created in
// the constructor and destroyed in the destructor, so this means that the
// lifetime of the dialog corresponds exactly to the lifetime of this class.
raw_ptr<DialogView> dialog_view_ = nullptr;
std::unique_ptr<views::Widget> dialog_widget_;
// Tracks the WebContents instance that is showing the dialog.
raw_ptr<content::WebContents> web_contents_ = nullptr;
base::WeakPtrFactory<HttpAuthDialog> weak_factory_{this};
};
} // namespace ash
#endif // CHROME_BROWSER_ASH_HTTP_AUTH_DIALOG_H_