910e62b5创建于 1月15日历史提交
// 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/views/autofill/edit_address_profile_view.h"

#include <memory>
#include <utility>

#include "base/functional/bind.h"
#include "base/notreached.h"
#include "chrome/browser/autofill/personal_data_manager_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/autofill/address_editor_controller.h"
#include "chrome/browser/ui/autofill/autofill_bubble_base.h"
#include "chrome/browser/ui/autofill/edit_address_profile_dialog_controller.h"
#include "chrome/browser/ui/autofill/edit_address_profile_view.h"
#include "chrome/browser/ui/tabs/public/tab_dialog_manager.h"
#include "chrome/browser/ui/tabs/public/tab_features.h"
#include "chrome/browser/ui/views/autofill/address_editor_view.h"
#include "chrome/browser/ui/views/chrome_layout_provider.h"
#include "components/autofill/core/common/autofill_features.h"
#include "components/constrained_window/constrained_window_views.h"
#include "components/strings/grit/components_strings.h"
#include "components/tabs/public/tab_interface.h"
#include "content/public/browser/web_contents.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/mojom/dialog_button.mojom.h"
#include "ui/base/mojom/ui_base_types.mojom-shared.h"
#include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h"
#include "ui/views/layout/fill_layout.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/widget/widget.h"

namespace autofill {

namespace {

class AutofillBubbleUI : public AutofillBubbleBase {
 public:
  AutofillBubbleUI(std::unique_ptr<views::Widget> dialog,
                   EditAddressProfileView* profile_view);
  AutofillBubbleUI(const AutofillBubbleUI&) = delete;
  AutofillBubbleUI& operator=(const AutofillBubbleUI&) = delete;
  ~AutofillBubbleUI() override;

 private:
  // Overrides from AutofillBubbleBase:
  void Hide() override;
  bool IsMouseHovered() const override;

  void CloseWidget(views::Widget::ClosedReason closed_reason);

