chromium/chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/ui/webui/ash/parent_access/parent_access_dialog.h"

#include <memory>
#include <optional>
#include <utility>

#include "ash/public/cpp/shell_window_ids.h"
#include "ash/shell.h"
#include "ash/wm/window_dimmer.h"
#include "base/metrics/histogram_functions.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/webui/ash/parent_access/parent_access_metrics_utils.h"
#include "chrome/browser/ui/webui/ash/parent_access/parent_access_ui.mojom.h"
#include "chrome/browser/ui/webui/ash/system_web_dialog_delegate.h"
#include "chrome/common/webui_url_constants.h"
#include "chrome/grit/generated_resources.h"
#include "ui/aura/window.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"

namespace ash {

namespace {

constexpr int kDialogHeightDp = 526;
constexpr int kDialogWidthDp = 600;
constexpr float kDimmerOpacity = 0.7f;
}  // namespace

void RecordParentAccessWidgetShowDialogError(
    ParentAccessDialogProvider::ShowErrorType error_type,
    std::optional<parent_access_ui::mojom::ParentAccessParams::FlowType>
        flow_type) {
  base::UmaHistogramEnumeration(
      parent_access::GetHistogramTitleForFlowType(
          parent_access::kParentAccessWidgetShowDialogErrorHistogramBase,
          flow_type),
      error_type);

  // Always record metric for "all" flow type.
  base::UmaHistogramEnumeration(
      parent_access::GetHistogramTitleForFlowType(
          parent_access::kParentAccessWidgetShowDialogErrorHistogramBase,
          std::nullopt),
      error_type);
}

ParentAccessDialogProvider::ShowError ParentAccessDialogProvider::Show(
    parent_access_ui::mojom::ParentAccessParamsPtr params,
    ParentAccessDialog::Callback callback) {
  Profile* profile = ProfileManager::GetPrimaryUserProfile();
  if (!profile->IsChild()) {
    RecordParentAccessWidgetShowDialogError(
        ParentAccessDialogProvider::ShowErrorType::kNotAChildUser,
        params->flow_type);
    return ParentAccessDialogProvider::ShowError::kNotAChildUser;
  }

  if (ParentAccessDialog::GetInstance()) {
    RecordParentAccessWidgetShowDialogError(
        ParentAccessDialogProvider::ShowErrorType::kAlreadyVisible,
        params->flow_type);
    return ParentAccessDialogProvider::ShowError::kDialogAlreadyVisible;
  }

  DCHECK(ParentAccessDialog::GetInstance() == nullptr);
  // Note:  |dialog|'s memory is freed when
  // SystemWebDialogDelegate::OnDialogClosed() is called.
  ParentAccessDialog* dialog =
      new ParentAccessDialog(std::move(params), std::move(callback));

  // Dimmer should be shown before the dialog.
  dialog->ShowDimmer();
  dialog->ShowSystemDialogForBrowserContext(profile);

  return ParentAccessDialogProvider::ShowError::kNone;
}

// static
ParentAccessDialog* ParentAccessDialog::GetInstance() {
  return static_cast<ParentAccessDialog*>(
      SystemWebDialogDelegate::FindInstance(chrome::kChromeUIParentAccessURL));
}

ui::mojom::ModalType ParentAccessDialog::GetDialogModalType() const {
  return ui::mojom::ModalType::kSystem;
}

void ParentAccessDialog::GetDialogSize(gfx::Size* size) const {
  size->SetSize(kDialogWidthDp, kDialogHeightDp);
}

bool ParentAccessDialog::ShouldCloseDialogOnEscape() const {
  return true;
}

bool ParentAccessDialog::ShouldShowDialogTitle() const {
  return false;
}
// The close button is implemented in the WebUI itself.
bool ParentAccessDialog::ShouldShowCloseButton() const {
  return false;
}

parent_access_ui::mojom::ParentAccessParamsPtr
ParentAccessDialog::CloneParentAccessParams() {
  return parent_access_params_->Clone();
}

void ParentAccessDialog::SetApproved(const std::string& parent_access_token,
                                     const base::Time& expire_timestamp) {
  auto result = std::make_unique<ParentAccessDialog::Result>();
  result->status = ParentAccessDialog::Result::Status::kApproved;
  result->parent_access_token = parent_access_token;
  result->parent_access_token_expire_timestamp = expire_timestamp;
  CloseWithResult(std::move(result));
}

void ParentAccessDialog::SetDeclined() {
  auto result = std::make_unique<ParentAccessDialog::Result>();
  result->status = ParentAccessDialog::Result::Status::kDeclined;
  CloseWithResult(std::move(result));
}

void ParentAccessDialog::SetCanceled() {
  auto result = std::make_unique<ParentAccessDialog::Result>();
  result->status = ParentAccessDialog::Result::Status::kCanceled;
  CloseWithResult(std::move(result));
}

void ParentAccessDialog::SetDisabled() {
  auto result = std::make_unique<ParentAccessDialog::Result>();
  result->status = ParentAccessDialog::Result::Status::kDisabled;
  CloseWithResult(std::move(result));
}

void ParentAccessDialog::SetError() {
  auto result = std::make_unique<ParentAccessDialog::Result>();
  result->status = ParentAccessDialog::Result::Status::kError;
  // Don't close dialog on error state, as user will close the dialog manually
  // after seeing the error.
  result_ = std::move(result);
}

parent_access_ui::mojom::ParentAccessParams*
ParentAccessDialog::GetParentAccessParamsForTest() const {
  return parent_access_params_.get();
}

ParentAccessDialog::ParentAccessDialog(
    parent_access_ui::mojom::ParentAccessParamsPtr params,
    ParentAccessDialog::Callback callback)
    : SystemWebDialogDelegate(GURL(chrome::kChromeUIParentAccessURL),
                              /*title=*/std::u16string()),
      parent_access_params_(std::move(params)),
      callback_(std::move(callback)) {}

void ParentAccessDialog::ShowDimmer() {
  DCHECK_EQ(nullptr, dimmer_);
  dimmer_ = std::make_unique<WindowDimmer>(
      Shell::GetPrimaryRootWindow()->GetChildById(
          kShellWindowId_SystemModalContainer));
  dimmer_->SetDimOpacity(kDimmerOpacity);
  dimmer_->window()->Show();
}

ParentAccessDialog::~ParentAccessDialog() {
  std::move(callback_).Run(
      result_ ? std::move(result_)
              /* default status is kCanceled */
              : std::make_unique<ParentAccessDialog::Result>());
}

void ParentAccessDialog::CloseWithResult(
    std::unique_ptr<ParentAccessDialog::Result> result) {
  result_ = std::move(result);
  // This will trigger dialog destruction, which will in turn result in the
  // callback being called.
  Close();
}

}  // namespace ash