// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40285824): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "chrome/browser/ui/webui/ash/add_supervision/add_supervision_ui.h"
#include <memory>
#include <utility>
#include "base/command_line.h"
#include "base/functional/bind.h"
#include "base/system/sys_info.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/ui/views/chrome_web_dialog_view.h"
#include "chrome/browser/ui/webui/ash/add_supervision/add_supervision.mojom.h"
#include "chrome/browser/ui/webui/ash/add_supervision/add_supervision_handler_utils.h"
#include "chrome/browser/ui/webui/ash/add_supervision/add_supervision_metrics_recorder.h"
#include "chrome/browser/ui/webui/ash/add_supervision/confirm_signout_dialog.h"
#include "chrome/browser/ui/webui/webui_util.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/add_supervision_resources.h"
#include "chrome/grit/add_supervision_resources_map.h"
#include "chrome/grit/browser_resources.h"
#include "chrome/grit/generated_resources.h"
#include "chrome/grit/supervision_resources.h"
#include "chrome/grit/supervision_resources_map.h"
#include "components/google/core/common/google_util.h"
#include "content/public/browser/web_ui.h"
#include "content/public/browser/web_ui_data_source.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/resources/grit/ui_resources.h"
#include "ui/web_dialogs/web_dialog_delegate.h"
namespace ash {
namespace {
constexpr int kDialogHeightPx = 608;
constexpr int kDialogWidthPx = 768;
// Shows the dialog indicating that user has to sign out if supervision has been
// enabled for their account. Returns a boolean indicating whether the
// ConfirmSignoutDialog is being shown.
bool MaybeShowConfirmSignoutDialog() {
if (EnrollmentCompleted()) {
ConfirmSignoutDialog::Show();
return true;
}
return false;
}
const char kAddSupervisionDefaultURL[] =
"https://families.google.com/supervision/setup";
const char kAddSupervisionFlowType[] = "1";
const char kAddSupervisionSwitch[] = "add-supervision-url";
} // namespace
// AddSupervisionDialog implementations.
// static
void AddSupervisionDialog::Show() {
// Get the system singleton instance of the AddSupervisionDialog.
SystemWebDialogDelegate* current_instance = GetInstance();
if (current_instance) {
// Focus the dialog if it is already there. Currently, this is
// effectively a no-op, since the dialog is system-modal, but
// it's here nonethless so that if the dialog becomes non-modal
// at some point, the correct focus behavior occurs.
current_instance->Focus();
return;
}
// Note: |current_instance|'s memory is freed when
// SystemWebDialogDelegate::OnDialogClosed() is called.
current_instance = new AddSupervisionDialog();
current_instance->ShowSystemDialogForBrowserContext(
ProfileManager::GetPrimaryUserProfile());
// Record UMA metric that user has initiated the Add Supervision process.
AddSupervisionMetricsRecorder::GetInstance()->RecordAddSupervisionEnrollment(
AddSupervisionMetricsRecorder::EnrollmentState::kInitiated);
}
// static
AddSupervisionDialog* AddSupervisionDialog::GetInstance() {
return static_cast<AddSupervisionDialog*>(
SystemWebDialogDelegate::FindInstance(
chrome::kChromeUIAddSupervisionURL));
}
// static
void AddSupervisionDialog::Close() {
SystemWebDialogDelegate* current_instance = GetInstance();
if (current_instance) {
current_instance->Close();
}
}
// static
void AddSupervisionDialog::SetCloseOnEscape(bool enabled) {
AddSupervisionDialog* current_instance = GetInstance();
if (current_instance) {
current_instance->should_close_on_escape_ = enabled;
}
}
void AddSupervisionDialog::CloseNowForTesting() {
SystemWebDialogDelegate* current_instance = GetInstance();
if (current_instance) {
DCHECK(dialog_window()) << "No dialog window instance currently set.";
views::Widget::GetWidgetForNativeWindow(dialog_window())->CloseNow();
}
}
ui::mojom::ModalType AddSupervisionDialog::GetDialogModalType() const {
return ui::mojom::ModalType::kWindow;
}
void AddSupervisionDialog::GetDialogSize(gfx::Size* size) const {
size->SetSize(kDialogWidthPx, kDialogHeightPx);
}
bool AddSupervisionDialog::OnDialogCloseRequested() {
bool showing_confirm_dialog = MaybeShowConfirmSignoutDialog();
return !showing_confirm_dialog;
}
void AddSupervisionDialog::OnDialogWillClose() {
// Record UMA metric that user has closed the Add Supervision dialog.
AddSupervisionMetricsRecorder::GetInstance()->RecordAddSupervisionEnrollment(
AddSupervisionMetricsRecorder::EnrollmentState::kClosed);
}
bool AddSupervisionDialog::ShouldCloseDialogOnEscape() const {
return should_close_on_escape_;
}
bool AddSupervisionDialog::ShouldShowDialogTitle() const {
return false;
}
AddSupervisionDialog::AddSupervisionDialog()
: SystemWebDialogDelegate(
GURL(chrome::kChromeUIAddSupervisionURL),
l10n_util::GetStringUTF16(IDS_ADD_SUPERVISION_PAGE_TITLE)) {}
AddSupervisionDialog::~AddSupervisionDialog() = default;
// AddSupervisionUI implementations.
// static
signin::IdentityManager* AddSupervisionUI::test_identity_manager_ = nullptr;
AddSupervisionUI::AddSupervisionUI(content::WebUI* web_ui)
: ui::MojoWebUIController(web_ui) {
// Set up the basic page framework.
SetUpResources();
}
WEB_UI_CONTROLLER_TYPE_IMPL(AddSupervisionUI)
AddSupervisionUI::~AddSupervisionUI() = default;
// AddSupervisionHandler::Delegate:
bool AddSupervisionUI::CloseDialog() {
bool showing_confirm_dialog = MaybeShowConfirmSignoutDialog();
if (!showing_confirm_dialog) {
// We aren't showing the confirm dialog, so close the AddSupervisionDialog.
AddSupervisionDialog::Close();
}
return !showing_confirm_dialog;
}
// AddSupervisionHandler::Delegate:
void AddSupervisionUI::SetCloseOnEscape(bool enabled) {
AddSupervisionDialog::SetCloseOnEscape(enabled);
}
// static
void AddSupervisionUI::SetUpForTest(signin::IdentityManager* identity_manager) {
test_identity_manager_ = identity_manager;
}
void AddSupervisionUI::BindInterface(
mojo::PendingReceiver<add_supervision::mojom::AddSupervisionHandler>
receiver) {
signin::IdentityManager* identity_manager =
test_identity_manager_
? test_identity_manager_
: IdentityManagerFactory::GetForProfile(Profile::FromWebUI(web_ui()));
mojo_api_handler_ = std::make_unique<AddSupervisionHandler>(
std::move(receiver), web_ui(), identity_manager, this);
}
void AddSupervisionUI::SetUpResources() {
content::WebUIDataSource* source = content::WebUIDataSource::CreateAndAdd(
Profile::FromWebUI(web_ui()), chrome::kChromeUIAddSupervisionHost);
webui::EnableTrustedTypesCSP(source);
// Initialize supervision URL from the command-line arguments (if provided).
supervision_url_ = GetAddSupervisionURL();
if (!allow_non_google_url_for_tests_) {
DCHECK(supervision_url_.DomainIs("google.com"));
}
source->EnableReplaceI18nInJS();
// Forward data to the WebUI.
source->AddResourcePaths(
base::make_span(kAddSupervisionResources, kAddSupervisionResourcesSize));
source->AddResourcePaths(
base::make_span(kSupervisionResources, kSupervisionResourcesSize));
source->AddLocalizedString("pageTitle", IDS_ADD_SUPERVISION_PAGE_TITLE);
source->AddLocalizedString("webviewLoadingMessage",
IDS_ADD_SUPERVISION_WEBVIEW_LOADING_MESSAGE);
source->AddLocalizedString("supervisedUserErrorDescription",
IDS_SUPERVISED_USER_ERROR_DESCRIPTION);
source->AddLocalizedString("supervisedUserErrorTitle",
IDS_SUPERVISED_USER_ERROR_TITLE);
source->AddLocalizedString("supervisedUserOfflineDescription",
IDS_SUPERVISED_USER_OFFLINE_DESCRIPTION);
source->AddLocalizedString("supervisedUserOfflineTitle",
IDS_SUPERVISED_USER_OFFLINE_TITLE);
source->UseStringsJs();
source->SetDefaultResource(IDR_ADD_SUPERVISION_ADD_SUPERVISION_HTML);
source->AddString("webviewUrl", supervision_url_.spec());
source->AddString("eventOriginFilter",
supervision_url_.DeprecatedGetOriginAsURL().spec());
source->AddString("platformVersion", base::SysInfo::OperatingSystemVersion());
source->AddString("flowType", kAddSupervisionFlowType);
// Forward the browser language code.
source->AddString(
"languageCode",
google_util::GetGoogleLocale(g_browser_process->GetApplicationLocale()));
}
// Returns the URL of the Add Supervision flow from the command-line switch,
// or the default value if it's not defined.
GURL AddSupervisionUI::GetAddSupervisionURL() {
std::string url;
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(kAddSupervisionSwitch)) {
url = command_line->GetSwitchValueASCII(kAddSupervisionSwitch);
// The URL should only be set on the command line for testing purposes,
// which may include pointing to a non-google URL (i.e. http://localhost/).
// Therefore, we allow non-Google URLs in this instance.
allow_non_google_url_for_tests_ = true;
} else {
url = kAddSupervisionDefaultURL;
}
const GURL result(url);
DCHECK(result.is_valid()) << "Invalid URL \"" << url << "\" for switch \""
<< kAddSupervisionSwitch << "\"";
return result;
}
} // namespace ash