diff options
author | jcivelli@chromium.org <jcivelli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-10 16:54:49 +0000 |
---|---|---|
committer | jcivelli@chromium.org <jcivelli@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-09-10 16:54:49 +0000 |
commit | ba70d082593f9e20ab059b7f09495d94c0856005 (patch) | |
tree | e56ce8065866ebde7de1ad8137b2fe2ccc7cf8c2 /chrome/browser/file_select_helper.cc | |
parent | ba62fbd428232eee1815d4b4d63fc65cb3ead566 (diff) | |
download | chromium_src-ba70d082593f9e20ab059b7f09495d94c0856005.zip chromium_src-ba70d082593f9e20ab059b7f09495d94c0856005.tar.gz chromium_src-ba70d082593f9e20ab059b7f09495d94c0856005.tar.bz2 |
Input file type now supported in extension popups.
To do this, this CL generalize the TabContentsFileSelectHelper (renamed FileSelectHelper) so it is associated with a RenderViewHost rather than a TabContents.
This allows the extension popups which don't use a TabContents to use it.
As part of that, I also moved GetTopLevelNativeWindow() from TabContentsView to TabContent, as it can be implemented in a non-platform specific way.
BUG=28829
TEST=Make sure you can still open file on web pages (such as http://www.cs.tut.fi/~jkorpela/forms/file.html.
Create an extension with a popup that contains an input file tag. Make sure it does open a file dialog and lets you choose a file.
Review URL: http://codereview.chromium.org/3209002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@59105 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/file_select_helper.cc')
-rw-r--r-- | chrome/browser/file_select_helper.cc | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/chrome/browser/file_select_helper.cc b/chrome/browser/file_select_helper.cc new file mode 100644 index 0000000..6ee0b73 --- /dev/null +++ b/chrome/browser/file_select_helper.cc @@ -0,0 +1,242 @@ +// Copyright (c) 2010 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 "chrome/browser/file_select_helper.h" + +#include "app/l10n_util.h" +#include "base/file_util.h" +#include "base/string_split.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "net/base/mime_util.h" +#include "chrome/browser/platform_util.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_widget_host_view.h" +#include "chrome/browser/shell_dialogs.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/browser/tab_contents/tab_contents_view.h" +#include "chrome/common/notification_details.h" +#include "chrome/common/notification_source.h" +#include "chrome/common/render_messages_params.h" +#include "grit/generated_resources.h" + +FileSelectHelper::FileSelectHelper(Profile* profile) + : profile_(profile), + render_view_host_(NULL), + select_file_dialog_(), + dialog_type_(SelectFileDialog::SELECT_OPEN_FILE) { +} + +FileSelectHelper::~FileSelectHelper() { + // There may be pending file dialogs, we need to tell them that we've gone + // away so they don't try and call back to us. + if (select_file_dialog_.get()) + select_file_dialog_->ListenerDestroyed(); + + // Stop any pending directory enumeration and prevent a callback. + if (directory_lister_.get()) { + directory_lister_->set_delegate(NULL); + directory_lister_->Cancel(); + } +} + +void FileSelectHelper::FileSelected(const FilePath& path, + int index, void* params) { + if (!render_view_host_) + return; + + profile_->set_last_selected_directory(path.DirName()); + + if (dialog_type_ == SelectFileDialog::SELECT_FOLDER) { + DirectorySelected(path); + return; + } + + std::vector<FilePath> files; + files.push_back(path); + render_view_host_->FilesSelectedInChooser(files); + // We are done with this showing of the dialog. + render_view_host_ = NULL; +} + +void FileSelectHelper::MultiFilesSelected(const std::vector<FilePath>& files, + void* params) { + if (!files.empty()) + profile_->set_last_selected_directory(files[0].DirName()); + if (!render_view_host_) + return; + + render_view_host_->FilesSelectedInChooser(files); + // We are done with this showing of the dialog. + render_view_host_ = NULL; +} + +void FileSelectHelper::FileSelectionCanceled(void* params) { + if (!render_view_host_) + return; + + // If the user cancels choosing a file to upload we pass back an + // empty vector. + render_view_host_->FilesSelectedInChooser(std::vector<FilePath>()); + + // We are done with this showing of the dialog. + render_view_host_ = NULL; +} + +void FileSelectHelper::DirectorySelected(const FilePath& path) { + directory_lister_ = new net::DirectoryLister(path, + true, + net::DirectoryLister::NO_SORT, + this); + if (!directory_lister_->Start()) + FileSelectionCanceled(NULL); +} + +void FileSelectHelper::OnListFile( + const net::DirectoryLister::DirectoryListerData& data) { + // Directory upload only cares about files. This util call just checks + // the flags in the structure; there's no file I/O going on. + if (file_util::FileEnumerator::IsDirectory(data.info)) + return; + + directory_lister_results_.push_back(data.path); +} + +void FileSelectHelper::OnListDone(int error) { + if (!render_view_host_) + return; + + if (error) { + FileSelectionCanceled(NULL); + return; + } + + render_view_host_->FilesSelectedInChooser(directory_lister_results_); + render_view_host_ = NULL; + directory_lister_ = NULL; + directory_lister_results_.clear(); +} + +SelectFileDialog::FileTypeInfo* FileSelectHelper::GetFileTypesFromAcceptType( + const string16& accept_types) { + if (accept_types.empty()) + return NULL; + + // Split the accept-type string on commas. + std::vector<string16> mime_types; + base::SplitStringUsingSubstr(accept_types, ASCIIToUTF16(","), &mime_types); + if (mime_types.empty()) + return NULL; + + // Create FileTypeInfo and pre-allocate for the first extension list. + scoped_ptr<SelectFileDialog::FileTypeInfo> file_type( + new SelectFileDialog::FileTypeInfo()); + file_type->include_all_files = true; + file_type->extensions.resize(1); + std::vector<FilePath::StringType>* extensions = &file_type->extensions.back(); + + // Find the correspondinge extensions. + int valid_type_count = 0; + int description_id = 0; + for (size_t i = 0; i < mime_types.size(); ++i) { + string16 mime_type = mime_types[i]; + std::string ascii_mime_type = StringToLowerASCII(UTF16ToASCII(mime_type)); + + TrimWhitespace(ascii_mime_type, TRIM_ALL, &ascii_mime_type); + if (ascii_mime_type.empty()) + continue; + + size_t old_extension_size = extensions->size(); + if (ascii_mime_type == "image/*") { + description_id = IDS_IMAGE_FILES; + net::GetImageExtensions(extensions); + } else if (ascii_mime_type == "audio/*") { + description_id = IDS_AUDIO_FILES; + net::GetAudioExtensions(extensions); + } else if (ascii_mime_type == "video/*") { + description_id = IDS_VIDEO_FILES; + net::GetVideoExtensions(extensions); + } else { + net::GetExtensionsForMimeType(ascii_mime_type, extensions); + } + + if (extensions->size() > old_extension_size) + valid_type_count++; + } + + // Use a generic description "Custom Files" if either of the following is + // true: + // 1) There're multiple types specified, like "audio/*,video/*" + // 2) There're multiple extensions for a MIME type without parameter, like + // "ehtml,shtml,htm,html" for "text/html". On Windows, the select file + // dialog uses the first extension in the list to form the description, + // like "EHTML Files". This is not what we want. + if (valid_type_count > 1 || + (valid_type_count == 1 && description_id == 0 && extensions->size() > 1)) + description_id = IDS_CUSTOM_FILES; + + if (description_id) { + file_type->extension_description_overrides.push_back( + l10n_util::GetStringUTF16(description_id)); + } + + return file_type.release(); +} + +void FileSelectHelper::RunFileChooser( + RenderViewHost* render_view_host, + const ViewHostMsg_RunFileChooser_Params ¶ms) { + DCHECK(!render_view_host_); + render_view_host_ = render_view_host; + notification_registrar_.RemoveAll(); + notification_registrar_.Add(this, + NotificationType::RENDER_WIDGET_HOST_DESTROYED, + Source<RenderViewHost>(render_view_host)); + + if (!select_file_dialog_.get()) + select_file_dialog_ = SelectFileDialog::Create(this); + + switch (params.mode) { + case ViewHostMsg_RunFileChooser_Params::Open: + dialog_type_ = SelectFileDialog::SELECT_OPEN_FILE; + break; + case ViewHostMsg_RunFileChooser_Params::OpenMultiple: + dialog_type_ = SelectFileDialog::SELECT_OPEN_MULTI_FILE; + break; + case ViewHostMsg_RunFileChooser_Params::OpenFolder: + dialog_type_ = SelectFileDialog::SELECT_FOLDER; + break; + case ViewHostMsg_RunFileChooser_Params::Save: + dialog_type_ = SelectFileDialog::SELECT_SAVEAS_FILE; + break; + default: + dialog_type_ = SelectFileDialog::SELECT_OPEN_FILE; // Prevent warning. + NOTREACHED(); + } + scoped_ptr<SelectFileDialog::FileTypeInfo> file_types( + GetFileTypesFromAcceptType(params.accept_types)); + FilePath default_file_name = params.default_file_name; + if (default_file_name.empty()) + default_file_name = profile_->last_selected_directory(); + + gfx::NativeWindow owning_window = + platform_util::GetTopLevel(render_view_host_->view()->GetNativeView()); + select_file_dialog_->SelectFile(dialog_type_, + params.title, + default_file_name, + file_types.get(), + 0, + FILE_PATH_LITERAL(""), + owning_window, + NULL); +} + +void FileSelectHelper::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type == NotificationType::RENDER_WIDGET_HOST_DESTROYED); + DCHECK(Details<RenderViewHost>(details).ptr() == render_view_host_); + render_view_host_ = NULL; +} |