chromium/chrome/browser/win/chrome_select_file_dialog_factory.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 "chrome/browser/win/chrome_select_file_dialog_factory.h"

#include <utility>
#include <vector>

#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/win_util.h"
#include "chrome/browser/win/util_win_service.h"
#include "chrome/services/util_win/public/mojom/util_win.mojom.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/bindings/sync_call_restrictions.h"
#include "ui/shell_dialogs/execute_select_file_win.h"
#include "ui/shell_dialogs/select_file_dialog_win.h"
#include "ui/shell_dialogs/select_file_policy.h"

// Helper class to execute a select file operation on a utility process. It
// hides the complexity of managing the lifetime of the connection to the
// UtilWin service.
class UtilWinHelper {
 public:
  UtilWinHelper(const UtilWinHelper&) = delete;
  UtilWinHelper& operator=(const UtilWinHelper&) = delete;

  // Executes the select file operation and returns the result via
  // |on_select_file_executed_callback|.
  static void ExecuteSelectFile(
      ui::SelectFileDialog::Type type,
      const std::u16string& title,
      const base::FilePath& default_path,
      const std::vector<ui::FileFilterSpec>& filter,
      int file_type_index,
      const std::wstring& default_extension,
      HWND owner,
      ui::OnSelectFileExecutedCallback on_select_file_executed_callback);

 private:
  UtilWinHelper(
      ui::SelectFileDialog::Type type,
      const std::u16string& title,
      const base::FilePath& default_path,
      const std::vector<ui::FileFilterSpec>& filter,
      int file_type_index,
      const std::wstring& default_extension,
      HWND owner,
      ui::OnSelectFileExecutedCallback on_select_file_executed_callback);

  // Connection error handler for the interface pipe.
  void OnConnectionError();

  // Forwards the result of the file operation to the
  // |on_select_file_executed_callback_|.
  void OnSelectFileExecuted(const std::vector<base::FilePath>& paths,
                            int index);

  // The pointer to the UtilWin interface. This must be kept alive while waiting
  // for the response.
  mojo::Remote<chrome::mojom::UtilWin> remote_util_win_;

  // The callback that is invoked when the file operation is finished.
  ui::OnSelectFileExecutedCallback on_select_file_executed_callback_;

  SEQUENCE_CHECKER(sequence_checker_);
};

// static
void UtilWinHelper::ExecuteSelectFile(
    ui::SelectFileDialog::Type type,
    const std::u16string& title,
    const base::FilePath& default_path,
    const std::vector<ui::FileFilterSpec>& filter,
    int file_type_index,
    const std::wstring& default_extension,
    HWND owner,
    ui::OnSelectFileExecutedCallback on_select_file_executed_callback) {
  // Self-deleting when the select file operation completes.
  new UtilWinHelper(type, title, default_path, filter, file_type_index,
                    default_extension, owner,
                    std::move(on_select_file_executed_callback));
}

UtilWinHelper::UtilWinHelper(
    ui::SelectFileDialog::Type type,
    const std::u16string& title,
    const base::FilePath& default_path,
    const std::vector<ui::FileFilterSpec>& filter,
    int file_type_index,
    const std::wstring& default_extension,
    HWND owner,
    ui::OnSelectFileExecutedCallback on_select_file_executed_callback)
    : on_select_file_executed_callback_(
          std::move(on_select_file_executed_callback)) {
  remote_util_win_ = LaunchUtilWinServiceInstance();

  // |remote_util_win_| owns the callbacks and is guaranteed to be destroyed
  // before |this|, therefore making base::Unretained() safe to use.
  remote_util_win_.set_disconnect_handler(base::BindOnce(
      &UtilWinHelper::OnConnectionError, base::Unretained(this)));

  remote_util_win_->CallExecuteSelectFile(
      type, base::win::HandleToUint32(owner), title, default_path, filter,
      file_type_index, base::WideToUTF16(default_extension),
      base::BindOnce(&UtilWinHelper::OnSelectFileExecuted,
                     base::Unretained(this)));
}

void UtilWinHelper::OnConnectionError() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  std::move(on_select_file_executed_callback_).Run({}, 0);

  delete this;
}

void UtilWinHelper::OnSelectFileExecuted(
    const std::vector<base::FilePath>& paths,
    int index) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  std::move(on_select_file_executed_callback_).Run(paths, index);
  delete this;
}

void ExecuteSelectFileImpl(
    ui::SelectFileDialog::Type type,
    const std::u16string& title,
    const base::FilePath& default_path,
    const std::vector<ui::FileFilterSpec>& filter,
    int file_type_index,
    const std::wstring& default_extension,
    HWND owner,
    ui::OnSelectFileExecutedCallback on_select_file_executed_callback) {
  UtilWinHelper::ExecuteSelectFile(type, title, default_path, filter,
                                   file_type_index, default_extension, owner,
                                   std::move(on_select_file_executed_callback));
}

ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory() = default;

ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() = default;

ui::SelectFileDialog* ChromeSelectFileDialogFactory::Create(
    ui::SelectFileDialog::Listener* listener,
    std::unique_ptr<ui::SelectFilePolicy> policy) {
  return ui::CreateWinSelectFileDialog(
      listener, std::move(policy), base::BindRepeating(&ExecuteSelectFileImpl));
}