chromium/remoting/host/chromeos/message_box.cc

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

#include "remoting/host/chromeos/message_box.h"

#include <utility>

#include "base/memory/raw_ptr.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/views/controls/message_box_view.h"
#include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_delegate.h"

namespace remoting {

// MessageBox::Core creates the dialog using the views::DialogWidget.  The
// DialogWidget is created by the caller but its lifetime is managed by the
// NativeWidget.  The DialogWidget communicates with the caller using the
// DialogDelegateView interface, which must remain valid until DeleteDelegate()
// is called, at which the DialogDelegateView deletes itself.
//
// The Core class is introduced to abstract this awkward ownership model.  The
// Core and the MessageBox hold a raw references to each other, which is
// invalidated when either side are destroyed.
class MessageBox::Core : public views::DialogDelegateView {
  METADATA_HEADER(Core, views::DialogDelegateView)

 public:
  Core(const std::u16string& title_label,
       const std::u16string& message_label,
       const std::u16string& ok_label,
       const std::u16string& cancel_label,
       ResultCallback result_callback,
       MessageBox* message_box);
  Core(const Core&) = delete;
  Core& operator=(const Core&) = delete;

  // Mirrors the public MessageBox interface.
  void Show();
  void Hide();

  // views::DialogDelegateView:
  ui::mojom::ModalType GetModalType() const override;
  std::u16string GetWindowTitle() const override;
  views::View* GetContentsView() override;
  views::Widget* GetWidget() override;
  const views::Widget* GetWidget() const override;

  // Called by MessageBox::Core when it is destroyed.
  void OnMessageBoxDestroyed();

 private:
  const std::u16string title_label_;
  ResultCallback result_callback_;
  raw_ptr<MessageBox> message_box_;

  // Owned by the native widget hierarchy.
  raw_ptr<views::MessageBoxView> message_box_view_;
};

MessageBox::Core::Core(const std::u16string& title_label,
                       const std::u16string& message_label,
                       const std::u16string& ok_label,
                       const std::u16string& cancel_label,
                       ResultCallback result_callback,
                       MessageBox* message_box)
    : title_label_(title_label),
      result_callback_(std::move(result_callback)),
      message_box_(message_box),
      message_box_view_(new views::MessageBoxView(message_label)) {
  DCHECK(message_box_);
  SetButtonLabel(ui::mojom::DialogButton::kOk, ok_label);
  SetButtonLabel(ui::mojom::DialogButton::kCancel, cancel_label);

  auto run_callback = [](MessageBox::Core* core, Result result) {
    if (core->result_callback_) {
      std::move(core->result_callback_).Run(result);
    }
  };
  SetAcceptCallback(base::BindOnce(run_callback, base::Unretained(this), OK));
  SetCancelCallback(
      base::BindOnce(run_callback, base::Unretained(this), CANCEL));
  SetCloseCallback(
      base::BindOnce(run_callback, base::Unretained(this), CANCEL));
  RegisterDeleteDelegateCallback(base::BindOnce(
      [](Core* dialog) {
        if (dialog->message_box_) {
          dialog->message_box_->core_ = nullptr;
        }
      },
      this));
}

void MessageBox::Core::Show() {
  // The widget is owned by the NativeWidget.  See  comments in widget.h.
  views::Widget* widget =
      CreateDialogWidget(this, /* delegate */
                         nullptr /* parent window*/, nullptr /* parent view */);

  if (widget) {
    widget->Show();
  }
}

void MessageBox::Core::Hide() {
  if (GetWidget()) {
    GetWidget()->Close();
  }
}

ui::mojom::ModalType MessageBox::Core::GetModalType() const {
  return ui::mojom::ModalType::kSystem;
}

std::u16string MessageBox::Core::GetWindowTitle() const {
  return title_label_;
}

views::View* MessageBox::Core::GetContentsView() {
  return message_box_view_;
}

views::Widget* MessageBox::Core::GetWidget() {
  return message_box_view_->GetWidget();
}

const views::Widget* MessageBox::Core::GetWidget() const {
  return message_box_view_->GetWidget();
}

void MessageBox::Core::OnMessageBoxDestroyed() {
  DCHECK(message_box_);
  message_box_ = nullptr;
  // The callback should not be invoked after MessageBox is destroyed.
  result_callback_.Reset();
}

BEGIN_METADATA(MessageBox, Core)
END_METADATA

MessageBox::MessageBox(const std::u16string& title_label,
                       const std::u16string& message_label,
                       const std::u16string& ok_label,
                       const std::u16string& cancel_label,
                       ResultCallback result_callback)
    : core_(new Core(title_label,
                     message_label,
                     ok_label,
                     cancel_label,
                     std::move(result_callback),
                     this)) {}

void MessageBox::Show() {
  core_->Show();
}

MessageBox::~MessageBox() {
  DCHECK(thread_checker_.CalledOnValidThread());
  if (core_) {
    core_->OnMessageBoxDestroyed();
    core_->Hide();
    core_ = nullptr;
  }
}

}  // namespace remoting