| // Copyright 2012 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ui/shell_dialogs/select_file_dialog_mac.h" |
| |
| #include "base/check.h" |
| #include "base/functional/bind.h" |
| #include "base/notreached.h" |
| #include "base/ranges/algorithm.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "components/remote_cocoa/app_shim/select_file_dialog_bridge.h" |
| #include "components/remote_cocoa/browser/window.h" |
| #include "components/remote_cocoa/common/native_widget_ns_window.mojom.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| #include "ui/shell_dialogs/select_file_policy.h" |
| #include "ui/shell_dialogs/selected_file_info.h" |
| #include "url/gurl.h" |
| |
| using remote_cocoa::mojom::SelectFileDialogType; |
| using remote_cocoa::mojom::SelectFileTypeInfo; |
| using remote_cocoa::mojom::SelectFileTypeInfoPtr; |
| |
| namespace ui { |
| |
| using Type = SelectFileDialog::Type; |
| using FileTypeInfo = SelectFileDialog::FileTypeInfo; |
| |
| SelectFileDialogImpl::SelectFileDialogImpl( |
| Listener* listener, |
| std::unique_ptr<ui::SelectFilePolicy> policy) |
| : SelectFileDialog(listener, std::move(policy)), weak_factory_(this) {} |
| |
| bool SelectFileDialogImpl::IsRunning(gfx::NativeWindow parent_window) const { |
| for (const auto& dialog_data : dialog_data_list_) { |
| if (dialog_data.parent_window == parent_window) |
| return true; |
| } |
| return false; |
| } |
| |
| void SelectFileDialogImpl::ListenerDestroyed() { |
| listener_ = nullptr; |
| } |
| |
| void SelectFileDialogImpl::FileWasSelected( |
| DialogData* dialog_data, |
| bool is_multi, |
| bool was_cancelled, |
| const std::vector<base::FilePath>& files, |
| int index, |
| const std::vector<std::string>& file_tags) { |
| auto it = base::ranges::find(dialog_data_list_, dialog_data, |
| [](const DialogData& d) { return &d; }); |
| DCHECK(it != dialog_data_list_.end()); |
| void* params = dialog_data->params; |
| dialog_data_list_.erase(it); |
| |
| if (dialog_closed_callback_for_testing_) |
| dialog_closed_callback_for_testing_.Run(); |
| |
| if (!listener_) |
| return; |
| |
| if (was_cancelled || files.empty()) { |
| listener_->FileSelectionCanceled(params); |
| } else { |
| if (is_multi) { |
| listener_->MultiFilesSelected(FilePathListToSelectedFileInfoList(files), |
| params); |
| } else { |
| SelectedFileInfo file(files[0]); |
| file.file_tags = file_tags; |
| listener_->FileSelected(file, index, params); |
| } |
| } |
| } |
| |
| void SelectFileDialogImpl::SelectFileImpl( |
| Type type, |
| const std::u16string& title, |
| const base::FilePath& default_path, |
| const FileTypeInfo* file_types, |
| int file_type_index, |
| const base::FilePath::StringType& default_extension, |
| gfx::NativeWindow gfx_window, |
| void* params, |
| const GURL* caller) { |
| DCHECK(type == SELECT_FOLDER || type == SELECT_UPLOAD_FOLDER || |
| type == SELECT_EXISTING_FOLDER || type == SELECT_OPEN_FILE || |
| type == SELECT_OPEN_MULTI_FILE || type == SELECT_SAVEAS_FILE); |
| |
| hasMultipleFileTypeChoices_ = |
| file_types ? file_types->extensions.size() > 1 : true; |
| |
| // Add a new DialogData to the list. Note that it is safe to pass |
| // |dialog_data| by pointer because it will only be removed from the list when |
| // the callback is made or after the callback has been cancelled by |
| // |weak_factory_|. |
| dialog_data_list_.emplace_back(gfx_window, params); |
| DialogData& dialog_data = dialog_data_list_.back(); |
| |
| // Create a NSSavePanel for it. |
| auto* mojo_window = remote_cocoa::GetWindowMojoInterface(gfx_window); |
| auto receiver = dialog_data.select_file_dialog.BindNewPipeAndPassReceiver(); |
| if (mojo_window) { |
| mojo_window->CreateSelectFileDialog(std::move(receiver)); |
| } else { |
| NSWindow* ns_window = gfx_window.GetNativeNSWindow(); |
| mojo::MakeSelfOwnedReceiver( |
| std::make_unique<remote_cocoa::SelectFileDialogBridge>(ns_window), |
| std::move(receiver)); |
| } |
| |
| // Show the panel. |
| SelectFileDialogType mojo_type = SelectFileDialogType::kFolder; |
| switch (type) { |
| case SELECT_FOLDER: |
| mojo_type = SelectFileDialogType::kFolder; |
| break; |
| case SELECT_UPLOAD_FOLDER: |
| mojo_type = SelectFileDialogType::kUploadFolder; |
| break; |
| case SELECT_EXISTING_FOLDER: |
| mojo_type = SelectFileDialogType::kExistingFolder; |
| break; |
| case SELECT_OPEN_FILE: |
| mojo_type = SelectFileDialogType::kOpenFile; |
| break; |
| case SELECT_OPEN_MULTI_FILE: |
| mojo_type = SelectFileDialogType::kOpenMultiFile; |
| break; |
| case SELECT_SAVEAS_FILE: |
| mojo_type = SelectFileDialogType::kSaveAsFile; |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| |
| SelectFileTypeInfoPtr mojo_file_types; |
| if (file_types) { |
| mojo_file_types = SelectFileTypeInfo::New(); |
| mojo_file_types->extensions = file_types->extensions; |
| mojo_file_types->extension_description_overrides = |
| file_types->extension_description_overrides; |
| mojo_file_types->include_all_files = file_types->include_all_files; |
| mojo_file_types->keep_extension_visible = |
| file_types->keep_extension_visible; |
| } |
| |
| auto callback = base::BindOnce(&SelectFileDialogImpl::FileWasSelected, |
| weak_factory_.GetWeakPtr(), &dialog_data, |
| type == SELECT_OPEN_MULTI_FILE); |
| |
| dialog_data.select_file_dialog->Show( |
| mojo_type, title, default_path, std::move(mojo_file_types), |
| file_type_index, default_extension, std::move(callback)); |
| } |
| |
| SelectFileDialogImpl::DialogData::DialogData(gfx::NativeWindow parent_window_, |
| void* params_) |
| : parent_window(parent_window_), params(params_) {} |
| |
| SelectFileDialogImpl::DialogData::~DialogData() {} |
| |
| SelectFileDialogImpl::~SelectFileDialogImpl() { |
| // Clear |weak_factory_| beforehand, to ensure that no callbacks will be made |
| // when we cancel the NSSavePanels. |
| weak_factory_.InvalidateWeakPtrs(); |
| |
| // Walk through the open dialogs and issue the cancel callbacks that would |
| // have been made. |
| for (const auto& dialog_data : dialog_data_list_) { |
| if (listener_) |
| listener_->FileSelectionCanceled(dialog_data.params); |
| } |
| |
| // Cancel the NSSavePanels by destroying their bridges. |
| dialog_data_list_.clear(); |
| } |
| |
| bool SelectFileDialogImpl::HasMultipleFileTypeChoicesImpl() { |
| return hasMultipleFileTypeChoices_; |
| } |
| |
| SelectFileDialog* CreateSelectFileDialog( |
| SelectFileDialog::Listener* listener, |
| std::unique_ptr<SelectFilePolicy> policy) { |
| return new SelectFileDialogImpl(listener, std::move(policy)); |
| } |
| |
| } // namespace ui |