Skip to content

Commit 4b7add6

Browse files
Windows: Display select filters based on the "accept" attribute for input type="file" (issue #791).
git-svn-id: http://chromiumembedded.googlecode.com/svn/trunk/cef1@1027 5089003a-bbd8-11dd-ad1f-f1f9622dbc98
1 parent b1112f0 commit 4b7add6

File tree

5 files changed

+211
-22
lines changed

5 files changed

+211
-22
lines changed

libcef/browser_webview_delegate.cc

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -310,16 +310,20 @@ bool BrowserWebViewDelegate::runFileChooser(
310310
WebKit::WebFileChooserCompletion* chooser_completion) {
311311
// Support file open dialog.
312312
std::vector<FilePath> file_names;
313+
std::vector<std::string> mime_types;
314+
315+
for (size_t i = 0; i < params.acceptTypes.size(); ++i)
316+
mime_types.push_back(params.acceptTypes[i].utf8());
313317

314318
if (!ShowFileChooser(file_names, params.multiSelect, params.title,
315-
webkit_base::WebStringToFilePath(params.initialValue))) {
319+
webkit_base::WebStringToFilePath(params.initialValue),
320+
mime_types)) {
316321
return false;
317322
}
318323

319324
WebVector<WebString> ws_file_names(file_names.size());
320-
for (size_t i = 0; i < file_names.size(); ++i) {
325+
for (size_t i = 0; i < file_names.size(); ++i)
321326
ws_file_names[i] = webkit_base::FilePathToWebString(file_names[i]);
322-
}
323327

324328
chooser_completion->didChooseFile(ws_file_names);
325329

libcef/browser_webview_delegate.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -316,9 +316,10 @@ class BrowserWebViewDelegate : public WebKit::WebViewClient,
316316

317317
// Called to show the file chooser dialog.
318318
bool ShowFileChooser(std::vector<FilePath>& file_names,
319-
const bool multi_select,
319+
bool multi_select,
320320
const WebKit::WebString& title,
321-
const FilePath& default_file);
321+
const FilePath& default_file,
322+
const std::vector<std::string>& accept_mime_types);
322323

323324
// Called to show status messages.
324325
void ShowStatus(const WebKit::WebString& text, cef_handler_statustype_t type);

libcef/browser_webview_delegate_gtk.cc

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,10 +399,12 @@ bool BrowserWebViewDelegate::ShowJavaScriptPrompt(
399399
}
400400

401401
// Called to show the file chooser dialog.
402-
bool BrowserWebViewDelegate::ShowFileChooser(std::vector<FilePath>& file_names,
403-
const bool multi_select,
404-
const WebKit::WebString& title,
405-
const FilePath& default_file) {
402+
bool BrowserWebViewDelegate::ShowFileChooser(
403+
std::vector<FilePath>& file_names,
404+
bool multi_select,
405+
const WebKit::WebString& title,
406+
const FilePath& default_file,
407+
const std::vector<std::string>& accept_mime_types) {
406408
NOTIMPLEMENTED();
407409
return false;
408410
}

libcef/browser_webview_delegate_mac.mm

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -494,10 +494,12 @@ - (NSRect)_growBoxRect;
494494
}
495495

