mirror of
https://bitbucket.org/chromiumembedded/cef
synced 2024-12-13 10:06:28 +01:00
019611c764
Under ARC (Automatic Reference Counting), assigning to an Objective-C pointer has different semantics than assigning to a void* pointer. This makes it dangerous to treat the same memory address as an Objective-C pointer in some cases and as a "regular C pointer" in other cases. This change removes the conditional type defines and instead uses void* everywhere. Explicit type casting in combination with ARC annotations makes it safe to get typed Objective-C pointers from the void* pointers. This change enables ARC by default in the CEF binary distribution CMake configuration for the cefclient and cefsimple sample applications. It can be disabled by adding `-DOPTION_USE_ARC=Off` to the CMake command line. ARC is not supported when building Chromium due to the substantial number of changes that would be required in the Chromium code base.
402 lines
14 KiB
Plaintext
402 lines
14 KiB
Plaintext
// Copyright (c) 2012 The Chromium Embedded Framework Authors.
|
|
// Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "libcef/browser/native/file_dialog_runner_mac.h"
|
|
|
|
#import <Cocoa/Cocoa.h>
|
|
#import <CoreServices/CoreServices.h>
|
|
|
|
#include "libcef/browser/browser_host_impl.h"
|
|
|
|
#include "base/mac/mac_util.h"
|
|
#include "base/stl_util.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"
|
|
#include "base/threading/thread_restrictions.h"
|
|
#include "cef/grit/cef_strings.h"
|
|
#include "chrome/grit/generated_resources.h"
|
|
#include "net/base/mime_util.h"
|
|
#include "ui/base/l10n/l10n_util.h"
|
|
#include "ui/strings/grit/ui_strings.h"
|
|
|
|
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_AUDIO_FILES},
|
|
{"image", IDS_IMAGE_FILES},
|
|
{"text", IDS_TEXT_FILES},
|
|
{"video", IDS_VIDEO_FILES},
|
|
};
|
|
|
|
for (size_t i = 0; i < base::size(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);
|
|
|
|
const std::vector<base::string16>& ext = base::SplitString(
|
|
filter.substr(sep_index + 1), base::ASCIIToUTF16(";"),
|
|
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
|
|
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 {
|
|
|
|
void RunOpenFileDialog(const CefFileDialogRunner::FileChooserParams& params,
|
|
NSView* view,
|
|
int filter_index,
|
|
CefFileDialogRunner::RunFileChooserCallback callback) {
|
|
NSOpenPanel* openPanel = [NSOpenPanel openPanel];
|
|
|
|
base::string16 title;
|
|
if (!params.title.empty()) {
|
|
title = params.title;
|
|
} else {
|
|
title = l10n_util::GetStringUTF16(
|
|
params.mode == blink::mojom::FileChooserParams::Mode::kOpen
|
|
? IDS_OPEN_FILE_DIALOG_TITLE
|
|
: (params.mode ==
|
|
blink::mojom::FileChooserParams::Mode::kOpenMultiple
|
|
? IDS_OPEN_FILES_DIALOG_TITLE
|
|
: IDS_SELECT_FOLDER_DIALOG_TITLE));
|
|
}
|
|
[openPanel setTitle:base::SysUTF16ToNSString(title)];
|
|
|
|
std::string filename, directory;
|
|
if (!params.default_file_name.empty()) {
|
|
if (params.mode == blink::mojom::FileChooserParams::Mode::kUploadFolder ||
|
|
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)]];
|
|
}
|
|
|
|
CefFilterDelegate* filter_delegate = nil;
|
|
if (params.mode != blink::mojom::FileChooserParams::Mode::kUploadFolder &&
|
|
!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 ==
|
|
blink::mojom::FileChooserParams::Mode::kOpenMultiple)];
|
|
[openPanel
|
|
setCanChooseFiles:(params.mode !=
|
|
blink::mojom::FileChooserParams::Mode::kUploadFolder)];
|
|
[openPanel
|
|
setCanChooseDirectories:(params.mode == blink::mojom::FileChooserParams::
|
|
Mode::kUploadFolder)];
|
|
[openPanel setShowsHiddenFiles:!params.hidereadonly];
|
|
|
|
// Show panel.
|
|
[openPanel
|
|
beginSheetModalForWindow:[view window]
|
|
completionHandler:^(NSInteger returnCode) {
|
|
int filter_index_to_use = (filter_delegate != nil)
|
|
? [filter_delegate filter]
|
|
: filter_index;
|
|
if (returnCode == NSFileHandlingPanelOKButton) {
|
|
std::vector<base::FilePath> files;
|
|
files.reserve(openPanel.URLs.count);
|
|
for (NSURL* url in openPanel.URLs) {
|
|
if (url.isFileURL)
|
|
files.push_back(base::FilePath(url.path.UTF8String));
|
|
}
|
|
callback.Run(filter_index_to_use, files);
|
|
} else {
|
|
callback.Run(filter_index_to_use,
|
|
std::vector<base::FilePath>());
|
|
}
|
|
}];
|
|
}
|
|
|
|
void RunSaveFileDialog(const CefFileDialogRunner::FileChooserParams& params,
|
|
NSView* view,
|
|
int filter_index,
|
|
CefFileDialogRunner::RunFileChooserCallback callback) {
|
|
NSSavePanel* savePanel = [NSSavePanel savePanel];
|
|
|
|
base::string16 title;
|
|
if (!params.title.empty())
|
|
title = params.title;
|
|
else
|
|
title = l10n_util::GetStringUTF16(IDS_SAVE_AS_DIALOG_TITLE);
|
|
[savePanel setTitle:base::SysUTF16ToNSString(title)];
|
|
|
|
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)]];
|
|
}
|
|
|
|
CefFilterDelegate* filter_delegate = nil;
|
|
if (!params.accept_types.empty()) {
|
|
// 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];
|
|
|
|
// Show panel.
|
|
[savePanel
|
|
beginSheetModalForWindow:view.window
|
|
completionHandler:^(NSInteger resultCode) {
|
|
int filter_index_to_use = (filter_delegate != nil)
|
|
? [filter_delegate filter]
|
|
: filter_index;
|
|
if (resultCode == NSFileHandlingPanelOKButton) {
|
|
NSURL* url = savePanel.URL;
|
|
const char* path = url.path.UTF8String;
|
|
std::vector<base::FilePath> files(1, base::FilePath(path));
|
|
callback.Run(filter_index_to_use, files);
|
|
} else {
|
|
callback.Run(filter_index_to_use,
|
|
std::vector<base::FilePath>());
|
|
}
|
|
}];
|
|
}
|
|
|
|
} // namespace
|
|
|
|
CefFileDialogRunnerMac::CefFileDialogRunnerMac() {}
|
|
|
|
void CefFileDialogRunnerMac::Run(CefBrowserHostImpl* browser,
|
|
const FileChooserParams& params,
|
|
RunFileChooserCallback callback) {
|
|
int filter_index = params.selected_accept_filter;
|
|
NSView* owner = CAST_CEF_WINDOW_HANDLE_TO_NSVIEW(browser->GetWindowHandle());
|
|
|
|
if (params.mode == blink::mojom::FileChooserParams::Mode::kOpen ||
|
|
params.mode == blink::mojom::FileChooserParams::Mode::kOpenMultiple ||
|
|
params.mode == blink::mojom::FileChooserParams::Mode::kUploadFolder) {
|
|
RunOpenFileDialog(params, owner, filter_index, callback);
|
|
} else if (params.mode == blink::mojom::FileChooserParams::Mode::kSave) {
|
|
RunSaveFileDialog(params, owner, filter_index, callback);
|
|
} else {
|
|
NOTIMPLEMENTED();
|
|
}
|
|
}
|