blob: 84fb620f4bbee5c8f4f28fa5210ac48532d2bfc9 [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 "content/renderer/pepper/pepper_file_chooser_host.h"
#include <stddef.h>
#include "base/files/file_path.h"
#include "base/macros.h"
#include "base/strings/utf_string_conversions.h"
#include "content/public/renderer/renderer_ppapi_host.h"
#include "content/renderer/pepper/pepper_file_ref_renderer_host.h"
#include "content/renderer/render_view_impl.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/host/dispatch_host_message.h"
#include "ppapi/host/ppapi_host.h"
#include "ppapi/proxy/ppapi_messages.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/blink/public/mojom/choosers/file_chooser.mojom.h"
#include "third_party/blink/public/platform/file_path_conversion.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_vector.h"
namespace content {
using blink::mojom::FileChooserParams;
class PepperFileChooserHost::CompletionHandler {
public:
explicit CompletionHandler(const base::WeakPtr<PepperFileChooserHost>& host)
: host_(host) {}
~CompletionHandler() {}
bool OpenFileChooser(RenderFrameImpl* render_frame,
blink::mojom::FileChooserParamsPtr params) {
if (!render_frame)
return false;
render_frame->GetInterfaceProvider()->GetInterface(&file_chooser_);
file_chooser_.set_connection_error_handler(base::BindOnce(
&CompletionHandler::OnConnectionError, base::Unretained(this)));
file_chooser_->OpenFileChooser(
std::move(params), base::BindOnce(&CompletionHandler::DidChooseFiles,
base::Unretained(this)));
return true;
}
void DidChooseFiles(blink::mojom::FileChooserResultPtr result) {
if (host_.get()) {
std::vector<PepperFileChooserHost::ChosenFileInfo> files;
if (result) {
std::vector<blink::mojom::FileChooserFileInfoPtr> mojo_files =
std::move(result->files);
for (size_t i = 0; i < mojo_files.size(); i++) {
base::FilePath file_path =
mojo_files[i]->get_native_file()->file_path;
// Drop files of which names can not be converted to Unicode. We
// can't expose such files in Flash.
if (blink::FilePathToWebString(file_path).IsEmpty())
continue;
files.push_back(PepperFileChooserHost::ChosenFileInfo(
file_path, base::UTF16ToUTF8(
mojo_files[i]->get_native_file()->display_name)));
}
}
host_->StoreChosenFiles(files);
}
// It is the responsibility of this method to delete the instance.
delete this;
}
private:
void OnConnectionError() {
if (host_)
host_->StoreChosenFiles(
std::vector<PepperFileChooserHost::ChosenFileInfo>());
delete this;
}
base::WeakPtr<PepperFileChooserHost> host_;
blink::mojom::FileChooserPtr file_chooser_;
DISALLOW_COPY_AND_ASSIGN(CompletionHandler);
};
PepperFileChooserHost::ChosenFileInfo::ChosenFileInfo(
const base::FilePath& file_path,
const std::string& display_name)
: file_path(file_path), display_name(display_name) {}
PepperFileChooserHost::PepperFileChooserHost(RendererPpapiHost* host,
PP_Instance instance,
PP_Resource resource)
: ResourceHost(host->GetPpapiHost(), instance, resource),
renderer_ppapi_host_(host),
handler_(nullptr),
weak_factory_(this) {}
PepperFileChooserHost::~PepperFileChooserHost() {}
int32_t PepperFileChooserHost::OnResourceMessageReceived(
const IPC::Message& msg,
ppapi::host::HostMessageContext* context) {
PPAPI_BEGIN_MESSAGE_MAP(PepperFileChooserHost, msg)
PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileChooser_Show, OnShow)
PPAPI_END_MESSAGE_MAP()
return PP_ERROR_FAILED;
}
void PepperFileChooserHost::StoreChosenFiles(
const std::vector<ChosenFileInfo>& files) {
std::vector<IPC::Message> create_msgs;
std::vector<base::FilePath> file_paths;
std::vector<std::string> display_names;
for (size_t i = 0; i < files.size(); i++) {
base::FilePath file_path = files[i].file_path;
file_paths.push_back(file_path);
create_msgs.push_back(PpapiHostMsg_FileRef_CreateForRawFS(file_path));
display_names.push_back(files[i].display_name);
}
if (!files.empty()) {
renderer_ppapi_host_->CreateBrowserResourceHosts(
pp_instance(),
create_msgs,
base::Bind(&PepperFileChooserHost::DidCreateResourceHosts,
weak_factory_.GetWeakPtr(),
file_paths,
display_names));
} else {
reply_context_.params.set_result(PP_ERROR_USERCANCEL);
std::vector<ppapi::FileRefCreateInfo> chosen_files;
host()->SendReply(reply_context_,
PpapiPluginMsg_FileChooser_ShowReply(chosen_files));
reply_context_ = ppapi::host::ReplyMessageContext();
handler_ = nullptr; // Handler deletes itself.
}
}
int32_t PepperFileChooserHost::OnShow(
ppapi::host::HostMessageContext* context,
bool save_as,
bool open_multiple,
const std::string& suggested_file_name,
const std::vector<std::string>& accept_mime_types) {
if (handler_)
return PP_ERROR_INPROGRESS; // Already pending.
if (!host()->permissions().HasPermission(
ppapi::PERMISSION_BYPASS_USER_GESTURE) &&
!renderer_ppapi_host_->HasUserGesture(pp_instance())) {
return PP_ERROR_NO_USER_GESTURE;
}
auto params = FileChooserParams::New();
if (save_as) {
params->mode = FileChooserParams::Mode::kSave;
params->default_file_name =
base::FilePath::FromUTF8Unsafe(suggested_file_name).BaseName();
} else {
params->mode = open_multiple ? FileChooserParams::Mode::kOpenMultiple
: FileChooserParams::Mode::kOpen;
}
params->accept_types.reserve(accept_mime_types.size());
for (const auto& mime_type : accept_mime_types)
params->accept_types.push_back(base::UTF8ToUTF16(mime_type));
params->need_local_path = true;
params->requestor = renderer_ppapi_host_->GetDocumentURL(pp_instance());
handler_ = new CompletionHandler(AsWeakPtr());
RenderFrameImpl* render_frame = static_cast<RenderFrameImpl*>(
renderer_ppapi_host_->GetRenderFrameForInstance(pp_instance()));
if (!handler_->OpenFileChooser(render_frame, std::move(params))) {
delete handler_;
handler_ = nullptr;
return PP_ERROR_NOACCESS;
}
reply_context_ = context->MakeReplyMessageContext();
return PP_OK_COMPLETIONPENDING;
}
void PepperFileChooserHost::DidCreateResourceHosts(
const std::vector<base::FilePath>& file_paths,
const std::vector<std::string>& display_names,
const std::vector<int>& browser_ids) {
DCHECK(file_paths.size() == display_names.size());
DCHECK(file_paths.size() == browser_ids.size());
std::vector<ppapi::FileRefCreateInfo> chosen_files;
for (size_t i = 0; i < browser_ids.size(); ++i) {
PepperFileRefRendererHost* renderer_host = new PepperFileRefRendererHost(
renderer_ppapi_host_, pp_instance(), 0, file_paths[i]);
int renderer_id =
renderer_ppapi_host_->GetPpapiHost()->AddPendingResourceHost(
std::unique_ptr<ppapi::host::ResourceHost>(renderer_host));
ppapi::FileRefCreateInfo info = ppapi::MakeExternalFileRefCreateInfo(
file_paths[i], display_names[i], browser_ids[i], renderer_id);
chosen_files.push_back(info);
}
reply_context_.params.set_result(PP_OK);
host()->SendReply(reply_context_,
PpapiPluginMsg_FileChooser_ShowReply(chosen_files));
reply_context_ = ppapi::host::ReplyMessageContext();
handler_ = nullptr; // Handler deletes itself.
}
} // namespace content