Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / renderer_host / pepper / pepper_file_system_browser_host.cc
blobbdb2514007043e0d33c2d878fd72beda0ad9965f
1 // Copyright 2013 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.
5 #include "content/browser/renderer_host/pepper/pepper_file_system_browser_host.h"
7 #include "base/bind.h"
8 #include "base/callback.h"
9 #include "content/browser/renderer_host/pepper/pepper_file_io_host.h"
10 #include "content/browser/renderer_host/pepper/quota_reservation.h"
11 #include "content/common/pepper_file_util.h"
12 #include "content/public/browser/browser_ppapi_host.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "content/public/browser/plugin_service.h"
15 #include "content/public/browser/render_process_host.h"
16 #include "content/public/browser/storage_partition.h"
17 #include "content/public/common/pepper_plugin_info.h"
18 #include "net/base/mime_util.h"
19 #include "ppapi/c/pp_errors.h"
20 #include "ppapi/host/dispatch_host_message.h"
21 #include "ppapi/host/ppapi_host.h"
22 #include "ppapi/proxy/ppapi_messages.h"
23 #include "ppapi/shared_impl/file_system_util.h"
24 #include "ppapi/shared_impl/file_type_conversion.h"
25 #include "storage/browser/fileapi/file_system_operation_runner.h"
26 #include "storage/browser/fileapi/isolated_context.h"
27 #include "storage/browser/quota/quota_manager_proxy.h"
28 #include "storage/common/fileapi/file_system_util.h"
29 #include "storage/common/quota/quota_types.h"
31 namespace content {
33 namespace {
35 // This is the minimum amount of quota we reserve per file system.
36 const int64_t kMinimumQuotaReservationSize = 1024 * 1024; // 1 MB
38 scoped_refptr<storage::FileSystemContext> GetFileSystemContextFromRenderId(
39 int render_process_id) {
40 DCHECK_CURRENTLY_ON(BrowserThread::UI);
41 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
42 if (!host)
43 return NULL;
44 StoragePartition* storage_partition = host->GetStoragePartition();
45 if (!storage_partition)
46 return NULL;
47 return storage_partition->GetFileSystemContext();
50 } // namespace
52 PepperFileSystemBrowserHost::PepperFileSystemBrowserHost(BrowserPpapiHost* host,
53 PP_Instance instance,
54 PP_Resource resource,
55 PP_FileSystemType type)
56 : ResourceHost(host->GetPpapiHost(), instance, resource),
57 browser_ppapi_host_(host),
58 type_(type),
59 called_open_(false),
60 opened_(false),
61 file_system_context_(NULL),
62 reserved_quota_(0),
63 reserving_quota_(false),
64 weak_factory_(this) {}
66 PepperFileSystemBrowserHost::~PepperFileSystemBrowserHost() {
67 // If |files_| is not empty, the plugin failed to close some files. It must
68 // have crashed.
69 if (!files_.empty()) {
70 file_system_context_->default_file_task_runner()->PostTask(
71 FROM_HERE,
72 base::Bind(&QuotaReservation::OnClientCrash, quota_reservation_));
75 // All FileRefs and FileIOs that reference us must have been destroyed. Cancel
76 // all pending file system operations.
77 if (file_system_operation_runner_)
78 file_system_operation_runner_->Shutdown();
81 void PepperFileSystemBrowserHost::OpenExisting(const GURL& root_url,
82 const base::Closure& callback) {
83 root_url_ = root_url;
84 int render_process_id = 0;
85 int unused;
86 if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(
87 pp_instance(), &render_process_id, &unused)) {
88 NOTREACHED();
90 called_open_ = true;
91 // Get the file system context asynchronously, and then complete the Open
92 // operation by calling |callback|.
93 BrowserThread::PostTaskAndReplyWithResult(
94 BrowserThread::UI,
95 FROM_HERE,
96 base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
97 base::Bind(&PepperFileSystemBrowserHost::OpenExistingFileSystem,
98 weak_factory_.GetWeakPtr(),
99 callback));
102 int32_t PepperFileSystemBrowserHost::OnResourceMessageReceived(
103 const IPC::Message& msg,
104 ppapi::host::HostMessageContext* context) {
105 PPAPI_BEGIN_MESSAGE_MAP(PepperFileSystemBrowserHost, msg)
106 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileSystem_Open,
107 OnHostMsgOpen)
108 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
109 PpapiHostMsg_FileSystem_InitIsolatedFileSystem,
110 OnHostMsgInitIsolatedFileSystem)
111 PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileSystem_ReserveQuota,
112 OnHostMsgReserveQuota)
113 PPAPI_END_MESSAGE_MAP()
114 return PP_ERROR_FAILED;
117 bool PepperFileSystemBrowserHost::IsFileSystemHost() { return true; }
119 void PepperFileSystemBrowserHost::OpenQuotaFile(
120 PepperFileIOHost* file_io_host,
121 const storage::FileSystemURL& url,
122 const OpenQuotaFileCallback& callback) {
123 int32_t id = file_io_host->pp_resource();
124 std::pair<FileMap::iterator, bool> insert_result =
125 files_.insert(std::make_pair(id, file_io_host));
126 if (insert_result.second) {
127 base::PostTaskAndReplyWithResult(
128 file_system_context_->default_file_task_runner(),
129 FROM_HERE,
130 base::Bind(&QuotaReservation::OpenFile, quota_reservation_, id, url),
131 callback);
132 } else {
133 NOTREACHED();
137 void PepperFileSystemBrowserHost::CloseQuotaFile(
138 PepperFileIOHost* file_io_host,
139 const ppapi::FileGrowth& file_growth) {
140 int32_t id = file_io_host->pp_resource();
141 FileMap::iterator it = files_.find(id);
142 if (it != files_.end()) {
143 files_.erase(it);
144 } else {
145 NOTREACHED();
146 return;
149 file_system_context_->default_file_task_runner()->PostTask(
150 FROM_HERE,
151 base::Bind(
152 &QuotaReservation::CloseFile, quota_reservation_, id, file_growth));
155 int32_t PepperFileSystemBrowserHost::OnHostMsgOpen(
156 ppapi::host::HostMessageContext* context,
157 int64_t /* unused */) {
158 // TODO(raymes): The file system size is now unused by FileSystemDispatcher.
159 // Figure out why. Why is the file system size signed?
161 // Not allow multiple opens.
162 if (called_open_)
163 return PP_ERROR_INPROGRESS;
164 called_open_ = true;
166 storage::FileSystemType file_system_type =
167 PepperFileSystemTypeToFileSystemType(type_);
168 if (file_system_type == storage::kFileSystemTypeUnknown)
169 return PP_ERROR_FAILED;
171 int render_process_id = 0;
172 int unused;
173 if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(
174 pp_instance(), &render_process_id, &unused)) {
175 return PP_ERROR_FAILED;
178 BrowserThread::PostTaskAndReplyWithResult(
179 BrowserThread::UI,
180 FROM_HERE,
181 base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
182 base::Bind(&PepperFileSystemBrowserHost::OpenFileSystem,
183 weak_factory_.GetWeakPtr(),
184 context->MakeReplyMessageContext(),
185 file_system_type));
186 return PP_OK_COMPLETIONPENDING;
189 void PepperFileSystemBrowserHost::OpenExistingFileSystem(
190 const base::Closure& callback,
191 scoped_refptr<storage::FileSystemContext> file_system_context) {
192 if (file_system_context.get()) {
193 opened_ = true;
194 } else {
195 // If there is no file system context, we log a warning and continue with an
196 // invalid resource (which will produce errors when used), since we have no
197 // way to communicate the error to the caller.
198 LOG(WARNING) << "Could not retrieve file system context.";
200 SetFileSystemContext(file_system_context);
202 if (ShouldCreateQuotaReservation())
203 CreateQuotaReservation(callback);
204 else
205 callback.Run();
208 void PepperFileSystemBrowserHost::OpenFileSystem(
209 ppapi::host::ReplyMessageContext reply_context,
210 storage::FileSystemType file_system_type,
211 scoped_refptr<storage::FileSystemContext> file_system_context) {
212 if (!file_system_context.get()) {
213 OpenFileSystemComplete(
214 reply_context, GURL(), std::string(), base::File::FILE_ERROR_FAILED);
215 return;
218 SetFileSystemContext(file_system_context);
220 GURL origin =
221 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin();
222 file_system_context_->OpenFileSystem(
223 origin,
224 file_system_type,
225 storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
226 base::Bind(&PepperFileSystemBrowserHost::OpenFileSystemComplete,
227 weak_factory_.GetWeakPtr(),
228 reply_context));
231 void PepperFileSystemBrowserHost::OpenFileSystemComplete(
232 ppapi::host::ReplyMessageContext reply_context,
233 const GURL& root,
234 const std::string& /* unused */,
235 base::File::Error error) {
236 int32 pp_error = ppapi::FileErrorToPepperError(error);
237 if (pp_error == PP_OK) {
238 opened_ = true;
239 root_url_ = root;
241 if (ShouldCreateQuotaReservation()) {
242 CreateQuotaReservation(
243 base::Bind(&PepperFileSystemBrowserHost::SendReplyForFileSystem,
244 weak_factory_.GetWeakPtr(),
245 reply_context,
246 static_cast<int32_t>(PP_OK)));
247 return;
250 SendReplyForFileSystem(reply_context, pp_error);
253 void PepperFileSystemBrowserHost::OpenIsolatedFileSystem(
254 ppapi::host::ReplyMessageContext reply_context,
255 const std::string& fsid,
256 PP_IsolatedFileSystemType_Private type,
257 scoped_refptr<storage::FileSystemContext> file_system_context) {
258 if (!file_system_context.get()) {
259 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
260 return;
262 SetFileSystemContext(file_system_context);
264 root_url_ = GURL(storage::GetIsolatedFileSystemRootURIString(
265 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
266 fsid,
267 ppapi::IsolatedFileSystemTypeToRootName(type)));
268 if (!root_url_.is_valid()) {
269 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
270 return;
273 switch (type) {
274 case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_CRX:
275 opened_ = true;
276 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_OK);
277 return;
278 case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE:
279 OpenPluginPrivateFileSystem(reply_context, fsid, file_system_context_);
280 return;
281 default:
282 NOTREACHED();
283 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_BADARGUMENT);
284 return;
288 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystem(
289 ppapi::host::ReplyMessageContext reply_context,
290 const std::string& fsid,
291 scoped_refptr<storage::FileSystemContext> file_system_context) {
292 GURL origin =
293 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin();
294 if (!origin.is_valid()) {
295 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
296 return;
299 const std::string& plugin_id = GeneratePluginId(GetPluginMimeType());
300 if (plugin_id.empty()) {
301 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_BADARGUMENT);
302 return;
305 file_system_context->OpenPluginPrivateFileSystem(
306 origin,
307 storage::kFileSystemTypePluginPrivate,
308 fsid,
309 plugin_id,
310 storage::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
311 base::Bind(
312 &PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete,
313 weak_factory_.GetWeakPtr(),
314 reply_context,
315 fsid));
318 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete(
319 ppapi::host::ReplyMessageContext reply_context,
320 const std::string& fsid,
321 base::File::Error error) {
322 int32 pp_error = ppapi::FileErrorToPepperError(error);
323 if (pp_error == PP_OK)
324 opened_ = true;
325 SendReplyForIsolatedFileSystem(reply_context, fsid, pp_error);
328 int32_t PepperFileSystemBrowserHost::OnHostMsgInitIsolatedFileSystem(
329 ppapi::host::HostMessageContext* context,
330 const std::string& fsid,
331 PP_IsolatedFileSystemType_Private type) {
332 // Do not allow multiple opens.
333 if (called_open_)
334 return PP_ERROR_INPROGRESS;
335 called_open_ = true;
337 // Do a sanity check.
338 if (!storage::ValidateIsolatedFileSystemId(fsid))
339 return PP_ERROR_BADARGUMENT;
341 int render_process_id = 0;
342 int unused;
343 if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(
344 pp_instance(), &render_process_id, &unused)) {
345 storage::IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
346 return PP_ERROR_FAILED;
349 root_url_ = GURL(storage::GetIsolatedFileSystemRootURIString(
350 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
351 fsid,
352 ppapi::IsolatedFileSystemTypeToRootName(type)));
354 BrowserThread::PostTaskAndReplyWithResult(
355 BrowserThread::UI,
356 FROM_HERE,
357 base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
358 base::Bind(&PepperFileSystemBrowserHost::OpenIsolatedFileSystem,
359 weak_factory_.GetWeakPtr(),
360 context->MakeReplyMessageContext(),
361 fsid,
362 type));
363 return PP_OK_COMPLETIONPENDING;
366 int32_t PepperFileSystemBrowserHost::OnHostMsgReserveQuota(
367 ppapi::host::HostMessageContext* context,
368 int64_t amount,
369 const ppapi::FileGrowthMap& file_growths) {
370 DCHECK(ChecksQuota());
371 DCHECK_GT(amount, 0);
373 if (reserving_quota_)
374 return PP_ERROR_INPROGRESS;
375 reserving_quota_ = true;
377 int64_t reservation_amount =
378 std::max<int64_t>(kMinimumQuotaReservationSize, amount);
379 file_system_context_->default_file_task_runner()->PostTask(
380 FROM_HERE,
381 base::Bind(&QuotaReservation::ReserveQuota,
382 quota_reservation_,
383 reservation_amount,
384 file_growths,
385 base::Bind(&PepperFileSystemBrowserHost::GotReservedQuota,
386 weak_factory_.GetWeakPtr(),
387 context->MakeReplyMessageContext())));
389 return PP_OK_COMPLETIONPENDING;
392 void PepperFileSystemBrowserHost::SendReplyForFileSystem(
393 ppapi::host::ReplyMessageContext reply_context,
394 int32_t pp_error) {
395 reply_context.params.set_result(pp_error);
396 host()->SendReply(reply_context, PpapiPluginMsg_FileSystem_OpenReply());
399 void PepperFileSystemBrowserHost::SendReplyForIsolatedFileSystem(
400 ppapi::host::ReplyMessageContext reply_context,
401 const std::string& fsid,
402 int32_t error) {
403 if (error != PP_OK)
404 storage::IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
405 reply_context.params.set_result(error);
406 host()->SendReply(reply_context,
407 PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply());
410 void PepperFileSystemBrowserHost::SetFileSystemContext(
411 scoped_refptr<storage::FileSystemContext> file_system_context) {
412 file_system_context_ = file_system_context;
413 if (type_ != PP_FILESYSTEMTYPE_EXTERNAL || root_url_.is_valid()) {
414 file_system_operation_runner_ =
415 file_system_context_->CreateFileSystemOperationRunner();
419 bool PepperFileSystemBrowserHost::ShouldCreateQuotaReservation() const {
420 // Some file system types don't have quota.
421 if (!ppapi::FileSystemTypeHasQuota(type_))
422 return false;
424 // For file system types with quota, some origins have unlimited storage.
425 storage::QuotaManagerProxy* quota_manager_proxy =
426 file_system_context_->quota_manager_proxy();
427 CHECK(quota_manager_proxy);
428 CHECK(quota_manager_proxy->quota_manager());
429 storage::FileSystemType file_system_type =
430 PepperFileSystemTypeToFileSystemType(type_);
431 return !quota_manager_proxy->quota_manager()->IsStorageUnlimited(
432 root_url_.GetOrigin(),
433 storage::FileSystemTypeToQuotaStorageType(file_system_type));
436 void PepperFileSystemBrowserHost::CreateQuotaReservation(
437 const base::Closure& callback) {
438 DCHECK(root_url_.is_valid());
439 base::PostTaskAndReplyWithResult(
440 file_system_context_->default_file_task_runner(),
441 FROM_HERE,
442 base::Bind(&QuotaReservation::Create,
443 file_system_context_,
444 root_url_.GetOrigin(),
445 PepperFileSystemTypeToFileSystemType(type_)),
446 base::Bind(&PepperFileSystemBrowserHost::GotQuotaReservation,
447 weak_factory_.GetWeakPtr(),
448 callback));
451 void PepperFileSystemBrowserHost::GotQuotaReservation(
452 const base::Closure& callback,
453 scoped_refptr<QuotaReservation> quota_reservation) {
454 quota_reservation_ = quota_reservation;
455 callback.Run();
458 void PepperFileSystemBrowserHost::GotReservedQuota(
459 ppapi::host::ReplyMessageContext reply_context,
460 int64_t amount,
461 const ppapi::FileSizeMap& file_sizes) {
462 DCHECK(reserving_quota_);
463 reserving_quota_ = false;
464 reserved_quota_ = amount;
466 reply_context.params.set_result(PP_OK);
467 host()->SendReply(
468 reply_context,
469 PpapiPluginMsg_FileSystem_ReserveQuotaReply(amount, file_sizes));
472 std::string PepperFileSystemBrowserHost::GetPluginMimeType() const {
473 base::FilePath plugin_path = browser_ppapi_host_->GetPluginPath();
474 PepperPluginInfo* info =
475 PluginService::GetInstance()->GetRegisteredPpapiPluginInfo(plugin_path);
476 if (!info || info->mime_types.empty())
477 return std::string();
478 // Use the first element in |info->mime_types| even if several elements exist.
479 return info->mime_types[0].mime_type;
482 std::string PepperFileSystemBrowserHost::GeneratePluginId(
483 const std::string& mime_type) const {
484 // TODO(nhiroki): This function is very specialized for specific plugins (MIME
485 // types). If we bring this API to stable, we might have to make it more
486 // general.
488 std::string top_level_type;
489 std::string subtype;
490 if (!net::ParseMimeTypeWithoutParameter(
491 mime_type, &top_level_type, &subtype) ||
492 !net::IsValidTopLevelMimeType(top_level_type))
493 return std::string();
495 // Replace a slash used for type/subtype separator with an underscore.
496 std::string output = top_level_type + "_" + subtype;
498 // Verify |output| contains only alphabets, digits, or "._-".
499 for (std::string::const_iterator it = output.begin(); it != output.end();
500 ++it) {
501 if (!base::IsAsciiAlpha(*it) && !base::IsAsciiDigit(*it) &&
502 *it != '.' && *it != '_' && *it != '-') {
503 LOG(WARNING) << "Failed to generate a plugin id.";
504 return std::string();
507 return output;
510 } // namespace content