  std::unique_ptr<views::Widget> dialog_;
  raw_ptr<EditAddressProfileView> profile_view_ = nullptr;
};

AutofillBubbleUI::AutofillBubbleUI(std::unique_ptr<views::Widget> dialog,
                                   EditAddressProfileView* profile_view)
    : dialog_(std::move(dialog)), profile_view_(profile_view) {
  dialog_->MakeCloseSynchronous(
      base::BindOnce(&AutofillBubbleUI::CloseWidget, base::Unretained(this)));
}

AutofillBubbleUI::~AutofillBubbleUI() = default;

void AutofillBubbleUI::Hide() {
  dialog_->Close();
}

bool AutofillBubbleUI::IsMouseHovered() const {
  // The edit view is not part of the bubbles managed by `BubbleManager`.
  NOTREACHED();
}

void AutofillBubbleUI::CloseWidget(views::Widget::ClosedReason closed_reason) {
  // We need to hold the dialog here so it remains alive long enough for the
  // stack to be cleaned up from the WidgetClosed() call. This keeps potential
  // dangling pointer checks from triggering as the stack unwinds.
  auto dialog = std::move(dialog_);
  profile_view_->WidgetClosed();
  dialog.reset();
}

}  // namespace

std::unique_ptr<AutofillBubbleBase> ShowEditAddressProfileDialogView(
    content::WebContents* web_contents,
    EditAddressProfileDialogController* controller) {
  auto* dialog = new EditAddressProfileView(controller);
  dialog->ShowForWebContents(web_contents);
  tabs::TabInterface* tab_interface =
      tabs::TabInterface::GetFromContents(web_contents);
  auto widget =
      tab_interface->GetTabFeatures()
          ->tab_dialog_manager()
          ->CreateAndShowDialog(
              dialog, std::make_unique<tabs::TabDialogManager::Params>());
  dialog->RequestFocus();
  return std::make_unique<AutofillBubbleUI>(std::move(widget), dialog);
}

EditAddressProfileView::EditAddressProfileView(
    EditAddressProfileDialogController* controller)
    : controller_(controller) {
  DCHECK(controller);

  // TODO(crbug.com/338254375): Remove the following line once this is the
  // default state for widgets.
  SetOwnershipOfNewWidget(views::Widget::InitParams::CLIENT_OWNS_WIDGET);

  SetButtons(static_cast<int>(ui::mojom::DialogButton::kOk) |
             static_cast<int>(ui::mojom::DialogButton::kCancel));
  SetModalType(ui::mojom::ModalType::kChild);
  SetShowCloseButton(false);
  set_fixed_width(views::LayoutProvider::Get()->GetDistanceMetric(
      views::DISTANCE_MODAL_DIALOG_PREFERRED_WIDTH));

  SetAcceptCallbackWithClose(base::BindRepeating(
      &EditAddressProfileView::OnAcceptButtonClicked, base::Unretained(this)));
  SetCancelCallback(base::BindOnce(
      &EditAddressProfileView::OnUserDecision, base::Unretained(this),
      AutofillClient::AddressPromptUserDecision::kEditDeclined));

  SetLayoutManager(std::make_unique<views::BoxLayout>(
      views::BoxLayout::Orientation::kVertical, gfx::Insets(),
      views::LayoutProvider::Get()->GetDistanceMetric(
          views::DISTANCE_RELATED_CONTROL_VERTICAL)));
  set_margins(ChromeLayoutProvider::Get()->GetInsetsMetric(
      views::InsetsMetric::INSETS_DIALOG));

  SetProperty(views::kElementIdentifierKey, kTopViewId);
  SetTitle(controller_->GetWindowTitle());
  SetButtonLabel(ui::mojom::DialogButton::kOk, controller_->GetOkButtonLabel());
  SetButtonLabel(ui::mojom::DialogButton::kCancel,
                 l10n_util::GetStringUTF16(
                     IDS_AUTOFILL_EDIT_ADDRESS_DIALOG_CANCEL_BUTTON_LABEL));
}

EditAddressProfileView::~EditAddressProfileView() = default;

void EditAddressProfileView::ShowForWebContents(
    content::WebContents* web_contents) {
  DCHECK(web_contents);
  auto address_editor_controller = std::make_unique<AddressEditorController>(
      controller_->GetProfileToEdit(),
      autofill::PersonalDataManagerFactory::GetForBrowserContext(
          web_contents->GetBrowserContext()),
      controller_->GetIsValidatable());

  // Storing subscription (which gets canceled in the destructor) in a property
  // secures using of Unretained(this).
  on_is_valid_change_subscription_ =
      address_editor_controller->AddIsValidChangedCallback(
          base::BindRepeating(&EditAddressProfileView::UpdateActionButtonState,
                              base::Unretained(this)));

  address_editor_view_ = AddChildView(std::make_unique<AddressEditorView>(
      std::move(address_editor_controller)));

  const std::u16string& footer_message = controller_->GetFooterMessage();
  if (!footer_message.empty()) {
    AddChildView(
        views::Builder<views::Label>()
            .SetText(footer_message)
            .SetTextStyle(views::style::STYLE_SECONDARY)
            .SetHorizontalAlignment(gfx::HorizontalAlignment::ALIGN_LEFT)
            .SetMultiLine(true)
            .Build());
  }
}

views::View* EditAddressProfileView::GetInitiallyFocusedView() {
  return address_editor_view_ ? address_editor_view_->initial_focus_view()
                              : nullptr;
}

void EditAddressProfileView::WidgetClosed() {
  if (controller_) {
    std::exchange(controller_, nullptr)
        ->OnDialogClosed(
            decision_,
            decision_ ==
                    AutofillClient::AddressPromptUserDecision::kEditAccepted
                ? base::optional_ref(address_editor_view_->GetAddressProfile())
                : std::nullopt);
  }
}

void EditAddressProfileView::ChildPreferredSizeChanged(views::View* child) {
  const int width = fixed_width();
  GetWidget()->SetSize(gfx::Size(width, GetHeightForWidth(width)));
}

AddressEditorView* EditAddressProfileView::GetAddressEditorViewForTesting() {
  return address_editor_view_;
}

void EditAddressProfileView::OnUserDecision(
    AutofillClient::AddressPromptUserDecision decision) {
  decision_ = decision;
}

void EditAddressProfileView::UpdateActionButtonState(bool is_valid) {
  SetButtonEnabled(ui::mojom::DialogButton::kOk, is_valid);
}

bool EditAddressProfileView::OnAcceptButtonClicked() {
  bool is_form_valid = address_editor_view_->ValidateAllFields();
  if (is_form_valid) {
    OnUserDecision(AutofillClient::AddressPromptUserDecision::kEditAccepted);
  }
  return is_form_valid;
}

DEFINE_CLASS_ELEMENT_IDENTIFIER_VALUE(EditAddressProfileView, kTopViewId);

}  // namespace autofill