diff --git a/atom/browser/BUILD.gn b/atom/browser/BUILD.gn index 40b77c0918..0e367dbbdc 100644 --- a/atom/browser/BUILD.gn +++ b/atom/browser/BUILD.gn @@ -118,6 +118,8 @@ source_set("browser") { "browser_context_keyed_service_factories.cc", "browser_context_keyed_service_factories.h", "browser_observer.h", + "//chrome/browser/extensions/api/file_system/file_entry_picker.cc", + "//chrome/browser/extensions/api/file_system/file_entry_picker.h", "common_web_contents_delegate.cc", "common_web_contents_delegate.h", "javascript_environment.cc", @@ -156,7 +158,6 @@ source_set("browser") { "ui/atom_menu_model.cc", "ui/atom_menu_model.h", "ui/drag_util.h", - "ui/file_dialog.h", "ui/message_box.h", "ui/tray_icon.cc", "ui/tray_icon.h", @@ -206,7 +207,6 @@ source_set("browser") { "ui/cocoa/atom_menu_controller.h", "ui/cocoa/atom_menu_controller.mm", "ui/drag_util_mac.mm", - "ui/file_dialog_mac.mm", "ui/message_box_mac.mm", "ui/tray_icon_cocoa.h", "ui/tray_icon_cocoa.mm", @@ -247,7 +247,6 @@ source_set("browser") { "browser_win.cc", "native_window_views_win.cc", "relauncher_win.cc", - "ui/file_dialog_win.cc", "ui/message_box_win.cc", "ui/tray_icon_win.cc", "ui/win/atom_desktop_window_tree_host_win.cc", @@ -276,7 +275,6 @@ source_set("browser") { sources += [ "browser_linux.cc", "relauncher_linux.cc", - "ui/file_dialog_gtk.cc", "ui/message_box_gtk.cc", "ui/tray_icon_gtk.cc", "ui/tray_icon_gtk.h", diff --git a/atom/browser/api/atom_api_dialog.cc b/atom/browser/api/atom_api_dialog.cc index e218af00e4..2910635c7a 100644 --- a/atom/browser/api/atom_api_dialog.cc +++ b/atom/browser/api/atom_api_dialog.cc @@ -8,29 +8,57 @@ #include "atom/browser/api/atom_api_window.h" #include "atom/browser/native_window.h" -#include "atom/browser/ui/file_dialog.h" #include "atom/browser/ui/message_box.h" #include "atom/common/native_mate_converters/callback.h" #include "atom/common/native_mate_converters/file_path_converter.h" #include "atom/common/native_mate_converters/image_converter.h" +#include "base/bind.h" +#include "base/path_service.h" +#include "chrome/browser/extensions/api/file_system/file_entry_picker.h" +#include "chrome/common/chrome_paths.h" #include "native_mate/dictionary.h" +#include "vendor/brightray/browser/inspectable_web_contents.h" #include "atom/common/node_includes.h" +namespace file_dialog { + +typedef base::Callback& paths)> DialogCallback; + +struct DialogSettings { + atom::NativeWindow* parent_window = nullptr; + base::FilePath default_path; + std::vector> extensions; + std::vector extension_description_overrides; + ui::SelectFileDialog::Type type; + bool include_all_files = true; +}; +} // namespace file_dialog + namespace mate { template<> -struct Converter { +struct Converter { static bool FromV8(v8::Isolate* isolate, v8::Local val, - file_dialog::Filter* out) { - mate::Dictionary dict; - if (!ConvertFromV8(isolate, val, &dict)) - return false; - if (!dict.Get("name", &(out->first))) - return false; - if (!dict.Get("extensions", &(out->second))) + ui::SelectFileDialog::Type* out) { + std::string type; + if (!ConvertFromV8(isolate, val, &type)) return false; + if (type == "select-folder") { + *out = ui::SelectFileDialog::SELECT_FOLDER; + } else if (type == "select-upload-folder") { + *out = ui::SelectFileDialog::SELECT_UPLOAD_FOLDER; + } else if (type == "select-saveas-file") { + *out = ui::SelectFileDialog::SELECT_SAVEAS_FILE; + } else if (type == "select-open-file") { + *out = ui::SelectFileDialog::SELECT_OPEN_FILE; + } else if (type == "select-open-multi-file") { + *out = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE; + } else { + *out = ui::SelectFileDialog::SELECT_NONE; + } return true; } }; @@ -44,11 +72,12 @@ struct Converter { if (!ConvertFromV8(isolate, val, &dict)) return false; dict.Get("window", &(out->parent_window)); - dict.Get("title", &(out->title)); - dict.Get("buttonLabel", &(out->button_label)); dict.Get("defaultPath", &(out->default_path)); - dict.Get("filters", &(out->filters)); - dict.Get("properties", &(out->properties)); + dict.Get("type", &(out->type)); + dict.Get("extensions", &(out->extensions)); + dict.Get("extensionDescriptionOverrides", + &(out->extension_description_overrides)); + dict.Get("includeAllFiles", &(out->include_all_files)); return true; } }; @@ -84,34 +113,58 @@ void ShowMessageBox(int type, } } -void ShowOpenDialog(const file_dialog::DialogSettings& settings, - mate::Arguments* args) { - v8::Local peek = args->PeekNext(); - file_dialog::OpenDialogCallback callback; - if (mate::Converter::FromV8(args->isolate(), - peek, - &callback)) { - file_dialog::ShowOpenDialog(settings, callback); - } else { - std::vector paths; - if (file_dialog::ShowOpenDialog(settings, &paths)) - args->Return(paths); - } +void OnShowDialogSelected(const file_dialog::DialogCallback& callback, + const std::vector& paths) { + DCHECK(!paths.empty()); + callback.Run(true, paths); } -void ShowSaveDialog(const file_dialog::DialogSettings& settings, - mate::Arguments* args) { +void OnShowDialogCancelled(const file_dialog::DialogCallback& callback) { + std::vector files; + files.push_back(base::FilePath()); + callback.Run(false, files); +} + +void ShowDialog(const file_dialog::DialogSettings& settings, + mate::Arguments* args) { v8::Local peek = args->PeekNext(); - file_dialog::SaveDialogCallback callback; - if (mate::Converter::FromV8(args->isolate(), - peek, - &callback)) { - file_dialog::ShowSaveDialog(settings, callback); - } else { - base::FilePath path; - if (file_dialog::ShowSaveDialog(settings, &path)) - args->Return(path); + file_dialog::DialogCallback callback; + if (!args->GetNext(&callback)) { + args->ThrowError("`callback` is a required field"); + return; + } + + ui::SelectFileDialog::FileTypeInfo file_type_info; + for (size_t i = 0; i < settings.extensions.size(); ++i) { + file_type_info.extensions.push_back(settings.extensions[i]); + } + if (file_type_info.extensions.empty()) { + base::FilePath::StringType extension = + settings.default_path.FinalExtension(); + if (!extension.empty()) { + file_type_info.extensions.push_back( + std::vector()); + extension.erase(extension.begin()); // drop the . + file_type_info.extensions[0].push_back(extension); + } + } + + base::FilePath default_path = settings.default_path; + if (default_path.empty()) { + PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &default_path); } + file_type_info.include_all_files = settings.include_all_files; + file_type_info.extension_description_overrides = + settings.extension_description_overrides; + file_type_info.allowed_paths = + ui::SelectFileDialog::FileTypeInfo::NATIVE_OR_DRIVE_PATH; + new extensions::FileEntryPicker( + settings.parent_window->inspectable_web_contents()->GetWebContents(), + default_path, + file_type_info, + settings.type, + base::Bind(&OnShowDialogSelected, callback), + base::Bind(&OnShowDialogCancelled, callback)); } void Initialize(v8::Local exports, v8::Local unused, @@ -119,8 +172,7 @@ void Initialize(v8::Local exports, v8::Local unused, mate::Dictionary dict(context->GetIsolate(), exports); dict.SetMethod("showMessageBox", &ShowMessageBox); dict.SetMethod("showErrorBox", &atom::ShowErrorBox); - dict.SetMethod("showOpenDialog", &ShowOpenDialog); - dict.SetMethod("showSaveDialog", &ShowSaveDialog); + dict.SetMethod("showDialog", &ShowDialog); } } // namespace diff --git a/atom/browser/atom_download_manager_delegate.cc b/atom/browser/atom_download_manager_delegate.cc index 2e1fc884bb..92c093f7d5 100644 --- a/atom/browser/atom_download_manager_delegate.cc +++ b/atom/browser/atom_download_manager_delegate.cc @@ -5,12 +5,12 @@ #include "atom/browser/atom_download_manager_delegate.h" #include +#include -#include "atom/browser/api/atom_api_download_item.h" #include "atom/browser/native_window.h" -#include "atom/browser/ui/file_dialog.h" #include "base/bind.h" #include "base/files/file_util.h" +#include "chrome/browser/extensions/api/file_system/file_entry_picker.h" #include "chrome/browser/profiles/profile.h" #include "chrome/common/pref_names.h" #include "components/prefs/pref_service.h" @@ -18,6 +18,8 @@ #include "content/public/browser/browser_thread.h" #include "content/public/browser/download_manager.h" #include "net/base/filename_util.h" +#include "net/base/mime_util.h" +#include "vendor/brightray/browser/inspectable_web_contents.h" namespace atom { @@ -53,6 +55,50 @@ void AtomDownloadManagerDelegate::GetItemSavePath(content::DownloadItem* item, *path = download->GetSavePath(); } +bool AtomDownloadManagerDelegate::GetItemExtension( + content::DownloadItem* item, + base::FilePath::StringType* extension) { + v8::Isolate* isolate = v8::Isolate::GetCurrent(); + v8::Locker locker(isolate); + v8::HandleScope handle_scope(isolate); + api::DownloadItem* download = api::DownloadItem::FromWrappedClass(isolate, + item); + if (download && !download->GetMimeType().empty()) + return net::GetPreferredExtensionForMimeType( + download->GetMimeType(), extension); + return false; +} + +void AtomDownloadManagerDelegate:: OnDownloadItemSelected( + const content::DownloadTargetCallback& callback, + api::DownloadItem* download_item, + const std::vector& paths) { + DCHECK(!paths.empty()); + // Remember the last selected download directory. + Profile* profile = static_cast( + download_manager_->GetBrowserContext()); + profile->GetPrefs()->SetFilePath(prefs::kDownloadDefaultDirectory, + paths[0].DirName()); + if (download_item) + download_item->SetSavePath(paths[0]); + + callback.Run(paths[0], + content::DownloadItem::TARGET_DISPOSITION_PROMPT, + content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, paths[0], + content::DOWNLOAD_INTERRUPT_REASON_NONE); +} + +void AtomDownloadManagerDelegate::OnDownloadItemSelectionCancelled( + const content::DownloadTargetCallback& callback, + content::DownloadItem* item) { + item->Remove(); + base::FilePath path; + callback.Run(path, + content::DownloadItem::TARGET_DISPOSITION_PROMPT, + content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path, + content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED); +} + void AtomDownloadManagerDelegate::OnDownloadPathGenerated( int32_t download_id, const content::DownloadTargetCallback& callback, @@ -89,30 +135,33 @@ void AtomDownloadManagerDelegate::OnDownloadPathGenerated( GetItemSavePath(item, &path); // Show save dialog if save path was not set already on item - file_dialog::DialogSettings settings; - settings.parent_window = window; - settings.title = item->GetURL().spec(); - settings.default_path = target_path; - if (path.empty() && file_dialog::ShowSaveDialog(settings, &path)) { - // Remember the last selected download directory. - Profile* profile = static_cast( - download_manager_->GetBrowserContext()); - profile->GetPrefs()->SetFilePath(prefs::kDownloadDefaultDirectory, - path.DirName()); + ui::SelectFileDialog::FileTypeInfo file_type_info; + if (path.empty()) { + std::vector extensions; + base::FilePath::StringType extension; + if (GetItemExtension(item, &extension)) { + extensions.push_back(extension); + file_type_info.extensions.push_back(extensions); + } + file_type_info.include_all_files = true; + new extensions::FileEntryPicker( + window->inspectable_web_contents()->GetWebContents(), + target_path, + file_type_info, + ui::SelectFileDialog::SELECT_SAVEAS_FILE, + base::Bind(&AtomDownloadManagerDelegate::OnDownloadItemSelected, + base::Unretained(this), callback, download_item), + base::Bind(&AtomDownloadManagerDelegate::OnDownloadItemSelectionCancelled, + base::Unretained(this), callback, item)); + } else { + if (download_item) + download_item->SetSavePath(path); + + callback.Run(path, + content::DownloadItem::TARGET_DISPOSITION_PROMPT, + content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path, + content::DOWNLOAD_INTERRUPT_REASON_NONE); } - - if (path.empty()) - item->Remove(); - - if (download_item) - download_item->SetSavePath(path); - - callback.Run(path, - content::DownloadItem::TARGET_DISPOSITION_PROMPT, - content::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path, - path.empty() - ? content::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED - : content::DOWNLOAD_INTERRUPT_REASON_NONE); } void AtomDownloadManagerDelegate::Shutdown() { diff --git a/atom/browser/atom_download_manager_delegate.h b/atom/browser/atom_download_manager_delegate.h index 12675487fd..4a56364c95 100644 --- a/atom/browser/atom_download_manager_delegate.h +++ b/atom/browser/atom_download_manager_delegate.h @@ -6,7 +6,9 @@ #define ATOM_BROWSER_ATOM_DOWNLOAD_MANAGER_DELEGATE_H_ #include +#include +#include "atom/browser/api/atom_api_download_item.h" #include "base/memory/weak_ptr.h" #include "chrome/browser/download/download_path_reservation_tracker.h" #include "content/public/browser/download_manager_delegate.h" @@ -42,6 +44,18 @@ class AtomDownloadManagerDelegate : public content::DownloadManagerDelegate { // Get the save path set on the associated api::DownloadItem object void GetItemSavePath(content::DownloadItem* item, base::FilePath* path); + bool GetItemExtension(content::DownloadItem* item, + base::FilePath::StringType* extension); + + void OnDownloadItemSelected(const content::DownloadTargetCallback& callback, + api::DownloadItem* download_item, + const std::vector& paths); + + void OnDownloadItemSelectionCancelled( + const content::DownloadTargetCallback& callback, + content::DownloadItem* item); + + content::DownloadManager* download_manager_; base::WeakPtrFactory weak_ptr_factory_; diff --git a/atom/browser/common_web_contents_delegate.cc b/atom/browser/common_web_contents_delegate.cc index ef28803f54..bd2f6340ad 100644 --- a/atom/browser/common_web_contents_delegate.cc +++ b/atom/browser/common_web_contents_delegate.cc @@ -12,17 +12,19 @@ #include "atom/browser/atom_browser_context.h" #include "atom/browser/browser.h" #include "atom/browser/native_window.h" -#include "atom/browser/ui/file_dialog.h" #include "atom/browser/web_contents_permission_helper.h" #include "atom/common/atom_constants.h" #include "base/files/file_util.h" +#include "base/path_service.h" #include "base/strings/utf_string_conversions.h" #include "brave/browser/brave_javascript_dialog_manager.h" #include "chrome/browser/certificate_viewer.h" +#include "chrome/browser/extensions/api/file_system/file_entry_picker.h" #include "chrome/browser/file_select_helper.h" #include "chrome/browser/ssl/security_state_tab_helper.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_dialogs.h" +#include "chrome/common/chrome_paths.h" #include "chrome/common/pref_names.h" #include "chrome/grit/generated_resources.h" #include "components/pref_registry/pref_registry_syncable.h" @@ -171,7 +173,8 @@ bool IsDevToolsFileSystemAdded( CommonWebContentsDelegate::CommonWebContentsDelegate() : html_fullscreen_(false), native_fullscreen_(false), - devtools_file_system_indexer_(new DevToolsFileSystemIndexer) { + devtools_file_system_indexer_(new DevToolsFileSystemIndexer), + weak_ptr_factory_(this) { } CommonWebContentsDelegate::~CommonWebContentsDelegate() { @@ -329,25 +332,25 @@ void CommonWebContentsDelegate::DevToolsSaveToFile( auto it = saved_files_.find(url); if (it != saved_files_.end() && !save_as) { path = it->second; - } else { - file_dialog::DialogSettings settings; - settings.parent_window = owner_window(); - settings.title = url; - settings.default_path = base::FilePath::FromUTF8Unsafe(url); - if (!file_dialog::ShowSaveDialog(settings, &path)) { - base::Value url_value(url); - web_contents_->CallClientFunction( - "DevToolsAPI.canceledSaveURL", &url_value, nullptr, nullptr); - return; - } - } - - saved_files_[url] = path; - BrowserThread::PostTaskAndReply( + saved_files_[url] = path; + BrowserThread::PostTaskAndReply( BrowserThread::FILE, FROM_HERE, base::Bind(&WriteToFile, path, content), base::Bind(&CommonWebContentsDelegate::OnDevToolsSaveToFile, base::Unretained(this), url)); + } else { + base::FilePath default_path; + PathService::Get(chrome::DIR_DEFAULT_DOWNLOADS, &default_path); + default_path = default_path.Append(base::FilePath::FromUTF8Unsafe(url)); + new extensions::FileEntryPicker( + GetWebContents(), default_path, + ui::SelectFileDialog::FileTypeInfo(), + ui::SelectFileDialog::SELECT_SAVEAS_FILE, + base::Bind(&CommonWebContentsDelegate::OnSaveFileSelected, + weak_ptr_factory_.GetWeakPtr(), url, content), + base::Bind(&CommonWebContentsDelegate::OnSaveFileSelectionCancelled, + weak_ptr_factory_.GetWeakPtr(), url)); + } } void CommonWebContentsDelegate::DevToolsAppendToFile( @@ -394,37 +397,18 @@ void CommonWebContentsDelegate::DevToolsRequestFileSystems() { void CommonWebContentsDelegate::DevToolsAddFileSystem( const base::FilePath& file_system_path) { - base::FilePath path = file_system_path; - if (path.empty()) { - std::vector paths; - file_dialog::DialogSettings settings; - settings.parent_window = owner_window(); - settings.properties = file_dialog::FILE_DIALOG_OPEN_DIRECTORY; - if (!file_dialog::ShowOpenDialog(settings, &paths)) - return; - - path = paths[0]; + if (file_system_path.empty()) { + new extensions::FileEntryPicker( + GetWebContents(), file_system_path, + ui::SelectFileDialog::FileTypeInfo(), + ui::SelectFileDialog::SELECT_UPLOAD_FOLDER, + base::Bind(&CommonWebContentsDelegate::OnAddFileSelected, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&CommonWebContentsDelegate::OnAddFileSelectionCancelled, + weak_ptr_factory_.GetWeakPtr())); + } else { + DevToolsAddFileSystemInteral(file_system_path); } - - std::string file_system_id = RegisterFileSystem(GetDevToolsWebContents(), - path); - if (IsDevToolsFileSystemAdded(GetDevToolsWebContents(), path.AsUTF8Unsafe())) - return; - - FileSystem file_system = CreateFileSystemStruct(GetDevToolsWebContents(), - file_system_id, - path.AsUTF8Unsafe()); - std::unique_ptr file_system_value( - CreateFileSystemValue(file_system)); - - auto pref_service = GetPrefService(GetDevToolsWebContents()); - DictionaryPrefUpdate update(pref_service, prefs::kDevToolsFileSystemPaths); - update.Get()->SetWithoutPathExpansion( - path.AsUTF8Unsafe(), base::MakeUnique()); - - web_contents_->CallClientFunction("DevToolsAPI.fileSystemAdded", - file_system_value.get(), - nullptr, nullptr); } void CommonWebContentsDelegate::DevToolsRemoveFileSystem( @@ -501,6 +485,52 @@ void CommonWebContentsDelegate::DevToolsSearchInPath( file_system_path)); } +void CommonWebContentsDelegate::OnSaveFileSelected( + const std::string& url, + const std::string& content, + const std::vector& paths) { + DCHECK(!paths.empty()); + WriteToFile(paths[0], content); + OnDevToolsSaveToFile(url); +} +void CommonWebContentsDelegate::OnSaveFileSelectionCancelled( + const std::string url) { + base::Value url_value(url); + web_contents_->CallClientFunction( + "DevToolsAPI.canceledSaveURL", &url_value, nullptr, nullptr); +} + +void CommonWebContentsDelegate::OnAddFileSelected( + const std::vector& paths) { + DCHECK(!paths.empty()); + DevToolsAddFileSystemInteral(paths[0]); +} + +void CommonWebContentsDelegate::OnAddFileSelectionCancelled() {} + +void CommonWebContentsDelegate::DevToolsAddFileSystemInteral( + const base::FilePath& path) { + std::string file_system_id = RegisterFileSystem(GetDevToolsWebContents(), + path); + if (IsDevToolsFileSystemAdded(GetDevToolsWebContents(), path.AsUTF8Unsafe())) + return; + + FileSystem file_system = CreateFileSystemStruct(GetDevToolsWebContents(), + file_system_id, + path.AsUTF8Unsafe()); + std::unique_ptr file_system_value( + CreateFileSystemValue(file_system)); + + auto pref_service = GetPrefService(GetDevToolsWebContents()); + DictionaryPrefUpdate update(pref_service, prefs::kDevToolsFileSystemPaths); + update.Get()->SetWithoutPathExpansion( + path.AsUTF8Unsafe(), base::MakeUnique()); + + web_contents_->CallClientFunction("DevToolsAPI.fileSystemAdded", + file_system_value.get(), + nullptr, nullptr); +} + void CommonWebContentsDelegate::OnDevToolsSaveToFile( const std::string& url) { // Notify DevTools. diff --git a/atom/browser/common_web_contents_delegate.h b/atom/browser/common_web_contents_delegate.h index 082a456c12..771d87f2bc 100644 --- a/atom/browser/common_web_contents_delegate.h +++ b/atom/browser/common_web_contents_delegate.h @@ -112,6 +112,16 @@ class CommonWebContentsDelegate #endif private: + void OnSaveFileSelected(const std::string& url, + const std::string& content, + const std::vector& paths); + void OnSaveFileSelectionCancelled(const std::string url); + + void OnAddFileSelected(const std::vector& paths); + void OnAddFileSelectionCancelled(); + + void DevToolsAddFileSystemInteral(const base::FilePath& path); + // Callback for when DevToolsSaveToFile has completed. void OnDevToolsSaveToFile(const std::string& url); @@ -165,6 +175,8 @@ class CommonWebContentsDelegate DevToolsIndexingJobsMap; DevToolsIndexingJobsMap devtools_indexing_jobs_; + base::WeakPtrFactory weak_ptr_factory_; + DISALLOW_COPY_AND_ASSIGN(CommonWebContentsDelegate); }; diff --git a/atom/browser/ui/file_dialog.h b/atom/browser/ui/file_dialog.h deleted file mode 100644 index 95fffe6ebb..0000000000 --- a/atom/browser/ui/file_dialog.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright (c) 2013 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#ifndef ATOM_BROWSER_UI_FILE_DIALOG_H_ -#define ATOM_BROWSER_UI_FILE_DIALOG_H_ - -#include -#include -#include - -#include "base/callback_forward.h" -#include "base/files/file_path.h" - -namespace atom { -class NativeWindow; -} - -namespace file_dialog { - -// -typedef std::pair > Filter; -typedef std::vector Filters; - -enum FileDialogProperty { - FILE_DIALOG_OPEN_FILE = 1 << 0, - FILE_DIALOG_OPEN_DIRECTORY = 1 << 1, - FILE_DIALOG_MULTI_SELECTIONS = 1 << 2, - FILE_DIALOG_CREATE_DIRECTORY = 1 << 3, - FILE_DIALOG_SHOW_HIDDEN_FILES = 1 << 4, - FILE_DIALOG_PROMPT_TO_CREATE = 1 << 5, -}; - -typedef base::Callback& paths)> OpenDialogCallback; - -typedef base::Callback SaveDialogCallback; - -struct DialogSettings { - atom::NativeWindow* parent_window = nullptr; - std::string title; - std::string button_label; - base::FilePath default_path; - Filters filters; - int properties = 0; -}; - -bool ShowOpenDialog(const DialogSettings& settings, - std::vector* paths); - -void ShowOpenDialog(const DialogSettings& settings, - const OpenDialogCallback& callback); - -bool ShowSaveDialog(const DialogSettings& settings, - base::FilePath* path); - -void ShowSaveDialog(const DialogSettings& settings, - const SaveDialogCallback& callback); - -} // namespace file_dialog - -#endif // ATOM_BROWSER_UI_FILE_DIALOG_H_ diff --git a/atom/browser/ui/file_dialog_gtk.cc b/atom/browser/ui/file_dialog_gtk.cc deleted file mode 100644 index dd6a63787f..0000000000 --- a/atom/browser/ui/file_dialog_gtk.cc +++ /dev/null @@ -1,277 +0,0 @@ -// Copyright (c) 2014 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/ui/file_dialog.h" - -#include "atom/browser/native_window_views.h" -#include "atom/browser/unresponsive_suppressor.h" -#include "base/callback.h" -#include "base/files/file_util.h" -#include "base/strings/string_util.h" -#include "chrome/browser/ui/libgtkui/gtk_signal.h" -#include "chrome/browser/ui/libgtkui/gtk_util.h" -#include "ui/views/widget/desktop_aura/x11_desktop_handler.h" - -namespace file_dialog { - -namespace { - -// Makes sure that .jpg also shows .JPG. -gboolean FileFilterCaseInsensitive(const GtkFileFilterInfo* file_info, - std::string* file_extension) { - // Makes .* file extension matches all file types. - if (*file_extension == ".*") - return true; - return base::EndsWith( - file_info->filename, - *file_extension, base::CompareCase::INSENSITIVE_ASCII); -} - -// Deletes |data| when gtk_file_filter_add_custom() is done with it. -void OnFileFilterDataDestroyed(std::string* file_extension) { - delete file_extension; -} - -class FileChooserDialog { - public: - FileChooserDialog(GtkFileChooserAction action, - const DialogSettings& settings) - : parent_(static_cast(settings.parent_window)), - filters_(settings.filters) { - const char* confirm_text = GTK_STOCK_OK; - - if (!settings.button_label.empty()) - confirm_text = settings.button_label.c_str(); - else if (action == GTK_FILE_CHOOSER_ACTION_SAVE) - confirm_text = GTK_STOCK_SAVE; - else if (action == GTK_FILE_CHOOSER_ACTION_OPEN) - confirm_text = GTK_STOCK_OPEN; - - dialog_ = gtk_file_chooser_dialog_new( - settings.title.c_str(), - NULL, - action, - GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, - confirm_text, GTK_RESPONSE_ACCEPT, - NULL); - if (parent_) { - parent_->SetEnabled(false); - libgtkui::SetGtkTransientForAura(dialog_, parent_->GetNativeWindow()); - gtk_window_set_modal(GTK_WINDOW(dialog_), TRUE); - } - - if (action == GTK_FILE_CHOOSER_ACTION_SAVE) - gtk_file_chooser_set_do_overwrite_confirmation(GTK_FILE_CHOOSER(dialog_), - TRUE); - if (action != GTK_FILE_CHOOSER_ACTION_OPEN) - gtk_file_chooser_set_create_folders(GTK_FILE_CHOOSER(dialog_), TRUE); - - if (!settings.default_path.empty()) { - if (base::DirectoryExists(settings.default_path)) { - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog_), - settings.default_path.value().c_str()); - } else { - gtk_file_chooser_set_current_folder(GTK_FILE_CHOOSER(dialog_), - settings.default_path.DirName().value().c_str()); - gtk_file_chooser_set_current_name(GTK_FILE_CHOOSER(dialog_), - settings.default_path.BaseName().value().c_str()); - } - } - - if (!settings.filters.empty()) - AddFilters(settings.filters); - } - - ~FileChooserDialog() { - gtk_widget_destroy(dialog_); - if (parent_) - parent_->SetEnabled(true); - } - - void SetupProperties(int properties) { - if (properties & FILE_DIALOG_MULTI_SELECTIONS) - gtk_file_chooser_set_select_multiple(GTK_FILE_CHOOSER(dialog()), TRUE); - if (properties & FILE_DIALOG_SHOW_HIDDEN_FILES) - g_object_set(dialog(), "show-hidden", TRUE, NULL); - } - - void RunAsynchronous() { - g_signal_connect(dialog_, "delete-event", - G_CALLBACK(gtk_widget_hide_on_delete), NULL); - g_signal_connect(dialog_, "response", - G_CALLBACK(OnFileDialogResponseThunk), this); - gtk_widget_show_all(dialog_); - - // We need to call gtk_window_present after making the widgets visible to - // make sure window gets correctly raised and gets focus. - int time = ui::X11EventSource::GetInstance()->GetTimestamp();; - gtk_window_present_with_time(GTK_WINDOW(dialog_), time); - } - - void RunSaveAsynchronous(const SaveDialogCallback& callback) { - save_callback_ = callback; - RunAsynchronous(); - } - - void RunOpenAsynchronous(const OpenDialogCallback& callback) { - open_callback_ = callback; - RunAsynchronous(); - } - - base::FilePath GetFileName() const { - gchar* filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(dialog_)); - base::FilePath path = AddExtensionForFilename(filename); - g_free(filename); - return path; - } - - std::vector GetFileNames() const { - std::vector paths; - GSList* filenames = gtk_file_chooser_get_filenames( - GTK_FILE_CHOOSER(dialog_)); - for (GSList* iter = filenames; iter != NULL; iter = g_slist_next(iter)) { - base::FilePath path = AddExtensionForFilename( - static_cast(iter->data)); - g_free(iter->data); - paths.push_back(path); - } - g_slist_free(filenames); - return paths; - } - - CHROMEGTK_CALLBACK_1(FileChooserDialog, void, OnFileDialogResponse, int); - - GtkWidget* dialog() const { return dialog_; } - - private: - void AddFilters(const Filters& filters); - base::FilePath AddExtensionForFilename(const gchar* filename) const; - - atom::NativeWindowViews* parent_; - atom::UnresponsiveSuppressor unresponsive_suppressor_; - - GtkWidget* dialog_; - - Filters filters_; - SaveDialogCallback save_callback_; - OpenDialogCallback open_callback_; - - DISALLOW_COPY_AND_ASSIGN(FileChooserDialog); -}; - -void FileChooserDialog::OnFileDialogResponse(GtkWidget* widget, int response) { - gtk_widget_hide(dialog_); - - if (!save_callback_.is_null()) { - if (response == GTK_RESPONSE_ACCEPT) - save_callback_.Run(true, GetFileName()); - else - save_callback_.Run(false, base::FilePath()); - } else if (!open_callback_.is_null()) { - if (response == GTK_RESPONSE_ACCEPT) - open_callback_.Run(true, GetFileNames()); - else - open_callback_.Run(false, std::vector()); - } - delete this; -} - -void FileChooserDialog::AddFilters(const Filters& filters) { - for (size_t i = 0; i < filters.size(); ++i) { - const Filter& filter = filters[i]; - GtkFileFilter* gtk_filter = gtk_file_filter_new(); - - for (size_t j = 0; j < filter.second.size(); ++j) { - std::unique_ptr file_extension( - new std::string("." + filter.second[j])); - gtk_file_filter_add_custom( - gtk_filter, - GTK_FILE_FILTER_FILENAME, - reinterpret_cast(FileFilterCaseInsensitive), - file_extension.release(), - reinterpret_cast(OnFileFilterDataDestroyed)); - } - - gtk_file_filter_set_name(gtk_filter, filter.first.c_str()); - gtk_file_chooser_add_filter(GTK_FILE_CHOOSER(dialog_), gtk_filter); - } -} - -base::FilePath FileChooserDialog::AddExtensionForFilename( - const gchar* filename) const { - base::FilePath path(filename); - GtkFileFilter* selected_filter = - gtk_file_chooser_get_filter(GTK_FILE_CHOOSER(dialog_)); - if (!selected_filter) - return path; - - GSList* filters = gtk_file_chooser_list_filters(GTK_FILE_CHOOSER(dialog_)); - int i = g_slist_index(filters, selected_filter); - g_slist_free(filters); - if (i >= filters_.size()) - return path; - - const auto& extensions = filters_[i].second; - for (const auto& extension : extensions) { - if (extension == "*" || - base::EndsWith(path.value(), "." + extension, - base::CompareCase::INSENSITIVE_ASCII)) - return path; - } - - return path.ReplaceExtension(extensions[0]); -} - - -} // namespace - -bool ShowOpenDialog(const DialogSettings& settings, - std::vector* paths) { - GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; - if (settings.properties & FILE_DIALOG_OPEN_DIRECTORY) - action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; - FileChooserDialog open_dialog(action, settings); - open_dialog.SetupProperties(settings.properties); - - gtk_widget_show_all(open_dialog.dialog()); - int response = gtk_dialog_run(GTK_DIALOG(open_dialog.dialog())); - if (response == GTK_RESPONSE_ACCEPT) { - *paths = open_dialog.GetFileNames(); - return true; - } else { - return false; - } -} - -void ShowOpenDialog(const DialogSettings& settings, - const OpenDialogCallback& callback) { - GtkFileChooserAction action = GTK_FILE_CHOOSER_ACTION_OPEN; - if (settings.properties & FILE_DIALOG_OPEN_DIRECTORY) - action = GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER; - FileChooserDialog* open_dialog = new FileChooserDialog(action, settings); - open_dialog->SetupProperties(settings.properties); - open_dialog->RunOpenAsynchronous(callback); -} - -bool ShowSaveDialog(const DialogSettings& settings, - base::FilePath* path) { - FileChooserDialog save_dialog(GTK_FILE_CHOOSER_ACTION_SAVE, settings); - gtk_widget_show_all(save_dialog.dialog()); - int response = gtk_dialog_run(GTK_DIALOG(save_dialog.dialog())); - if (response == GTK_RESPONSE_ACCEPT) { - *path = save_dialog.GetFileName(); - return true; - } else { - return false; - } -} - -void ShowSaveDialog(const DialogSettings& settings, - const SaveDialogCallback& callback) { - FileChooserDialog* save_dialog = new FileChooserDialog( - GTK_FILE_CHOOSER_ACTION_SAVE, settings); - save_dialog->RunSaveAsynchronous(callback); -} - -} // namespace file_dialog diff --git a/atom/browser/ui/file_dialog_mac.mm b/atom/browser/ui/file_dialog_mac.mm deleted file mode 100644 index a3cf358e95..0000000000 --- a/atom/browser/ui/file_dialog_mac.mm +++ /dev/null @@ -1,198 +0,0 @@ -// Copyright (c) 2013 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/ui/file_dialog.h" - -#import -#import - -#include "atom/browser/native_window.h" -#include "base/files/file_util.h" -#include "base/mac/foundation_util.h" -#include "base/mac/mac_util.h" -#include "base/mac/scoped_cftyperef.h" -#include "base/strings/sys_string_conversions.h" - -namespace file_dialog { - -namespace { - -void SetAllowedFileTypes(NSSavePanel* dialog, const Filters& filters) { - NSMutableSet* file_type_set = [NSMutableSet set]; - for (size_t i = 0; i < filters.size(); ++i) { - const Filter& filter = filters[i]; - for (size_t j = 0; j < filter.second.size(); ++j) { - // If we meet a '*' file extension, we allow all the file types and no - // need to set the specified file types. - if (filter.second[j] == "*") { - [dialog setAllowsOtherFileTypes:YES]; - return; - } - base::ScopedCFTypeRef ext_cf( - base::SysUTF8ToCFStringRef(filter.second[j])); - [file_type_set addObject:base::mac::CFToNSCast(ext_cf.get())]; - } - } - - // Passing empty array to setAllowedFileTypes will cause exception. - NSArray* file_types = nil; - if ([file_type_set count]) - file_types = [file_type_set allObjects]; - - [dialog setAllowedFileTypes:file_types]; -} - -void SetupDialog(NSSavePanel* dialog, - const DialogSettings& settings) { - if (!settings.title.empty()) - [dialog setTitle:base::SysUTF8ToNSString(settings.title)]; - - if (!settings.button_label.empty()) - [dialog setPrompt:base::SysUTF8ToNSString(settings.button_label)]; - - NSString* default_dir = nil; - NSString* default_filename = nil; - if (!settings.default_path.empty()) { - if (base::DirectoryExists(settings.default_path)) { - default_dir = base::SysUTF8ToNSString(settings.default_path.value()); - } else { - default_dir = - base::SysUTF8ToNSString(settings.default_path.DirName().value()); - default_filename = - base::SysUTF8ToNSString(settings.default_path.BaseName().value()); - } - } - - if (default_dir) - [dialog setDirectoryURL:[NSURL fileURLWithPath:default_dir]]; - if (default_filename) - [dialog setNameFieldStringValue:default_filename]; - - if (settings.filters.empty()) - [dialog setAllowsOtherFileTypes:YES]; - else - SetAllowedFileTypes(dialog, settings.filters); -} - -void SetupDialogForProperties(NSOpenPanel* dialog, int properties) { - [dialog setCanChooseFiles:(properties & FILE_DIALOG_OPEN_FILE)]; - if (properties & FILE_DIALOG_OPEN_DIRECTORY) - [dialog setCanChooseDirectories:YES]; - if (properties & FILE_DIALOG_CREATE_DIRECTORY) - [dialog setCanCreateDirectories:YES]; - if (properties & FILE_DIALOG_MULTI_SELECTIONS) - [dialog setAllowsMultipleSelection:YES]; - if (properties & FILE_DIALOG_SHOW_HIDDEN_FILES) - [dialog setShowsHiddenFiles:YES]; -} - -// Run modal dialog with parent window and return user's choice. -int RunModalDialog(NSSavePanel* dialog, atom::NativeWindow* parent_window) { - __block int chosen = NSFileHandlingPanelCancelButton; - if (!parent_window || !parent_window->GetNativeWindow()) { - chosen = [dialog runModal]; - } else { - NSWindow* window = parent_window->GetNativeWindow(); - - [dialog beginSheetModalForWindow:window - completionHandler:^(NSInteger c) { - chosen = c; - [NSApp stopModal]; - }]; - [NSApp runModalForWindow:window]; - } - - return chosen; -} - -void ReadDialogPaths(NSOpenPanel* dialog, std::vector* paths) { - NSArray* urls = [dialog URLs]; - for (NSURL* url in urls) - if ([url isFileURL]) - paths->push_back(base::FilePath(base::SysNSStringToUTF8([url path]))); -} - -} // namespace - -bool ShowOpenDialog(const DialogSettings& settings, - std::vector* paths) { - DCHECK(paths); - NSOpenPanel* dialog = [NSOpenPanel openPanel]; - - SetupDialog(dialog, settings); - SetupDialogForProperties(dialog, settings.properties); - - int chosen = RunModalDialog(dialog, settings.parent_window); - if (chosen == NSFileHandlingPanelCancelButton) - return false; - - ReadDialogPaths(dialog, paths); - return true; -} - -void ShowOpenDialog(const DialogSettings& settings, - const OpenDialogCallback& c) { - NSOpenPanel* dialog = [NSOpenPanel openPanel]; - - SetupDialog(dialog, settings); - SetupDialogForProperties(dialog, settings.properties); - - // Duplicate the callback object here since c is a reference and gcd would - // only store the pointer, by duplication we can force gcd to store a copy. - __block OpenDialogCallback callback = c; - - NSWindow* window = settings.parent_window ? - settings.parent_window->GetNativeWindow() : - NULL; - [dialog beginSheetModalForWindow:window - completionHandler:^(NSInteger chosen) { - if (chosen == NSFileHandlingPanelCancelButton) { - callback.Run(false, std::vector()); - } else { - std::vector paths; - ReadDialogPaths(dialog, &paths); - callback.Run(true, paths); - } - }]; -} - -bool ShowSaveDialog(const DialogSettings& settings, - base::FilePath* path) { - DCHECK(path); - NSSavePanel* dialog = [NSSavePanel savePanel]; - - SetupDialog(dialog, settings); - - int chosen = RunModalDialog(dialog, settings.parent_window); - if (chosen == NSFileHandlingPanelCancelButton || ![[dialog URL] isFileURL]) - return false; - - *path = base::FilePath(base::SysNSStringToUTF8([[dialog URL] path])); - return true; -} - -void ShowSaveDialog(const DialogSettings& settings, - const SaveDialogCallback& c) { - NSSavePanel* dialog = [NSSavePanel savePanel]; - - SetupDialog(dialog, settings); - [dialog setCanSelectHiddenExtension:YES]; - - __block SaveDialogCallback callback = c; - - NSWindow* window = settings.parent_window ? - settings.parent_window->GetNativeWindow() : - NULL; - [dialog beginSheetModalForWindow:window - completionHandler:^(NSInteger chosen) { - if (chosen == NSFileHandlingPanelCancelButton) { - callback.Run(false, base::FilePath()); - } else { - std::string path = base::SysNSStringToUTF8([[dialog URL] path]); - callback.Run(true, base::FilePath(path)); - } - }]; -} - -} // namespace file_dialog diff --git a/atom/browser/ui/file_dialog_win.cc b/atom/browser/ui/file_dialog_win.cc deleted file mode 100644 index e9ee0c3438..0000000000 --- a/atom/browser/ui/file_dialog_win.cc +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright (c) 2013 GitHub, Inc. -// Use of this source code is governed by the MIT license that can be -// found in the LICENSE file. - -#include "atom/browser/ui/file_dialog.h" - -#include // windows.h must be included first - -#include -#include -#include - -#include "atom/browser/native_window_views.h" -#include "atom/browser/unresponsive_suppressor.h" -#include "base/files/file_util.h" -#include "base/i18n/case_conversion.h" -#include "base/strings/string_split.h" -#include "base/strings/string_util.h" -#include "base/strings/utf_string_conversions.h" -#include "base/threading/thread.h" -#include "base/threading/thread_task_runner_handle.h" -#include "base/win/registry.h" -#include "third_party/wtl/include/atlapp.h" -#include "third_party/wtl/include/atldlgs.h" - -namespace file_dialog { - -namespace { - -// Distinguish directories from regular files. -bool IsDirectory(const base::FilePath& path) { - base::File::Info file_info; - return base::GetFileInfo(path, &file_info) ? - file_info.is_directory : path.EndsWithSeparator(); -} - -void ConvertFilters(const Filters& filters, - std::vector* buffer, - std::vector* filterspec) { - if (filters.empty()) { - COMDLG_FILTERSPEC spec = { L"All Files (*.*)", L"*.*" }; - filterspec->push_back(spec); - return; - } - - buffer->reserve(filters.size() * 2); - for (size_t i = 0; i < filters.size(); ++i) { - const Filter& filter = filters[i]; - - COMDLG_FILTERSPEC spec; - buffer->push_back(base::UTF8ToWide(filter.first)); - spec.pszName = buffer->back().c_str(); - - std::vector extensions(filter.second); - for (size_t j = 0; j < extensions.size(); ++j) - extensions[j].insert(0, "*."); - buffer->push_back(base::UTF8ToWide(base::JoinString(extensions, ";"))); - spec.pszSpec = buffer->back().c_str(); - - filterspec->push_back(spec); - } -} - -// Generic class to delegate common open/save dialog's behaviours, users need to -// call interface methods via GetPtr(). -template -class FileDialog { - public: - FileDialog(const DialogSettings& settings, int options) { - std::wstring file_part; - if (!IsDirectory(settings.default_path)) - file_part = settings.default_path.BaseName().value(); - - std::vector buffer; - std::vector filterspec; - ConvertFilters(settings.filters, &buffer, &filterspec); - - dialog_.reset(new T(file_part.c_str(), options, NULL, - filterspec.data(), filterspec.size())); - - if (!settings.title.empty()) - GetPtr()->SetTitle(base::UTF8ToUTF16(settings.title).c_str()); - - if (!settings.button_label.empty()) - GetPtr()->SetOkButtonLabel( - base::UTF8ToUTF16(settings.button_label).c_str()); - - // By default, *.* will be added to the file name if file type is "*.*". In - // Electron, we disable it to make a better experience. - // - // From MSDN: https://msdn.microsoft.com/en-us/library/windows/desktop/ - // bb775970(v=vs.85).aspx - // - // If SetDefaultExtension is not called, the dialog will not update - // automatically when user choose a new file type in the file dialog. - // - // We set file extension to the first none-wildcard extension to make - // sure the dialog will update file extension automatically. - for (size_t i = 0; i < filterspec.size(); ++i) { - if (std::wstring(filterspec[i].pszSpec) != L"*.*") { - // SetFileTypeIndex is regarded as one-based index. - GetPtr()->SetFileTypeIndex(i+1); - GetPtr()->SetDefaultExtension(filterspec[i].pszSpec); - break; - } - } - - SetDefaultFolder(settings.default_path); - } - - bool Show(atom::NativeWindow* parent_window) { - atom::UnresponsiveSuppressor suppressor; - HWND window = parent_window ? static_cast( - parent_window)->GetAcceleratedWidget() : - NULL; - return dialog_->DoModal(window) == IDOK; - } - - T* GetDialog() { return dialog_.get(); } - - IFileDialog* GetPtr() const { return dialog_->GetPtr(); } - - private: - // Set up the initial directory for the dialog. - void SetDefaultFolder(const base::FilePath file_path) { - std::wstring directory = IsDirectory(file_path) ? - file_path.value() : - file_path.DirName().value(); - - ATL::CComPtr folder_item; - HRESULT hr = SHCreateItemFromParsingName(directory.c_str(), - NULL, - IID_PPV_ARGS(&folder_item)); - if (SUCCEEDED(hr)) - GetPtr()->SetFolder(folder_item); - } - - std::unique_ptr dialog_; - - DISALLOW_COPY_AND_ASSIGN(FileDialog); -}; - -struct RunState { - base::Thread* dialog_thread; - scoped_refptr ui_task_runner; -}; - -bool CreateDialogThread(RunState* run_state) { - std::unique_ptr thread( - new base::Thread(ATOM_PRODUCT_NAME "FileDialogThread")); - thread->init_com_with_mta(false); - if (!thread->Start()) - return false; - - run_state->dialog_thread = thread.release(); - run_state->ui_task_runner = base::ThreadTaskRunnerHandle::Get(); - return true; -} - -void RunOpenDialogInNewThread(const RunState& run_state, - const DialogSettings& settings, - const OpenDialogCallback& callback) { - std::vector paths; - bool result = ShowOpenDialog(settings, &paths); - run_state.ui_task_runner->PostTask(FROM_HERE, - base::Bind(callback, result, paths)); - run_state.ui_task_runner->DeleteSoon(FROM_HERE, run_state.dialog_thread); -} - -void RunSaveDialogInNewThread(const RunState& run_state, - const DialogSettings& settings, - const SaveDialogCallback& callback) { - base::FilePath path; - bool result = ShowSaveDialog(settings, &path); - run_state.ui_task_runner->PostTask(FROM_HERE, - base::Bind(callback, result, path)); - run_state.ui_task_runner->DeleteSoon(FROM_HERE, run_state.dialog_thread); -} - -} // namespace - -bool ShowOpenDialog(const DialogSettings& settings, - std::vector* paths) { - int options = FOS_FORCEFILESYSTEM | FOS_FILEMUSTEXIST; - if (settings.properties & FILE_DIALOG_OPEN_DIRECTORY) - options |= FOS_PICKFOLDERS; - if (settings.properties & FILE_DIALOG_MULTI_SELECTIONS) - options |= FOS_ALLOWMULTISELECT; - if (settings.properties & FILE_DIALOG_SHOW_HIDDEN_FILES) - options |= FOS_FORCESHOWHIDDEN; - if (settings.properties & FILE_DIALOG_PROMPT_TO_CREATE) - options |= FOS_CREATEPROMPT; - - FileDialog open_dialog(settings, options); - if (!open_dialog.Show(settings.parent_window)) - return false; - - ATL::CComPtr items; - HRESULT hr = static_cast(open_dialog.GetPtr())->GetResults( - &items); - if (FAILED(hr)) - return false; - - ATL::CComPtr item; - DWORD count = 0; - hr = items->GetCount(&count); - if (FAILED(hr)) - return false; - - paths->reserve(count); - for (DWORD i = 0; i < count; ++i) { - hr = items->GetItemAt(i, &item); - if (FAILED(hr)) - return false; - - wchar_t file_name[MAX_PATH]; - hr = CShellFileOpenDialog::GetFileNameFromShellItem( - item, SIGDN_FILESYSPATH, file_name, MAX_PATH); - if (FAILED(hr)) - return false; - - paths->push_back(base::FilePath(file_name)); - } - - return true; -} - -void ShowOpenDialog(const DialogSettings& settings, - const OpenDialogCallback& callback) { - RunState run_state; - if (!CreateDialogThread(&run_state)) { - callback.Run(false, std::vector()); - return; - } - - run_state.dialog_thread->task_runner()->PostTask( - FROM_HERE, - base::Bind(&RunOpenDialogInNewThread, run_state, settings, callback)); -} - -bool ShowSaveDialog(const DialogSettings& settings, - base::FilePath* path) { - FileDialog save_dialog( - settings, FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST | FOS_OVERWRITEPROMPT); - if (!save_dialog.Show(settings.parent_window)) - return false; - - wchar_t buffer[MAX_PATH]; - HRESULT hr = save_dialog.GetDialog()->GetFilePath(buffer, MAX_PATH); - if (FAILED(hr)) - return false; - - *path = base::FilePath(buffer); - return true; -} - -void ShowSaveDialog(const DialogSettings& settings, - const SaveDialogCallback& callback) { - RunState run_state; - if (!CreateDialogThread(&run_state)) { - callback.Run(false, base::FilePath()); - return; - } - - run_state.dialog_thread->task_runner()->PostTask( - FROM_HERE, - base::Bind(&RunSaveDialogInNewThread, run_state, settings, callback)); -} - -} // namespace file_dialog diff --git a/docs/api/dialog.md b/docs/api/dialog.md index e49dbf75d0..f7a010c015 100644 --- a/docs/api/dialog.md +++ b/docs/api/dialog.md @@ -5,84 +5,66 @@ An example of showing a dialog to select multiple files and directories: ```javascript -const {dialog} = require('electron') -console.log(dialog.showOpenDialog({properties: ['openFile', 'openDirectory', 'multiSelections']})) -``` - -The Dialog is opened from Electron's main thread. If you want to use the dialog -object from a renderer process, remember to access it using the remote: - -```javascript -const {dialog} = require('electron').remote -console.log(dialog) +const {dialog, BrowserWindow} = require('electron') +dialog.showDialog(BrowserWindow.getFocusedWindow(), + {type: 'select-saveas-file'}, + (files) => { + console.log(files) + }) ``` ## Methods The `dialog` module has the following methods: -### `dialog.showOpenDialog([browserWindow, ]options[, callback])` +### `dialog.showDialog(browserWindow, options, callback)` -* `browserWindow` BrowserWindow (optional) +* `browserWindow` BrowserWindow * `options` Object - * `title` String * `defaultPath` String - * `buttonLabel` String - Custom label for the confirmation button, when - left empty the default label will be used. - * `filters` Array - * `properties` Array - Contains which features the dialog should use, can - contain `openFile`, `openDirectory`, `multiSelections`, `createDirectory` - and `showHiddenFiles`. -* `callback` Function (optional) + * `extensions` Array - Array of grouped array of extensions + * `extensionDescriptionOverrides` Array - Overrides the system descriptions + of the specified extensions. Entries correspond to `extensions`; + if left blank the system descriptions will be used. + * `includeAllFiles` Boollean - Show all files in extensions + * `type` String - Indicates which features the dialog should use, can + be `select-folder`, `select-upload-folder`, `select-saveas-file`, + `select-open-file` and `select-open-multi-file`. +* `callback` Function On success this method returns an array of file paths chosen by the user, otherwise it returns `undefined`. -The `filters` specifies an array of file types that can be displayed or -selected when you want to limit the user to a specific type. For example: +The `extensions` specifies an array of file types that can be displayed or +selected when you want to limit the user to a specific type. +The first element of `extensions` array will be default extension. +For example: ```javascript { - filters: [ - {name: 'Images', extensions: ['jpg', 'png', 'gif']}, - {name: 'Movies', extensions: ['mkv', 'avi', 'mp4']}, - {name: 'Custom File Type', extensions: ['as']}, - {name: 'All Files', extensions: ['*']} - ] + extensions: [['jpg', 'png', 'gif'], ['html', htm'], ['txt']] } ``` The `extensions` array should contain extensions without wildcards or dots (e.g. -`'png'` is good but `'.png'` and `'*.png'` are bad). To show all files, use the -`'*'` wildcard (no other wildcard is supported). +`'png'` is good but `'.png'` and `'*.png'` are bad). + +You can override system descriptions of extensions +```javascript +{ + extensions: [['jpg', 'png', 'gif'], ['html', htm'], ['txt'], ['brave']], + extensionDescriptionOverrides: ['IMAGE', '', 'text file', 'Amazing Ext'] +} +``` If a `callback` is passed, the API call will be asynchronous and the result will be passed via `callback(filenames)` -**Note:** On Windows and Linux an open dialog can not be both a file selector -and a directory selector, so if you set `properties` to -`['openFile', 'openDirectory']` on these platforms, a directory selector will be -shown. - -### `dialog.showSaveDialog([browserWindow, ]options[, callback])` - -* `browserWindow` BrowserWindow (optional) -* `options` Object - * `title` String - * `defaultPath` String - * `buttonLabel` String - Custom label for the confirmation button, when - left empty the default label will be used. - * `filters` Array -* `callback` Function (optional) - -On success this method returns the path of the file chosen by the user, -otherwise it returns `undefined`. +For `select-saveas-file`, the filename will be the first element of the +filename array(filename[0]) -The `filters` specifies an array of file types that can be displayed, see -`dialog.showOpenDialog` for an example. - -If a `callback` is passed, the API call will be asynchronous and the result -will be passed via `callback(filename)` +If you don't specify `defaultPath`, it will be your download path (set by +`download.default_directory` user prefs). ### `dialog.showMessageBox([browserWindow, ]options[, callback])` diff --git a/lib/browser/api/dialog.js b/lib/browser/api/dialog.js index 1f72b1bf78..4bbf67cd20 100644 --- a/lib/browser/api/dialog.js +++ b/lib/browser/api/dialog.js @@ -48,89 +48,35 @@ var checkAppInitialized = function () { } module.exports = { - showOpenDialog: function (...args) { - var prop, properties, value, wrappedCallback - checkAppInitialized() - let [window, options, callback] = parseArgs.apply(null, args) - if (options == null) { - options = { - title: 'Open', - properties: ['openFile'] - } - } - if (options.properties == null) { - options.properties = ['openFile'] - } - if (!Array.isArray(options.properties)) { - throw new TypeError('Properties must be an array') - } - properties = 0 - for (prop in fileDialogProperties) { - value = fileDialogProperties[prop] - if (includes.call(options.properties, prop)) { - properties |= value - } - } - if (options.title == null) { - options.title = '' - } else if (typeof options.title !== 'string') { - throw new TypeError('Title must be a string') - } - if (options.buttonLabel == null) { - options.buttonLabel = '' - } else if (typeof options.buttonLabel !== 'string') { - throw new TypeError('buttonLabel must be a string') - } - if (options.defaultPath == null) { - options.defaultPath = '' - } else if (typeof options.defaultPath !== 'string') { - throw new TypeError('Default path must be a string') - } - if (options.filters == null) { - options.filters = [] - } - wrappedCallback = typeof callback === 'function' ? function (success, result) { - return callback(success ? result : void 0) - } : null - let settings = options - settings.properties = properties - settings.window = window - return binding.showOpenDialog(settings, wrappedCallback) - }, - - showSaveDialog: function (...args) { + showDialog: function (...args) { var wrappedCallback checkAppInitialized() let [window, options, callback] = parseArgs.apply(null, args) + if (window == null) { + throw new TypeError('window can not be null') + } if (options == null) { - options = { - title: 'Save' - } + throw new TypeError('options can not be null') } - if (options.title == null) { - options.title = '' - } else if (typeof options.title !== 'string') { - throw new TypeError('Title must be a string') + if (callback == null) { + throw new TypeError('callback can not be null') } - if (options.buttonLabel == null) { - options.buttonLabel = '' - } else if (typeof options.buttonLabel !== 'string') { - throw new TypeError('buttonLabel must be a string') + if (options.type == null) { + options.type = '' + } else if (typeof options.type !== 'string') { + throw new TypeError('type must be a string') } if (options.defaultPath == null) { options.defaultPath = '' } else if (typeof options.defaultPath !== 'string') { throw new TypeError('Default path must be a string') } - if (options.filters == null) { - options.filters = [] - } wrappedCallback = typeof callback === 'function' ? function (success, result) { return callback(success ? result : void 0) } : null let settings = options settings.window = window - return binding.showSaveDialog(settings, wrappedCallback) + return binding.showDialog(settings, wrappedCallback) }, showMessageBox: function (...args) { @@ -196,7 +142,7 @@ module.exports = { } // Mark standard asynchronous functions. -var ref1 = ['showMessageBox', 'showOpenDialog', 'showSaveDialog'] +var ref1 = ['showMessageBox', 'showDialog'] var j, len, api for (j = 0, len = ref1.length; j < len; j++) { api = ref1[j] diff --git a/vendor/brightray/browser/inspectable_web_contents_impl.cc b/vendor/brightray/browser/inspectable_web_contents_impl.cc index 8281636ce6..c89c260063 100644 --- a/vendor/brightray/browser/inspectable_web_contents_impl.cc +++ b/vendor/brightray/browser/inspectable_web_contents_impl.cc @@ -690,10 +690,9 @@ void InspectableWebContentsImpl::RequestFileSystems() { } void InspectableWebContentsImpl::AddFileSystem( - const std::string& file_system_path) { + const std::string& type) { if (delegate_) - delegate_->DevToolsAddFileSystem( - base::FilePath::FromUTF8Unsafe(file_system_path)); + delegate_->DevToolsAddFileSystem(base::FilePath()); } void InspectableWebContentsImpl::RemoveFileSystem(