mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2025-06-05 21:39:12 +02:00
- Add open folder dialog support (FILE_DIALOG_OPEN_FOLDER mode) for CefBrowserHost::RunFileDialog (issue #1030).
- Standardize file dialog behavior across all platforms (issue #1492). -- Show a file type filter list on OS X. -- Show the file extensions as part of the filter list description on all platforms (e.g. "Image Types (*.png;*.gif;*.jpg)"). -- Rename the CefBrowserHost::RunFileDialog |accept_types| argument to |accept_filters| and expand support for filters that will be displayed as-is in addition to the currently supported mime-type and extension-based filters. For example, a filter value of "Supported Image Types|.png;.gif;.jpg" will display "Supported Image Types (*.png;*.gif;*.jpg)" in the filter drop-down list and accept *.png, *.gif and *.jpg files. -- Persist the selected filter item index by passing a new |selected_accept_filter| argument to CefBrowserHost::RunFileDialog and returning the newly selected index via the CefRunFileDialogCallback and CefFileDialogCallback callbacks. -- Rename the CefBrowserHost::RunFileDialog |default_file_name| argument to |default_file_path| and use the directory component, if any, to set the default directory location. If |default_file_path| ends in a trailing path separator it will be treated as a directory without a file name component. -- Add FILE_DIALOG_OVERWRITEPROMPT_FLAG and FILE_DIALOG_HIDEREADONLY_FLAG values to cef_file_dialog_mode_t for controlling those behaviors where possible. git-svn-id: https://chromiumembedded.googlecode.com/svn/trunk@1973 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
#include "base/files/file_util.h"
|
||||
#include "base/mac/mac_util.h"
|
||||
#include "base/mac/scoped_nsautorelease_pool.h"
|
||||
#include "base/strings/string_split.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/strings/sys_string_conversions.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
@@ -23,6 +24,7 @@
|
||||
#include "content/public/browser/render_widget_host_view.h"
|
||||
#include "content/public/browser/web_contents.h"
|
||||
#include "content/public/common/file_chooser_params.h"
|
||||
#include "grit/cef_strings.h"
|
||||
#include "grit/ui_strings.h"
|
||||
#include "net/base/mime_util.h"
|
||||
#include "third_party/WebKit/public/web/WebInputEvent.h"
|
||||
@@ -170,33 +172,216 @@
|
||||
|
||||
@end
|
||||
|
||||
namespace {
|
||||
|
||||
base::string16 GetDescriptionFromMimeType(const std::string& mime_type) {
|
||||
// Check for wild card mime types and return an appropriate description.
|
||||
static const struct {
|
||||
const char* mime_type;
|
||||
int string_id;
|
||||
} kWildCardMimeTypes[] = {
|
||||
{ "audio", IDS_APP_AUDIO_FILES },
|
||||
{ "image", IDS_APP_IMAGE_FILES },
|
||||
{ "text", IDS_APP_TEXT_FILES },
|
||||
{ "video", IDS_APP_VIDEO_FILES },
|
||||
};
|
||||
|
||||
for (size_t i = 0; i < arraysize(kWildCardMimeTypes); ++i) {
|
||||
if (mime_type == std::string(kWildCardMimeTypes[i].mime_type) + "/*")
|
||||
return l10n_util::GetStringUTF16(kWildCardMimeTypes[i].string_id);
|
||||
}
|
||||
|
||||
return base::string16();
|
||||
}
|
||||
|
||||
void AddFilters(NSPopUpButton *button,
|
||||
const std::vector<base::string16>& accept_filters,
|
||||
bool include_all_files,
|
||||
std::vector<std::vector<base::string16> >* all_extensions) {
|
||||
for (size_t i = 0; i < accept_filters.size(); ++i) {
|
||||
const base::string16& filter = accept_filters[i];
|
||||
if (filter.empty())
|
||||
continue;
|
||||
|
||||
std::vector<base::string16> extensions;
|
||||
base::string16 description;
|
||||
|
||||
size_t sep_index = filter.find('|');
|
||||
if (sep_index != std::string::npos) {
|
||||
// Treat as a filter of the form "Filter Name|.ext1;.ext2;.ext3".
|
||||
description = filter.substr(0, sep_index);
|
||||
|
||||
std::vector<base::string16> ext;
|
||||
base::SplitString(filter.substr(sep_index + 1), ';', &ext);
|
||||
for (size_t x = 0; x < ext.size(); ++x) {
|
||||
const base::string16& file_ext = ext[x];
|
||||
if (!file_ext.empty() && file_ext[0] == '.')
|
||||
extensions.push_back(file_ext);
|
||||
}
|
||||
} else if (filter[0] == '.') {
|
||||
// Treat as an extension beginning with the '.' character.
|
||||
extensions.push_back(filter);
|
||||
} else {
|
||||
// Otherwise convert mime type to one or more extensions.
|
||||
const std::string& ascii = base::UTF16ToASCII(filter);
|
||||
std::vector<base::FilePath::StringType> ext;
|
||||
net::GetExtensionsForMimeType(ascii, &ext);
|
||||
if (!ext.empty()) {
|
||||
for (size_t x = 0; x < ext.size(); ++x)
|
||||
extensions.push_back(base::ASCIIToUTF16("." + ext[x]));
|
||||
description = GetDescriptionFromMimeType(ascii);
|
||||
}
|
||||
}
|
||||
|
||||
if (extensions.empty())
|
||||
continue;
|
||||
|
||||
// Don't display a crazy number of extensions since the NSPopUpButton width
|
||||
// will keep growing.
|
||||
const size_t kMaxExtensions = 10;
|
||||
|
||||
base::string16 ext_str;
|
||||
for (size_t x = 0; x < std::min(kMaxExtensions, extensions.size()); ++x) {
|
||||
const base::string16& pattern = base::ASCIIToUTF16("*") + extensions[x];
|
||||
if (x != 0)
|
||||
ext_str += base::ASCIIToUTF16(";");
|
||||
ext_str += pattern;
|
||||
}
|
||||
|
||||
if (extensions.size() > kMaxExtensions)
|
||||
ext_str += base::ASCIIToUTF16(";...");
|
||||
|
||||
if (description.empty()) {
|
||||
description = ext_str;
|
||||
} else {
|
||||
description +=
|
||||
base::ASCIIToUTF16(" (") + ext_str + base::ASCIIToUTF16(")");
|
||||
}
|
||||
|
||||
[button addItemWithTitle:base::SysUTF16ToNSString(description)];
|
||||
|
||||
all_extensions->push_back(extensions);
|
||||
}
|
||||
|
||||
// Add the *.* filter, but only if we have added other filters (otherwise it
|
||||
// is implied).
|
||||
if (include_all_files && !all_extensions->empty()) {
|
||||
[button addItemWithTitle:base::SysUTF8ToNSString("All Files (*)")];
|
||||
all_extensions->push_back(std::vector<base::string16>());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Used to manage the file type filter in the NSSavePanel/NSOpenPanel.
|
||||
@interface CefFilterDelegate : NSObject {
|
||||
@private
|
||||
NSSavePanel* panel_;
|
||||
std::vector<std::vector<base::string16> > extensions_;
|
||||
int selected_index_;
|
||||
}
|
||||
- (id)initWithPanel:(NSSavePanel*)panel
|
||||
andAcceptFilters:(const std::vector<base::string16>&)accept_filters
|
||||
andFilterIndex:(int)index;
|
||||
- (void)setFilter:(int)index;
|
||||
- (int)filter;
|
||||
- (void)filterSelectionChanged:(id)sender;
|
||||
- (void)setFileExtension;
|
||||
@end
|
||||
|
||||
@implementation CefFilterDelegate
|
||||
|
||||
- (id)initWithPanel:(NSSavePanel*)panel
|
||||
andAcceptFilters:(const std::vector<base::string16>&)accept_filters
|
||||
andFilterIndex:(int)index {
|
||||
if (self = [super init]) {
|
||||
DCHECK(panel);
|
||||
panel_ = panel;
|
||||
selected_index_ = 0;
|
||||
|
||||
NSPopUpButton *button = [[NSPopUpButton alloc] init];
|
||||
AddFilters(button, accept_filters, true, &extensions_);
|
||||
[button sizeToFit];
|
||||
[button setTarget:self];
|
||||
[button setAction:@selector(filterSelectionChanged:)];
|
||||
|
||||
if (index < static_cast<int>(extensions_.size())) {
|
||||
[button selectItemAtIndex:index];
|
||||
[self setFilter:index];
|
||||
}
|
||||
|
||||
[panel_ setAccessoryView:button];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
// Set the current filter index.
|
||||
- (void)setFilter:(int)index {
|
||||
DCHECK(index >= 0 && index < static_cast<int>(extensions_.size()));
|
||||
selected_index_ = index;
|
||||
|
||||
// Set the selectable file types. For open panels this limits the files that
|
||||
// can be selected. For save panels this applies a default file extenion when
|
||||
// the dialog is dismissed if none is already provided.
|
||||
NSMutableArray* acceptArray = nil;
|
||||
if (!extensions_[index].empty()) {
|
||||
acceptArray = [[NSMutableArray alloc] init];
|
||||
for (size_t i = 0; i < extensions_[index].size(); ++i) {
|
||||
[acceptArray addObject:
|
||||
base::SysUTF16ToNSString(extensions_[index][i].substr(1))];
|
||||
}
|
||||
}
|
||||
[panel_ setAllowedFileTypes:acceptArray];
|
||||
|
||||
if (![panel_ isKindOfClass:[NSOpenPanel class]]) {
|
||||
// For save panels set the file extension.
|
||||
[self setFileExtension];
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the current filter index.
|
||||
- (int)filter {
|
||||
return selected_index_;
|
||||
}
|
||||
|
||||
// Called when the selected filter is changed via the NSPopUpButton.
|
||||
- (void)filterSelectionChanged:(id)sender {
|
||||
NSPopUpButton *button = (NSPopUpButton*)sender;
|
||||
[self setFilter:[button indexOfSelectedItem]];
|
||||
}
|
||||
|
||||
// Set the extension on the currently selected file name.
|
||||
- (void)setFileExtension {
|
||||
const std::vector<base::string16>& filter = extensions_[selected_index_];
|
||||
if (filter.empty()) {
|
||||
// All extensions are allowed so don't change anything.
|
||||
return;
|
||||
}
|
||||
|
||||
base::FilePath path(base::SysNSStringToUTF8([panel_ nameFieldStringValue]));
|
||||
|
||||
// If the file name currently includes an extension from |filter| then don't
|
||||
// change anything.
|
||||
base::string16 extension = base::UTF8ToUTF16(path.Extension());
|
||||
if (!extension.empty()) {
|
||||
for (size_t i = 0; i < filter.size(); ++i) {
|
||||
if (filter[i] == extension)
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Change the extension to the first value in |filter|.
|
||||
path = path.ReplaceExtension(base::UTF16ToUTF8(filter[0]));
|
||||
[panel_ setNameFieldStringValue:base::SysUTF8ToNSString(path.value())];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
namespace {
|
||||
|
||||
// Accept-types to file-types helper.
|
||||
NSMutableArray* GetFileTypesFromAcceptTypes(
|
||||
const std::vector<base::string16>& accept_types) {
|
||||
NSMutableArray* acceptArray = [[NSMutableArray alloc] init];
|
||||
for (size_t i=0; i<accept_types.size(); i++) {
|
||||
std::string ascii_type = base::UTF16ToASCII(accept_types[i]);
|
||||
if (ascii_type.length()) {
|
||||
// Just treat as extension if contains '.' as the first character.
|
||||
if (ascii_type[0] == '.') {
|
||||
[acceptArray addObject:base::SysUTF8ToNSString(ascii_type.substr(1))];
|
||||
} else {
|
||||
// Otherwise convert mime type to one or more extensions.
|
||||
std::vector<base::FilePath::StringType> ext;
|
||||
net::GetExtensionsForMimeType(ascii_type, &ext);
|
||||
for (size_t x = 0; x < ext.size(); ++x)
|
||||
[acceptArray addObject:base::SysUTF8ToNSString(ext[x])];
|
||||
}
|
||||
}
|
||||
}
|
||||
return acceptArray;
|
||||
}
|
||||
|
||||
void RunOpenFileDialog(const content::FileChooserParams& params,
|
||||
void RunOpenFileDialog(const CefBrowserHostImpl::FileChooserParams& params,
|
||||
NSView* view,
|
||||
int* filter_index,
|
||||
std::vector<base::FilePath>* files) {
|
||||
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
|
||||
|
||||
@@ -206,39 +391,51 @@ void RunOpenFileDialog(const content::FileChooserParams& params,
|
||||
} else {
|
||||
title = l10n_util::GetStringUTF16(
|
||||
params.mode == content::FileChooserParams::Open ?
|
||||
IDS_OPEN_FILE_DIALOG_TITLE : IDS_OPEN_FILES_DIALOG_TITLE);
|
||||
IDS_OPEN_FILE_DIALOG_TITLE :
|
||||
(params.mode == content::FileChooserParams::OpenMultiple ?
|
||||
IDS_OPEN_FILES_DIALOG_TITLE : IDS_SELECT_FOLDER_DIALOG_TITLE));
|
||||
}
|
||||
[openPanel setTitle:base::SysUTF16ToNSString(title)];
|
||||
|
||||
// Consider default file name if any.
|
||||
base::FilePath default_file_name(params.default_file_name);
|
||||
|
||||
if (!default_file_name.empty()) {
|
||||
if (!default_file_name.BaseName().empty()) {
|
||||
NSString* defaultName = base::SysUTF8ToNSString(
|
||||
default_file_name.BaseName().value());
|
||||
[openPanel setNameFieldStringValue:defaultName];
|
||||
}
|
||||
|
||||
if (!default_file_name.DirName().empty()) {
|
||||
NSString* defaultDir = base::SysUTF8ToNSString(
|
||||
default_file_name.DirName().value());
|
||||
[openPanel setDirectoryURL:[NSURL fileURLWithPath:defaultDir]];
|
||||
std::string filename, directory;
|
||||
if (!params.default_file_name.empty()) {
|
||||
if (params.mode == content::FileChooserParams::UploadFolder ||
|
||||
params.default_file_name.EndsWithSeparator()) {
|
||||
// The value is only a directory.
|
||||
directory = params.default_file_name.value();
|
||||
} else {
|
||||
// The value is a file name and possibly a directory.
|
||||
filename = params.default_file_name.BaseName().value();
|
||||
directory = params.default_file_name.DirName().value();
|
||||
}
|
||||
}
|
||||
if (!filename.empty()) {
|
||||
[openPanel setNameFieldStringValue:base::SysUTF8ToNSString(filename)];
|
||||
}
|
||||
if (!directory.empty()) {
|
||||
[openPanel setDirectoryURL:
|
||||
[NSURL fileURLWithPath:base::SysUTF8ToNSString(directory)]];
|
||||
}
|
||||
|
||||
// Consider supported file types
|
||||
if (!params.accept_types.empty()) {
|
||||
[openPanel setAllowedFileTypes:GetFileTypesFromAcceptTypes(
|
||||
params.accept_types)];
|
||||
CefFilterDelegate* filter_delegate = nil;
|
||||
if (params.mode != content::FileChooserParams::UploadFolder &&
|
||||
!params.accept_types.empty()) {
|
||||
// Add the file filter control.
|
||||
filter_delegate =
|
||||
[[CefFilterDelegate alloc] initWithPanel:openPanel
|
||||
andAcceptFilters:params.accept_types
|
||||
andFilterIndex:*filter_index];
|
||||
}
|
||||
|
||||
// Further panel configuration.
|
||||
[openPanel setAllowsOtherFileTypes:YES];
|
||||
[openPanel setAllowsMultipleSelection:
|
||||
(params.mode == content::FileChooserParams::OpenMultiple)];
|
||||
[openPanel setCanChooseFiles:YES];
|
||||
[openPanel setCanChooseDirectories:NO];
|
||||
[openPanel setCanChooseFiles:
|
||||
(params.mode != content::FileChooserParams::UploadFolder)];
|
||||
[openPanel setCanChooseDirectories:
|
||||
(params.mode == content::FileChooserParams::UploadFolder)];
|
||||
[openPanel setShowsHiddenFiles:!params.hidereadonly];
|
||||
|
||||
// Show panel.
|
||||
[openPanel beginSheetModalForWindow:[view window] completionHandler:nil];
|
||||
@@ -251,11 +448,16 @@ void RunOpenFileDialog(const content::FileChooserParams& params,
|
||||
files->push_back(base::FilePath(base::SysNSStringToUTF8([url path])));
|
||||
}
|
||||
}
|
||||
|
||||
if (filter_delegate != nil)
|
||||
*filter_index = [filter_delegate filter];
|
||||
|
||||
[NSApp endSheet:openPanel];
|
||||
}
|
||||
|
||||
bool RunSaveFileDialog(const content::FileChooserParams& params,
|
||||
bool RunSaveFileDialog(const CefBrowserHostImpl::FileChooserParams& params,
|
||||
NSView* view,
|
||||
int* filter_index,
|
||||
base::FilePath* file) {
|
||||
NSSavePanel* savePanel = [NSSavePanel savePanel];
|
||||
|
||||
@@ -266,30 +468,36 @@ bool RunSaveFileDialog(const content::FileChooserParams& params,
|
||||
title = l10n_util::GetStringUTF16(IDS_SAVE_AS_DIALOG_TITLE);
|
||||
[savePanel setTitle:base::SysUTF16ToNSString(title)];
|
||||
|
||||
// Consider default file name if any.
|
||||
base::FilePath default_file_name(params.default_file_name);
|
||||
|
||||
if (!default_file_name.empty()) {
|
||||
if (!default_file_name.BaseName().empty()) {
|
||||
NSString* defaultName = base::SysUTF8ToNSString(
|
||||
default_file_name.BaseName().value());
|
||||
[savePanel setNameFieldStringValue:defaultName];
|
||||
}
|
||||
|
||||
if (!default_file_name.DirName().empty()) {
|
||||
NSString* defaultDir = base::SysUTF8ToNSString(
|
||||
default_file_name.DirName().value());
|
||||
[savePanel setDirectoryURL:[NSURL fileURLWithPath:defaultDir]];
|
||||
std::string filename, directory;
|
||||
if (!params.default_file_name.empty()) {
|
||||
if (params.default_file_name.EndsWithSeparator()) {
|
||||
// The value is only a directory.
|
||||
directory = params.default_file_name.value();
|
||||
} else {
|
||||
// The value is a file name and possibly a directory.
|
||||
filename = params.default_file_name.BaseName().value();
|
||||
directory = params.default_file_name.DirName().value();
|
||||
}
|
||||
}
|
||||
if (!filename.empty()) {
|
||||
[savePanel setNameFieldStringValue:base::SysUTF8ToNSString(filename)];
|
||||
}
|
||||
if (!directory.empty()) {
|
||||
[savePanel setDirectoryURL:
|
||||
[NSURL fileURLWithPath:base::SysUTF8ToNSString(directory)]];
|
||||
}
|
||||
|
||||
// Consider supported file types
|
||||
CefFilterDelegate* filter_delegate = nil;
|
||||
if (!params.accept_types.empty()) {
|
||||
[savePanel setAllowedFileTypes:GetFileTypesFromAcceptTypes(
|
||||
params.accept_types)];
|
||||
// Add the file filter control.
|
||||
filter_delegate =
|
||||
[[CefFilterDelegate alloc] initWithPanel:savePanel
|
||||
andAcceptFilters:params.accept_types
|
||||
andFilterIndex:*filter_index];
|
||||
}
|
||||
|
||||
[savePanel setAllowsOtherFileTypes:YES];
|
||||
[savePanel setShowsHiddenFiles:!params.hidereadonly];
|
||||
|
||||
bool success = false;
|
||||
|
||||
@@ -300,6 +508,10 @@ bool RunSaveFileDialog(const content::FileChooserParams& params,
|
||||
*file = base::FilePath([path UTF8String]);
|
||||
success = true;
|
||||
}
|
||||
|
||||
if (filter_delegate != nil)
|
||||
*filter_index = [filter_delegate filter];
|
||||
|
||||
[NSApp endSheet:savePanel];
|
||||
|
||||
return success;
|
||||
@@ -491,22 +703,26 @@ void CefBrowserHostImpl::PlatformHandleKeyboardEvent(
|
||||
}
|
||||
|
||||
void CefBrowserHostImpl::PlatformRunFileChooser(
|
||||
const content::FileChooserParams& params,
|
||||
const FileChooserParams& params,
|
||||
RunFileChooserCallback callback) {
|
||||
std::vector<base::FilePath> files;
|
||||
int filter_index = params.selected_accept_filter;
|
||||
NSView* owner = PlatformGetWindowHandle();
|
||||
|
||||
if (params.mode == content::FileChooserParams::Open ||
|
||||
params.mode == content::FileChooserParams::OpenMultiple) {
|
||||
RunOpenFileDialog(params, PlatformGetWindowHandle(), &files);
|
||||
params.mode == content::FileChooserParams::OpenMultiple ||
|
||||
params.mode == content::FileChooserParams::UploadFolder) {
|
||||
RunOpenFileDialog(params, owner, &filter_index, &files);
|
||||
} else if (params.mode == content::FileChooserParams::Save) {
|
||||
base::FilePath file;
|
||||
if (RunSaveFileDialog(params, PlatformGetWindowHandle(), &file))
|
||||
if (RunSaveFileDialog(params, owner, &filter_index, &file)) {
|
||||
files.push_back(file);
|
||||
}
|
||||
} else {
|
||||
NOTIMPLEMENTED();
|
||||
}
|
||||
|
||||
callback.Run(files);
|
||||
callback.Run(filter_index, files);
|
||||
}
|
||||
|
||||
void CefBrowserHostImpl::PlatformHandleExternalProtocol(const GURL& url) {
|
||||
|
Reference in New Issue
Block a user