IndexedDBFactory now ForceCloses databases.
[chromium-blink-merge.git] / content / browser / renderer_host / pepper / pepper_file_system_browser_host.cc
blob4b13f43e456ee4ea4266c1fe92295c791c060455
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/public/browser/browser_ppapi_host.h"
12 #include "content/public/browser/browser_thread.h"
13 #include "content/public/browser/plugin_service.h"
14 #include "content/public/browser/render_process_host.h"
15 #include "content/public/browser/storage_partition.h"
16 #include "content/public/common/pepper_plugin_info.h"
17 #include "net/base/mime_util.h"
18 #include "ppapi/c/pp_errors.h"
19 #include "ppapi/host/dispatch_host_message.h"
20 #include "ppapi/host/ppapi_host.h"
21 #include "ppapi/proxy/ppapi_messages.h"
22 #include "ppapi/shared_impl/file_system_util.h"
23 #include "ppapi/shared_impl/file_type_conversion.h"
24 #include "webkit/browser/fileapi/file_system_operation_runner.h"
25 #include "webkit/browser/fileapi/isolated_context.h"
26 #include "webkit/browser/quota/quota_manager.h"
27 #include "webkit/common/fileapi/file_system_util.h"
28 #include "webkit/common/quota/quota_types.h"
30 namespace content {
32 namespace {
34 // This is the minimum amount of quota we reserve per file system.
35 const int64_t kMinimumQuotaReservationSize = 1024 * 1024; // 1 MB
37 scoped_refptr<fileapi::FileSystemContext>
38 GetFileSystemContextFromRenderId(int render_process_id) {
39 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
40 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
41 if (!host)
42 return NULL;
43 StoragePartition* storage_partition = host->GetStoragePartition();
44 if (!storage_partition)
45 return NULL;
46 return storage_partition->GetFileSystemContext();
49 } // namespace
51 PepperFileSystemBrowserHost::PepperFileSystemBrowserHost(BrowserPpapiHost* host,
52 PP_Instance instance,
53 PP_Resource resource,
54 PP_FileSystemType type)
55 : ResourceHost(host->GetPpapiHost(), instance, resource),
56 browser_ppapi_host_(host),
57 type_(type),
58 called_open_(false),
59 opened_(false),
60 file_system_context_(NULL),
61 reserved_quota_(0),
62 reserving_quota_(false),
63 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(), callback));
101 int32_t PepperFileSystemBrowserHost::OnResourceMessageReceived(
102 const IPC::Message& msg,
103 ppapi::host::HostMessageContext* context) {
104 IPC_BEGIN_MESSAGE_MAP(PepperFileSystemBrowserHost, msg)
105 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
106 PpapiHostMsg_FileSystem_Open,
107 OnHostMsgOpen)
108 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
109 PpapiHostMsg_FileSystem_InitIsolatedFileSystem,
110 OnHostMsgInitIsolatedFileSystem)
111 PPAPI_DISPATCH_HOST_RESOURCE_CALL(
112 PpapiHostMsg_FileSystem_ReserveQuota,
113 OnHostMsgReserveQuota)
114 IPC_END_MESSAGE_MAP()
115 return PP_ERROR_FAILED;
118 bool PepperFileSystemBrowserHost::IsFileSystemHost() {
119 return true;
122 void PepperFileSystemBrowserHost::OpenQuotaFile(
123 PepperFileIOHost* file_io_host,
124 const base::FilePath& file_path,
125 const OpenQuotaFileCallback& callback) {
126 int32_t id = file_io_host->pp_resource();
127 std::pair<FileMap::iterator, bool> insert_result =
128 files_.insert(std::make_pair(id, file_io_host));
129 if (insert_result.second) {
130 base::PostTaskAndReplyWithResult(
131 file_system_context_->default_file_task_runner(),
132 FROM_HERE,
133 base::Bind(&QuotaReservation::OpenFile,
134 quota_reservation_,
136 file_path),
137 callback);
138 } else {
139 NOTREACHED();
143 void PepperFileSystemBrowserHost::CloseQuotaFile(
144 PepperFileIOHost* file_io_host,
145 int64_t max_written_offset) {
146 int32_t id = file_io_host->pp_resource();
147 FileMap::iterator it = files_.find(id);
148 if (it != files_.end()) {
149 files_.erase(it);
150 } else {
151 NOTREACHED();
152 return;
155 file_system_context_->default_file_task_runner()->PostTask(
156 FROM_HERE,
157 base::Bind(&QuotaReservation::CloseFile,
158 quota_reservation_,
160 max_written_offset));
163 int32_t PepperFileSystemBrowserHost::OnHostMsgOpen(
164 ppapi::host::HostMessageContext* context,
165 int64_t /* unused */) {
166 // TODO(raymes): The file system size is now unused by FileSystemDispatcher.
167 // Figure out why. Why is the file system size signed?
169 // Not allow multiple opens.
170 if (called_open_)
171 return PP_ERROR_INPROGRESS;
172 called_open_ = true;
174 fileapi::FileSystemType file_system_type =
175 ppapi::PepperFileSystemTypeToFileSystemType(type_);
176 if (file_system_type == fileapi::kFileSystemTypeUnknown)
177 return PP_ERROR_FAILED;
179 int render_process_id = 0;
180 int unused;
181 if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(pp_instance(),
182 &render_process_id,
183 &unused)) {
184 return PP_ERROR_FAILED;
187 BrowserThread::PostTaskAndReplyWithResult(
188 BrowserThread::UI,
189 FROM_HERE,
190 base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
191 base::Bind(&PepperFileSystemBrowserHost::OpenFileSystem,
192 weak_factory_.GetWeakPtr(),
193 context->MakeReplyMessageContext(),
194 file_system_type));
195 return PP_OK_COMPLETIONPENDING;
198 void PepperFileSystemBrowserHost::OpenExistingFileSystem(
199 const base::Closure& callback,
200 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
201 if (file_system_context.get()) {
202 opened_ = true;
203 } else {
204 // If there is no file system context, we log a warning and continue with an
205 // invalid resource (which will produce errors when used), since we have no
206 // way to communicate the error to the caller.
207 LOG(WARNING) << "Could not retrieve file system context.";
209 SetFileSystemContext(file_system_context);
211 if (ShouldCreateQuotaReservation())
212 CreateQuotaReservation(callback);
213 else
214 callback.Run();
217 void PepperFileSystemBrowserHost::OpenFileSystem(
218 ppapi::host::ReplyMessageContext reply_context,
219 fileapi::FileSystemType file_system_type,
220 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
221 if (!file_system_context.get()) {
222 OpenFileSystemComplete(
223 reply_context, GURL(), std::string(), base::PLATFORM_FILE_ERROR_FAILED);
224 return;
227 SetFileSystemContext(file_system_context);
229 GURL origin = browser_ppapi_host_->GetDocumentURLForInstance(
230 pp_instance()).GetOrigin();
231 file_system_context_->OpenFileSystem(origin, file_system_type,
232 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
233 base::Bind(&PepperFileSystemBrowserHost::OpenFileSystemComplete,
234 weak_factory_.GetWeakPtr(),
235 reply_context));
238 void PepperFileSystemBrowserHost::OpenFileSystemComplete(
239 ppapi::host::ReplyMessageContext reply_context,
240 const GURL& root,
241 const std::string& /* unused */,
242 base::PlatformFileError error) {
243 int32 pp_error = ppapi::PlatformFileErrorToPepperError(error);
244 if (pp_error == PP_OK) {
245 opened_ = true;
246 root_url_ = root;
248 if (ShouldCreateQuotaReservation()) {
249 CreateQuotaReservation(
250 base::Bind(&PepperFileSystemBrowserHost::SendReplyForFileSystem,
251 weak_factory_.GetWeakPtr(),
252 reply_context,
253 static_cast<int32_t>(PP_OK)));
254 return;
257 SendReplyForFileSystem(reply_context, pp_error);
260 void PepperFileSystemBrowserHost::OpenIsolatedFileSystem(
261 ppapi::host::ReplyMessageContext reply_context,
262 const std::string& fsid,
263 PP_IsolatedFileSystemType_Private type,
264 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
265 if (!file_system_context.get()) {
266 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
267 return;
269 SetFileSystemContext(file_system_context);
271 root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString(
272 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
273 fsid, ppapi::IsolatedFileSystemTypeToRootName(type)));
274 if (!root_url_.is_valid()) {
275 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
276 return;
279 switch (type) {
280 case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_CRX:
281 opened_ = true;
282 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_OK);
283 return;
284 case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE:
285 OpenPluginPrivateFileSystem(reply_context, fsid, file_system_context_);
286 return;
287 default:
288 NOTREACHED();
289 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_BADARGUMENT);
290 return;
294 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystem(
295 ppapi::host::ReplyMessageContext reply_context,
296 const std::string& fsid,
297 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
298 GURL origin = browser_ppapi_host_->GetDocumentURLForInstance(
299 pp_instance()).GetOrigin();
300 if (!origin.is_valid()) {
301 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
302 return;
305 const std::string& plugin_id = GeneratePluginId(GetPluginMimeType());
306 if (plugin_id.empty()) {
307 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_BADARGUMENT);
308 return;
311 file_system_context->OpenPluginPrivateFileSystem(
312 origin, fileapi::kFileSystemTypePluginPrivate, fsid, plugin_id,
313 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
314 base::Bind(
315 &PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete,
316 weak_factory_.GetWeakPtr(), reply_context, fsid));
319 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete(
320 ppapi::host::ReplyMessageContext reply_context,
321 const std::string& fsid,
322 base::PlatformFileError error) {
323 int32 pp_error = ppapi::PlatformFileErrorToPepperError(error);
324 if (pp_error == PP_OK)
325 opened_ = true;
326 SendReplyForIsolatedFileSystem(reply_context, fsid, pp_error);
329 int32_t PepperFileSystemBrowserHost::OnHostMsgInitIsolatedFileSystem(
330 ppapi::host::HostMessageContext* context,
331 const std::string& fsid,
332 PP_IsolatedFileSystemType_Private type) {
333 // Do not allow multiple opens.
334 if (called_open_)
335 return PP_ERROR_INPROGRESS;
336 called_open_ = true;
338 // Do a sanity check.
339 if (!fileapi::ValidateIsolatedFileSystemId(fsid))
340 return PP_ERROR_BADARGUMENT;
342 int render_process_id = 0;
343 int unused;
344 if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(pp_instance(),
345 &render_process_id,
346 &unused)) {
347 fileapi::IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
348 return PP_ERROR_FAILED;
351 root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString(
352 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
353 fsid, ppapi::IsolatedFileSystemTypeToRootName(type)));
355 BrowserThread::PostTaskAndReplyWithResult(
356 BrowserThread::UI,
357 FROM_HERE,
358 base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
359 base::Bind(&PepperFileSystemBrowserHost::OpenIsolatedFileSystem,
360 weak_factory_.GetWeakPtr(),
361 context->MakeReplyMessageContext(), fsid, type));
362 return PP_OK_COMPLETIONPENDING;
365 int32_t PepperFileSystemBrowserHost::OnHostMsgReserveQuota(
366 ppapi::host::HostMessageContext* context,
367 int64_t amount,
368 const std::map<int32_t, int64_t>& max_written_offsets) {
369 DCHECK(ChecksQuota());
370 DCHECK(amount > 0);
372 if (reserving_quota_)
373 return PP_ERROR_INPROGRESS;
374 reserving_quota_ = true;
376 int64_t reservation_amount = std::max<int64_t>(kMinimumQuotaReservationSize,
377 amount);
378 file_system_context_->default_file_task_runner()->PostTask(
379 FROM_HERE,
380 base::Bind(&QuotaReservation::ReserveQuota,
381 quota_reservation_,
382 reservation_amount,
383 max_written_offsets,
384 base::Bind(&PepperFileSystemBrowserHost::GotReservedQuota,
385 weak_factory_.GetWeakPtr(),
386 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 fileapi::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<fileapi::FileSystemContext> file_system_context) {
412 file_system_context_ = file_system_context;
413 if (type_ != PP_FILESYSTEMTYPE_EXTERNAL) {
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 quota::QuotaManagerProxy* quota_manager_proxy =
426 file_system_context_->quota_manager_proxy();
427 CHECK(quota_manager_proxy);
428 CHECK(quota_manager_proxy->quota_manager());
429 fileapi::FileSystemType file_system_type =
430 ppapi::PepperFileSystemTypeToFileSystemType(type_);
431 return !quota_manager_proxy->quota_manager()->IsStorageUnlimited(
432 root_url_.GetOrigin(),
433 fileapi::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 ppapi::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 QuotaReservation::OffsetMap& max_written_offsets) {
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, max_written_offsets));
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 if (!net::IsMimeType(mime_type))
489 return std::string();
490 std::string output = mime_type;
492 // Replace a slash used for type/subtype separator with an underscore.
493 // NOTE: This assumes there is only one slash in the MIME type.
494 ReplaceFirstSubstringAfterOffset(&output, 0, "/", "_");
496 // Verify |output| contains only alphabets, digits, or "._-".
497 for (std::string::const_iterator it = output.begin();
498 it != output.end(); ++it) {
499 if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) &&
500 *it != '.' && *it != '_' && *it != '-') {
501 LOG(WARNING) << "Failed to generate a plugin id.";
502 return std::string();
505 return output;
508 } // namespace content