496496
// Called to show the file chooser dialog.
497-
bool BrowserWebViewDelegate::ShowFileChooser(std::vector<FilePath>& file_names,
498-
const bool multi_select,
499-
const WebKit::WebString& title,
500-
const FilePath& default_file) {
497+
bool BrowserWebViewDelegate::ShowFileChooser(
498+
std::vector<FilePath>& file_names,
499+
bool multi_select,
500+
const WebKit::WebString& title,
501+
const FilePath& default_file,
502+
const std::vector<std::string>& accept_mime_types) {
501503
NSOpenPanel* dialog = [NSOpenPanel openPanel];
502504
if (!title.isNull())
503505
[dialog setTitle:base::SysUTF16ToNSString(title)];

libcef/browser_webview_delegate_win.cc

Lines changed: 189 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,12 @@
2020
#include "libcef/drag_data_impl.h"
2121
#include "libcef/web_drop_target_win.h"
2222

23+
#include "base/i18n/case_conversion.h"
2324
#include "base/message_loop.h"
2425
#include "base/string_util.h"
26+
#include "base/utf_string_conversions.h"
27+
#include "base/win/registry.h"
28+
#include "net/base/mime_util.h"
2529
#include "net/base/net_errors.h"
2630
#include "third_party/WebKit/Source/WebKit/chromium/public/WebContextMenuData.h"
2731
#include "third_party/WebKit/Source/WebKit/chromium/public/WebCursorInfo.h"
@@ -31,6 +35,7 @@
3135
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebPoint.h"
3236
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebRect.h"
3337
#include "third_party/WebKit/Source/WebKit/chromium/public/WebView.h"
38+
#include "ui/base/l10n/l10n_util.h"
3439
#include "ui/gfx/gdi_util.h"
3540
#include "ui/gfx/native_widget_types.h"
3641
#include "ui/gfx/point.h"
@@ -61,6 +66,178 @@ namespace {
6166

6267
static const wchar_t kPluginWindowClassName[] = L"WebPluginHost";
6368

69+
// From ui/base/dialogs/select_file_dialog_win.cc.
70+
71+
// Get the file type description from the registry. This will be "Text Document"
72+
// for .txt files, "JPEG Image" for .jpg files, etc. If the registry doesn't
73+
// have an entry for the file type, we return false, true if the description was
74+
// found. 'file_ext' must be in form ".txt".
75+
static bool GetRegistryDescriptionFromExtension(const std::wstring& file_ext,
76+
std::wstring* reg_description) {
77+
DCHECK(reg_description);
78+
base::win::RegKey reg_ext(HKEY_CLASSES_ROOT, file_ext.c_str(), KEY_READ);
79+
std::wstring reg_app;
80+
if (reg_ext.ReadValue(NULL, &reg_app) == ERROR_SUCCESS && !reg_app.empty()) {
81+
base::win::RegKey reg_link(HKEY_CLASSES_ROOT, reg_app.c_str(), KEY_READ);
82+
if (reg_link.ReadValue(NULL, reg_description) == ERROR_SUCCESS)
83+
return true;
84+
}
85+
return false;
86+
}
87+
88+
// Set up a filter for a Save/Open dialog, which will consist of |file_ext| file
89+
// extensions (internally separated by semicolons), |ext_desc| as the text
90+
// descriptions of the |file_ext| types (optional), and (optionally) the default
91+
// 'All Files' view. The purpose of the filter is to show only files of a
92+
// particular type in a Windows Save/Open dialog box. The resulting filter is
93+
// returned. The filters created here are:
94+
// 1. only files that have 'file_ext' as their extension
95+
// 2. all files (only added if 'include_all_files' is true)
96+
// Example:
97+
// file_ext: { "*.txt", "*.htm;*.html" }
98+
// ext_desc: { "Text Document" }
99+
// returned: "Text Document\0*.txt\0HTML Document\0*.htm;*.html\0"
100+
// "All Files\0*.*\0\0" (in one big string)
101+
// If a description is not provided for a file extension, it will be retrieved
102+
// from the registry. If the file extension does not exist in the registry, it
103+
// will be omitted from the filter, as it is likely a bogus extension.
104+
std::wstring FormatFilterForExtensions(
105+
const std::vector<std::wstring>& file_ext,
106+
const std::vector<std::wstring>& ext_desc,
107+
bool include_all_files) {
108+
const std::wstring all_ext = L"*.*";
109+
const std::wstring all_desc =
110+
std::wstring(L"All Files") +
111+
L" (" + all_ext + L")";
112+
113+
DCHECK(file_ext.size() >= ext_desc.size());
114+
115+
if (file_ext.empty())
116+
include_all_files = true;
117+
118+
std::wstring result;
119+
120+
if (file_ext.size() > 1) {
121+
std::wstring extensions = JoinString(file_ext, L';');
122+
std::wstring all_supported_types = L"All Supported Types (" +
123+
extensions + L")";
124+
125+
result.append(all_supported_types.c_str(), all_supported_types.size() + 1);
126+
result.append(extensions.c_str(), extensions.size() + 1);
127+
}
128+
129+
for (size_t i = 0; i < file_ext.size(); ++i) {
130+
std::wstring ext = file_ext[i];
131+
std::wstring desc;
132+
if (i < ext_desc.size())
133+
desc = ext_desc[i];
134+
135+
if (ext.empty()) {
136+
// Force something reasonable to appear in the dialog box if there is no
137+
// extension provided.
138+
include_all_files = true;
139+
continue;
140+
}
141+
142+
if (desc.empty()) {
143+
DCHECK(ext.find(L'.') != std::wstring::npos);
144+
std::wstring first_extension = ext.substr(ext.find(L'.'));
145+
size_t first_separator_index = first_extension.find(L';');
146+
if (first_separator_index != std::wstring::npos)
147+
first_extension = first_extension.substr(0, first_separator_index);
148+
149+
// Find the extension name without the preceeding '.' character.
150+
std::wstring ext_name = first_extension;
151+
size_t ext_index = ext_name.find_first_not_of(L'.');
152+
if (ext_index != std::wstring::npos)
153+
ext_name = ext_name.substr(ext_index);
154+
155+
if (!GetRegistryDescriptionFromExtension(first_extension, &desc)) {
156+
// The extension doesn't exist in the registry. Create a description
157+
// based on the unknown extension type (i.e. if the extension is .qqq,
158+
// the we create a description "QQQ File (.qqq)").
159+
include_all_files = true;
160+
std::vector<string16> replacements;
161+
replacements.push_back(base::i18n::ToUpper(WideToUTF16(ext_name)));
162+
replacements.push_back(ext_name);
163+
desc = ReplaceStringPlaceholders(
164+
L"$1 File (.$2)",
165+
replacements,
166+
NULL);
167+
}
168+
}
169+
170+
if (!desc.empty())
171+
desc += L" (" + ext + L")";
172+
else
173+
desc = ext;
174+
175+
result.append(desc.c_str(), desc.size() + 1); // Append NULL too.
176+
result.append(ext.c_str(), ext.size() + 1);
177+
}
178+
179+
if (include_all_files) {
180+
result.append(all_desc.c_str(), all_desc.size() + 1);
181+
result.append(all_ext.c_str(), all_ext.size() + 1);
182+
}
183+
184+
result.append(1, '\0'); // Double NULL required.
185+
return result;
186+
}
187+
188+
std::wstring GetDescriptionFromMimeType(const std::string& mime_type) {
189+
// Check for wild card mime types and return an appropriate description.
190+
static const struct {
191+
const char* mime_type;
192+
std::wstring full_string;
193+
} kWildCardMimeTypes[] = {
194+
{ "audio", L"Audio Files" },
195+
{ "image", L"Image Files" },
196+
{ "text", L"Text Files" },
197+
{ "video", L"Video Files" },
198+
};
199+
200+
for (size_t i = 0; i < arraysize(kWildCardMimeTypes); ++i) {
201+
if (mime_type == std::string(kWildCardMimeTypes[i].mime_type) + "/*")
202+
return std::wstring(kWildCardMimeTypes[i].full_string);
203+
}
204+
205+
return std::wstring();
206+
}
207+
208+
std::wstring GetFilterStringFromAcceptTypes(
209+
const std::vector<std::string>& accept_types) {
210+
std::vector<std::wstring> extensions;
211+
std::vector<std::wstring> descriptions;
212+
213+
for (size_t i = 0; i < accept_types.size(); ++i) {
214+
std::string ascii_type = accept_types[i];
215+
if (ascii_type.length()) {
216+
// Just treat as extension if contains '.' as the first character.
217+
if (ascii_type[0] == '.') {
218+
extensions.push_back(L"*" + ASCIIToWide(ascii_type));
219+
descriptions.push_back(std::wstring());
220+
} else {
221+
// Otherwise convert mime type to one or more extensions.
222+
std::vector<FilePath::StringType> ext;
223+
std::wstring ext_str;
224+
net::GetExtensionsForMimeType(ascii_type, &ext);
225+
if (ext.size() > 0) {
226+
for (size_t x = 0; x < ext.size(); ++x) {
227+
if (x != 0)
228+
ext_str += L";";
229+
ext_str += L"*." + ext[x];
230+
}
231+
extensions.push_back(ext_str);
232+
descriptions.push_back(GetDescriptionFromMimeType(ascii_type));
233+
}
234+
}
235+
}
236+
}
237+
238+
return FormatFilterForExtensions(extensions, descriptions, true);
239+
}
240+
64241
void AddMenuItem(CefRefPtr<CefBrowser> browser,
65242
CefRefPtr<CefMenuHandler> handler,
66243
HMENU menu,
@@ -648,23 +825,26 @@ bool RunOpenMultiFileDialog(const std::wstring& filter, HWND owner,
648825

649826
} // namespace
650827

651-
bool BrowserWebViewDelegate::ShowFileChooser(std::vector<FilePath>& file_names,
652-
const bool multi_select,
653-
const WebKit::WebString& title,
654-
const FilePath& default_file) {
828+
bool BrowserWebViewDelegate::ShowFileChooser(
829+
std::vector<FilePath>& file_names,
830+
bool multi_select,
831+
const WebKit::WebString& title,
832+
const FilePath& default_file,
833+
const std::vector<std::string>& accept_mime_types) {
655834
bool result = false;
835+
const std::wstring& filter =
836+
GetFilterStringFromAcceptTypes(accept_mime_types);
656837

657838
if (multi_select) {
658-
result = RunOpenMultiFileDialog(L"", browser_->UIT_GetMainWndHandle(),
659-
&file_names);
839+
result = RunOpenMultiFileDialog(filter, browser_->UIT_GetMainWndHandle(),
840+
&file_names);
660841
} else {
661842
FilePath file_name;
662-
result = RunOpenFileDialog(L"", browser_->UIT_GetMainWndHandle(),
663-
&file_name);
843+
result = RunOpenFileDialog(filter, browser_->UIT_GetMainWndHandle(),
844+
&file_name);
664845
if (result)
665846
file_names.push_back(file_name);
666847
}
667848

668849
return result;
669850
}
670-

0 commit comments

Comments
 (0)