blob: 7eedd5956dce34bd08e1e9359815ebf93f1f6f20 [file] [log] [blame]
// 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 "webkit/plugins/ppapi/ppb_file_chooser_impl.h"
#include <string>
#include <vector>
#include "base/logging.h"
#include "base/string_split.h"
#include "base/string_util.h"
#include "base/sys_string_conversions.h"
#include "ppapi/c/pp_completion_callback.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/shared_impl/tracked_callback.h"
#include "ppapi/shared_impl/var.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebCString.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileChooserCompletion.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFileChooserParams.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebString.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/platform/WebVector.h"
#include "webkit/glue/webkit_glue.h"
#include "webkit/plugins/ppapi/common.h"
#include "webkit/plugins/ppapi/ppb_file_ref_impl.h"
#include "webkit/plugins/ppapi/plugin_delegate.h"
#include "webkit/plugins/ppapi/plugin_module.h"
#include "webkit/plugins/ppapi/ppapi_plugin_instance.h"
#include "webkit/plugins/ppapi/resource_helper.h"
using ppapi::StringVar;
using ppapi::thunk::PPB_FileChooser_API;
using ppapi::TrackedCallback;
using WebKit::WebCString;
using WebKit::WebFileChooserCompletion;
using WebKit::WebFileChooserParams;
using WebKit::WebString;
using WebKit::WebVector;
namespace webkit {
namespace ppapi {
namespace {
class FileChooserCompletionImpl : public WebFileChooserCompletion {
public:
FileChooserCompletionImpl(PPB_FileChooser_Impl* file_chooser)
: file_chooser_(file_chooser) {
DCHECK(file_chooser_);
}
virtual ~FileChooserCompletionImpl() {}
virtual void didChooseFile(const WebVector<WebString>& file_names) {
std::vector<std::string> files;
for (size_t i = 0; i < file_names.size(); i++)
files.push_back(file_names[i].utf8());
file_chooser_->StoreChosenFiles(files);
// It is the responsibility of this method to delete the instance.
delete this;
}
virtual void didChooseFile(const WebVector<SelectedFileInfo>& file_names) {
std::vector<std::string> files;
for (size_t i = 0; i < file_names.size(); i++)
files.push_back(file_names[i].path.utf8());
file_chooser_->StoreChosenFiles(files);
// It is the responsibility of this method to delete the instance.
delete this;
}
private:
scoped_refptr<PPB_FileChooser_Impl> file_chooser_;
};
} // namespace
PPB_FileChooser_Impl::PPB_FileChooser_Impl(
PP_Instance instance,
PP_FileChooserMode_Dev mode,
const char* accept_types)
: Resource(::ppapi::OBJECT_IS_IMPL, instance),
mode_(mode),
next_chosen_file_index_(0) {
if (accept_types)
accept_types_ = std::string(accept_types);
}
PPB_FileChooser_Impl::~PPB_FileChooser_Impl() {
}
// static
PP_Resource PPB_FileChooser_Impl::Create(
PP_Instance instance,
PP_FileChooserMode_Dev mode,
const char* accept_types) {
if (mode != PP_FILECHOOSERMODE_OPEN &&
mode != PP_FILECHOOSERMODE_OPENMULTIPLE)
return 0;
return (new PPB_FileChooser_Impl(instance, mode,
accept_types))->GetReference();
}
PPB_FileChooser_Impl* PPB_FileChooser_Impl::AsPPB_FileChooser_Impl() {
return this;
}
PPB_FileChooser_API* PPB_FileChooser_Impl::AsPPB_FileChooser_API() {
return this;
}
void PPB_FileChooser_Impl::StoreChosenFiles(
const std::vector<std::string>& files) {
next_chosen_file_index_ = 0;
// It is possible that |callback_| has been run: before the user takes action
// on the file chooser, the page navigates away and causes the plugin module
// (whose instance requested to show the file chooser) to be destroyed. In
// that case, |callback_| has been aborted when we get here.
if (!TrackedCallback::IsPending(callback_)) {
// To be cautious, reset our internal state.
output_.Reset();
chosen_files_.clear();
return;
}
std::vector< scoped_refptr<Resource> > chosen_files;
for (std::vector<std::string>::const_iterator it = files.begin();
it != files.end(); ++it) {
#if defined(OS_WIN)
FilePath file_path(base::SysUTF8ToWide(*it));
#else
FilePath file_path(*it);
#endif
chosen_files.push_back(scoped_refptr<Resource>(
PPB_FileRef_Impl::CreateExternal(pp_instance(), file_path)));
}
int32_t result_code = (chosen_files.size() > 0) ? PP_OK : PP_ERROR_USERCANCEL;
if (output_.is_valid())
output_.StoreResourceVector(chosen_files);
else // v0.5 API.
chosen_files_.swap(chosen_files);
RunCallback(result_code);
}
int32_t PPB_FileChooser_Impl::ValidateCallback(
const PP_CompletionCallback& callback) {
// We only support non-blocking calls.
if (!callback.func)
return PP_ERROR_BLOCKS_MAIN_THREAD;
if (TrackedCallback::IsPending(callback_))
return PP_ERROR_INPROGRESS;
return PP_OK;
}
void PPB_FileChooser_Impl::RegisterCallback(
const PP_CompletionCallback& callback) {
DCHECK(callback.func);
DCHECK(!TrackedCallback::IsPending(callback_));
PluginModule* plugin_module = ResourceHelper::GetPluginModule(this);
if (!plugin_module)
return;
callback_ = new TrackedCallback(this, callback);
}
void PPB_FileChooser_Impl::RunCallback(int32_t result) {
TrackedCallback::ClearAndRun(&callback_, result);
}
int32_t PPB_FileChooser_Impl::Show(const PP_ArrayOutput& output,
const PP_CompletionCallback& callback) {
int32_t result = Show0_5(callback);
if (result == PP_OK_COMPLETIONPENDING)
output_.set_pp_array_output(output);
return result;
}
int32_t PPB_FileChooser_Impl::ShowWithoutUserGesture(
PP_Bool save_as,
PP_Var suggested_file_name,
const PP_ArrayOutput& output,
const PP_CompletionCallback& callback) {
int32_t result = ShowWithoutUserGesture0_5(save_as, suggested_file_name,
callback);
if (result == PP_OK_COMPLETIONPENDING)
output_.set_pp_array_output(output);
return result;
}
int32_t PPB_FileChooser_Impl::Show0_5(const PP_CompletionCallback& callback) {
PluginInstance* plugin_instance = ResourceHelper::GetPluginInstance(this);
if (!plugin_instance)
return PP_ERROR_FAILED;
if (!plugin_instance->IsProcessingUserGesture())
return PP_ERROR_NO_USER_GESTURE;
return ShowWithoutUserGesture0_5(PP_FALSE, PP_MakeUndefined(), callback);
}
int32_t PPB_FileChooser_Impl::ShowWithoutUserGesture0_5(
PP_Bool save_as,
PP_Var suggested_file_name,
const PP_CompletionCallback& callback) {
int32_t rv = ValidateCallback(callback);
if (rv != PP_OK)
return rv;
DCHECK((mode_ == PP_FILECHOOSERMODE_OPEN) ||
(mode_ == PP_FILECHOOSERMODE_OPENMULTIPLE));
WebFileChooserParams params;
if (save_as) {
params.saveAs = true;
StringVar* str = StringVar::FromPPVar(suggested_file_name);
if (str)
params.initialValue = WebString::fromUTF8(str->value().c_str());
} else {
params.multiSelect = (mode_ == PP_FILECHOOSERMODE_OPENMULTIPLE);
}
params.acceptTypes = ParseAcceptValue(accept_types_);
params.directory = false;
PluginDelegate* plugin_delegate = ResourceHelper::GetPluginDelegate(this);
if (!plugin_delegate)
return PP_ERROR_FAILED;
if (!plugin_delegate->RunFileChooser(params,
new FileChooserCompletionImpl(this)))
return PP_ERROR_FAILED;
RegisterCallback(callback);
return PP_OK_COMPLETIONPENDING;
}
PP_Resource PPB_FileChooser_Impl::GetNextChosenFile() {
if (next_chosen_file_index_ >= chosen_files_.size())
return 0;
return chosen_files_[next_chosen_file_index_++]->GetReference();
}
std::vector<WebString> PPB_FileChooser_Impl::ParseAcceptValue(
const std::string& accept_types) {
if (accept_types.empty())
return std::vector<WebString>();
std::vector<std::string> type_list;
base::SplitString(accept_types, ',', &type_list);
std::vector<WebString> normalized_type_list;
normalized_type_list.reserve(type_list.size());
for (size_t i = 0; i < type_list.size(); ++i) {
std::string type = type_list[i];
TrimWhitespaceASCII(type, TRIM_ALL, &type);
// If the type is a single character, it definitely cannot be valid. In the
// case of a file extension it would be a single ".". In the case of a MIME
// type it would just be a "/".
if (type.length() < 2)
continue;
if (type.find_first_of('/') == std::string::npos && type[0] != '.')
continue;
StringToLowerASCII(&type);
normalized_type_list.push_back(WebString::fromUTF8(type.data(),
type.size()));
}
return normalized_type_list;
}
} // namespace ppapi
} // namespace webkit