1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/renderer/pepper/pepper_file_chooser_host.h"
6
7#include "base/files/file_path.h"
8#include "base/strings/utf_string_conversions.h"
9#include "content/public/renderer/renderer_ppapi_host.h"
10#include "content/renderer/pepper/pepper_file_ref_renderer_host.h"
11#include "content/renderer/render_view_impl.h"
12#include "ppapi/c/pp_errors.h"
13#include "ppapi/host/dispatch_host_message.h"
14#include "ppapi/host/ppapi_host.h"
15#include "ppapi/proxy/ppapi_messages.h"
16#include "third_party/WebKit/public/platform/WebCString.h"
17#include "third_party/WebKit/public/platform/WebString.h"
18#include "third_party/WebKit/public/platform/WebVector.h"
19#include "third_party/WebKit/public/web/WebFileChooserCompletion.h"
20#include "third_party/WebKit/public/web/WebFileChooserParams.h"
21
22namespace content {
23
24class PepperFileChooserHost::CompletionHandler
25    : public blink::WebFileChooserCompletion {
26 public:
27  explicit CompletionHandler(const base::WeakPtr<PepperFileChooserHost>& host)
28      : host_(host) {}
29
30  virtual ~CompletionHandler() {}
31
32  virtual void didChooseFile(
33      const blink::WebVector<blink::WebString>& file_names) {
34    if (host_.get()) {
35      std::vector<PepperFileChooserHost::ChosenFileInfo> files;
36      for (size_t i = 0; i < file_names.size(); i++) {
37        files.push_back(PepperFileChooserHost::ChosenFileInfo(
38            file_names[i].utf8(), std::string()));
39      }
40      host_->StoreChosenFiles(files);
41    }
42
43    // It is the responsibility of this method to delete the instance.
44    delete this;
45  }
46  virtual void didChooseFile(
47      const blink::WebVector<SelectedFileInfo>& file_names) {
48    if (host_.get()) {
49      std::vector<PepperFileChooserHost::ChosenFileInfo> files;
50      for (size_t i = 0; i < file_names.size(); i++) {
51        files.push_back(PepperFileChooserHost::ChosenFileInfo(
52            file_names[i].path.utf8(), file_names[i].displayName.utf8()));
53      }
54      host_->StoreChosenFiles(files);
55    }
56
57    // It is the responsibility of this method to delete the instance.
58    delete this;
59  }
60
61 private:
62  base::WeakPtr<PepperFileChooserHost> host_;
63
64  DISALLOW_COPY_AND_ASSIGN(CompletionHandler);
65};
66
67PepperFileChooserHost::ChosenFileInfo::ChosenFileInfo(
68    const std::string& path,
69    const std::string& display_name)
70    : path(path), display_name(display_name) {}
71
72PepperFileChooserHost::PepperFileChooserHost(RendererPpapiHost* host,
73                                             PP_Instance instance,
74                                             PP_Resource resource)
75    : ResourceHost(host->GetPpapiHost(), instance, resource),
76      renderer_ppapi_host_(host),
77      handler_(NULL),
78      weak_factory_(this) {}
79
80PepperFileChooserHost::~PepperFileChooserHost() {}
81
82int32_t PepperFileChooserHost::OnResourceMessageReceived(
83    const IPC::Message& msg,
84    ppapi::host::HostMessageContext* context) {
85  PPAPI_BEGIN_MESSAGE_MAP(PepperFileChooserHost, msg)
86    PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileChooser_Show, OnShow)
87  PPAPI_END_MESSAGE_MAP()
88  return PP_ERROR_FAILED;
89}
90
91void PepperFileChooserHost::StoreChosenFiles(
92    const std::vector<ChosenFileInfo>& files) {
93  std::vector<IPC::Message> create_msgs;
94  std::vector<base::FilePath> file_paths;
95  std::vector<std::string> display_names;
96  for (size_t i = 0; i < files.size(); i++) {
97#if defined(OS_WIN)
98    base::FilePath file_path(base::UTF8ToWide(files[i].path));
99#else
100    base::FilePath file_path(files[i].path);
101#endif
102    file_paths.push_back(file_path);
103    create_msgs.push_back(PpapiHostMsg_FileRef_CreateForRawFS(file_path));
104    display_names.push_back(files[i].display_name);
105  }
106
107  if (!files.empty()) {
108    renderer_ppapi_host_->CreateBrowserResourceHosts(
109        pp_instance(),
110        create_msgs,
111        base::Bind(&PepperFileChooserHost::DidCreateResourceHosts,
112                   weak_factory_.GetWeakPtr(),
113                   file_paths,
114                   display_names));
115  } else {
116    reply_context_.params.set_result(PP_ERROR_USERCANCEL);
117    std::vector<ppapi::FileRefCreateInfo> chosen_files;
118    host()->SendReply(reply_context_,
119                      PpapiPluginMsg_FileChooser_ShowReply(chosen_files));
120    reply_context_ = ppapi::host::ReplyMessageContext();
121    handler_ = NULL;  // Handler deletes itself.
122  }
123}
124
125int32_t PepperFileChooserHost::OnShow(
126    ppapi::host::HostMessageContext* context,
127    bool save_as,
128    bool open_multiple,
129    const std::string& suggested_file_name,
130    const std::vector<std::string>& accept_mime_types) {
131  if (handler_)
132    return PP_ERROR_INPROGRESS;  // Already pending.
133
134  if (!host()->permissions().HasPermission(
135          ppapi::PERMISSION_BYPASS_USER_GESTURE) &&
136      !renderer_ppapi_host_->HasUserGesture(pp_instance())) {
137    return PP_ERROR_NO_USER_GESTURE;
138  }
139
140  blink::WebFileChooserParams params;
141  if (save_as) {
142    params.saveAs = true;
143    params.initialValue = blink::WebString::fromUTF8(
144        suggested_file_name.data(), suggested_file_name.size());
145  } else {
146    params.multiSelect = open_multiple;
147  }
148  std::vector<blink::WebString> mine_types(accept_mime_types.size());
149  for (size_t i = 0; i < accept_mime_types.size(); i++) {
150    mine_types[i] = blink::WebString::fromUTF8(accept_mime_types[i].data(),
151                                               accept_mime_types[i].size());
152  }
153  params.acceptTypes = mine_types;
154  params.directory = false;
155
156  handler_ = new CompletionHandler(AsWeakPtr());
157  RenderViewImpl* render_view = static_cast<RenderViewImpl*>(
158      renderer_ppapi_host_->GetRenderViewForInstance(pp_instance()));
159  if (!render_view || !render_view->runFileChooser(params, handler_)) {
160    delete handler_;
161    handler_ = NULL;
162    return PP_ERROR_NOACCESS;
163  }
164
165  reply_context_ = context->MakeReplyMessageContext();
166  return PP_OK_COMPLETIONPENDING;
167}
168
169void PepperFileChooserHost::DidCreateResourceHosts(
170    const std::vector<base::FilePath>& file_paths,
171    const std::vector<std::string>& display_names,
172    const std::vector<int>& browser_ids) {
173  DCHECK(file_paths.size() == display_names.size());
174  DCHECK(file_paths.size() == browser_ids.size());
175
176  std::vector<ppapi::FileRefCreateInfo> chosen_files;
177  for (size_t i = 0; i < browser_ids.size(); ++i) {
178    PepperFileRefRendererHost* renderer_host = new PepperFileRefRendererHost(
179        renderer_ppapi_host_, pp_instance(), 0, file_paths[i]);
180    int renderer_id =
181        renderer_ppapi_host_->GetPpapiHost()->AddPendingResourceHost(
182            scoped_ptr<ppapi::host::ResourceHost>(renderer_host));
183    ppapi::FileRefCreateInfo info = ppapi::MakeExternalFileRefCreateInfo(
184        file_paths[i], display_names[i], browser_ids[i], renderer_id);
185    chosen_files.push_back(info);
186  }
187
188  reply_context_.params.set_result(PP_OK);
189  host()->SendReply(reply_context_,
190                    PpapiPluginMsg_FileChooser_ShowReply(chosen_files));
191  reply_context_ = ppapi::host::ReplyMessageContext();
192  handler_ = NULL;  // Handler deletes itself.
193}
194
195}  // namespace content
196