Ignore non-active fullscreen windows for shelf state.
[chromium-blink-merge.git] / content / browser / renderer_host / pepper / pepper_file_system_browser_host.cc
blob94b3263a6293e5c7d8535173ed1050724b5215ac
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/common/fileapi/file_system_util.h"
28 namespace content {
30 namespace {
32 // This is the minimum amount of quota we reserve per file system.
33 const int64_t kMinimumQuotaReservationSize = 1024 * 1024; // 1 MB
35 scoped_refptr<fileapi::FileSystemContext>
36 GetFileSystemContextFromRenderId(int render_process_id) {
37 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
38 RenderProcessHost* host = RenderProcessHost::FromID(render_process_id);
39 if (!host)
40 return NULL;
41 StoragePartition* storage_partition = host->GetStoragePartition();
42 if (!storage_partition)
43 return NULL;
44 return storage_partition->GetFileSystemContext();
47 } // namespace
49 PepperFileSystemBrowserHost::QuotaRequest::QuotaRequest(
50 int32_t amount_arg,
51 const RequestQuotaCallback& callback_arg)
52 : amount(amount_arg),
53 callback(callback_arg) {
56 PepperFileSystemBrowserHost::QuotaRequest::~QuotaRequest() {
59 PepperFileSystemBrowserHost::PepperFileSystemBrowserHost(BrowserPpapiHost* host,
60 PP_Instance instance,
61 PP_Resource resource,
62 PP_FileSystemType type)
63 : ResourceHost(host->GetPpapiHost(), instance, resource),
64 browser_ppapi_host_(host),
65 type_(type),
66 called_open_(false),
67 opened_(false),
68 file_system_context_(NULL),
69 reserved_quota_(0),
70 reserving_quota_(false),
71 weak_factory_(this) {
74 PepperFileSystemBrowserHost::~PepperFileSystemBrowserHost() {
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_->GetRenderViewIDsForInstance(
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 IPC_END_MESSAGE_MAP()
112 return PP_ERROR_FAILED;
115 bool PepperFileSystemBrowserHost::IsFileSystemHost() {
116 return true;
119 void PepperFileSystemBrowserHost::OpenQuotaFile(
120 PepperFileIOHost* file_io_host,
121 const base::FilePath& file_path,
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,
131 quota_reservation_,
133 file_path),
134 callback);
135 } else {
136 NOTREACHED();
140 void PepperFileSystemBrowserHost::CloseQuotaFile(
141 PepperFileIOHost* file_io_host) {
142 int32_t id = file_io_host->pp_resource();
143 int64_t max_written_offset = 0;
144 FileMap::iterator it = files_.find(id);
145 if (it != files_.end()) {
146 max_written_offset = file_io_host->max_written_offset();
147 files_.erase(it);
148 } else {
149 NOTREACHED();
150 return;
153 file_system_context_->default_file_task_runner()->PostTask(
154 FROM_HERE,
155 base::Bind(&QuotaReservation::CloseFile,
156 quota_reservation_,
158 max_written_offset));
161 int32_t PepperFileSystemBrowserHost::RequestQuota(
162 int32_t amount,
163 const RequestQuotaCallback& callback) {
164 DCHECK(amount >= 0);
165 if (!reserving_quota_ && reserved_quota_ >= amount) {
166 reserved_quota_ -= amount;
167 return amount;
170 // Queue up a pending quota request.
171 pending_quota_requests_.push(QuotaRequest(amount, callback));
173 // Reserve more quota if we haven't already.
174 if (!reserving_quota_)
175 ReserveQuota(amount);
177 return PP_OK_COMPLETIONPENDING;
180 int32_t PepperFileSystemBrowserHost::OnHostMsgOpen(
181 ppapi::host::HostMessageContext* context,
182 int64_t /* unused */) {
183 // TODO(raymes): The file system size is now unused by FileSystemDispatcher.
184 // Figure out why. Why is the file system size signed?
186 // Not allow multiple opens.
187 if (called_open_)
188 return PP_ERROR_INPROGRESS;
189 called_open_ = true;
191 fileapi::FileSystemType file_system_type =
192 ppapi::PepperFileSystemTypeToFileSystemType(type_);
193 if (file_system_type == fileapi::kFileSystemTypeUnknown)
194 return PP_ERROR_FAILED;
196 int render_process_id = 0;
197 int unused;
198 if (!browser_ppapi_host_->GetRenderViewIDsForInstance(pp_instance(),
199 &render_process_id,
200 &unused)) {
201 return PP_ERROR_FAILED;
204 BrowserThread::PostTaskAndReplyWithResult(
205 BrowserThread::UI,
206 FROM_HERE,
207 base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
208 base::Bind(&PepperFileSystemBrowserHost::OpenFileSystem,
209 weak_factory_.GetWeakPtr(),
210 context->MakeReplyMessageContext(),
211 file_system_type));
212 return PP_OK_COMPLETIONPENDING;
215 void PepperFileSystemBrowserHost::OpenExistingFileSystem(
216 const base::Closure& callback,
217 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
218 if (file_system_context.get()) {
219 opened_ = true;
220 } else {
221 // If there is no file system context, we log a warning and continue with an
222 // invalid resource (which will produce errors when used), since we have no
223 // way to communicate the error to the caller.
224 LOG(WARNING) << "Could not retrieve file system context.";
226 SetFileSystemContext(file_system_context);
228 int32_t pp_error = CreateQuotaReservation(callback);
229 if (pp_error == PP_OK_COMPLETIONPENDING)
230 return;
232 callback.Run();
235 void PepperFileSystemBrowserHost::OpenFileSystem(
236 ppapi::host::ReplyMessageContext reply_context,
237 fileapi::FileSystemType file_system_type,
238 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
239 if (!file_system_context.get()) {
240 OpenFileSystemComplete(
241 reply_context, GURL(), std::string(), base::PLATFORM_FILE_ERROR_FAILED);
242 return;
245 SetFileSystemContext(file_system_context);
247 GURL origin = browser_ppapi_host_->GetDocumentURLForInstance(
248 pp_instance()).GetOrigin();
249 file_system_context_->OpenFileSystem(origin, file_system_type,
250 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
251 base::Bind(&PepperFileSystemBrowserHost::OpenFileSystemComplete,
252 weak_factory_.GetWeakPtr(),
253 reply_context));
256 void PepperFileSystemBrowserHost::OpenFileSystemComplete(
257 ppapi::host::ReplyMessageContext reply_context,
258 const GURL& root,
259 const std::string& /* unused */,
260 base::PlatformFileError error) {
261 int32 pp_error = ppapi::PlatformFileErrorToPepperError(error);
262 if (pp_error == PP_OK) {
263 opened_ = true;
264 root_url_ = root;
266 pp_error = CreateQuotaReservation(
267 base::Bind(&PepperFileSystemBrowserHost::SendReplyForFileSystem,
268 weak_factory_.GetWeakPtr(),
269 reply_context,
270 static_cast<int32_t>(PP_OK)));
271 if (pp_error == PP_OK_COMPLETIONPENDING)
272 return;
273 // For PP_OK and all other error codes, we can send the reply now.
275 SendReplyForFileSystem(reply_context, pp_error);
278 void PepperFileSystemBrowserHost::OpenIsolatedFileSystem(
279 ppapi::host::ReplyMessageContext reply_context,
280 const std::string& fsid,
281 PP_IsolatedFileSystemType_Private type,
282 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
283 if (!file_system_context.get()) {
284 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
285 return;
287 SetFileSystemContext(file_system_context);
289 root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString(
290 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
291 fsid, ppapi::IsolatedFileSystemTypeToRootName(type)));
292 if (!root_url_.is_valid()) {
293 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
294 return;
297 switch (type) {
298 case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_CRX:
299 opened_ = true;
300 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_OK);
301 return;
302 case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE:
303 OpenPluginPrivateFileSystem(reply_context, fsid, file_system_context_);
304 return;
305 default:
306 NOTREACHED();
307 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_BADARGUMENT);
308 return;
312 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystem(
313 ppapi::host::ReplyMessageContext reply_context,
314 const std::string& fsid,
315 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
316 GURL origin = browser_ppapi_host_->GetDocumentURLForInstance(
317 pp_instance()).GetOrigin();
318 if (!origin.is_valid()) {
319 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
320 return;
323 const std::string& plugin_id = GeneratePluginId(GetPluginMimeType());
324 if (plugin_id.empty()) {
325 SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_BADARGUMENT);
326 return;
329 file_system_context->OpenPluginPrivateFileSystem(
330 origin, fileapi::kFileSystemTypePluginPrivate, fsid, plugin_id,
331 fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
332 base::Bind(
333 &PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete,
334 weak_factory_.GetWeakPtr(), reply_context, fsid));
337 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete(
338 ppapi::host::ReplyMessageContext reply_context,
339 const std::string& fsid,
340 base::PlatformFileError error) {
341 int32 pp_error = ppapi::PlatformFileErrorToPepperError(error);
342 if (pp_error == PP_OK)
343 opened_ = true;
344 SendReplyForIsolatedFileSystem(reply_context, fsid, pp_error);
347 int32_t PepperFileSystemBrowserHost::OnHostMsgInitIsolatedFileSystem(
348 ppapi::host::HostMessageContext* context,
349 const std::string& fsid,
350 PP_IsolatedFileSystemType_Private type) {
351 // Do not allow multiple opens.
352 if (called_open_)
353 return PP_ERROR_INPROGRESS;
354 called_open_ = true;
356 // Do a sanity check.
357 if (!fileapi::ValidateIsolatedFileSystemId(fsid))
358 return PP_ERROR_BADARGUMENT;
360 int render_process_id = 0;
361 int unused;
362 if (!browser_ppapi_host_->GetRenderViewIDsForInstance(pp_instance(),
363 &render_process_id,
364 &unused)) {
365 fileapi::IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
366 return PP_ERROR_FAILED;
369 root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString(
370 browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
371 fsid, ppapi::IsolatedFileSystemTypeToRootName(type)));
373 BrowserThread::PostTaskAndReplyWithResult(
374 BrowserThread::UI,
375 FROM_HERE,
376 base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
377 base::Bind(&PepperFileSystemBrowserHost::OpenIsolatedFileSystem,
378 weak_factory_.GetWeakPtr(),
379 context->MakeReplyMessageContext(), fsid, type));
380 return PP_OK_COMPLETIONPENDING;
383 void PepperFileSystemBrowserHost::SendReplyForFileSystem(
384 ppapi::host::ReplyMessageContext reply_context,
385 int32_t pp_error) {
386 reply_context.params.set_result(pp_error);
387 host()->SendReply(reply_context, PpapiPluginMsg_FileSystem_OpenReply());
390 void PepperFileSystemBrowserHost::SendReplyForIsolatedFileSystem(
391 ppapi::host::ReplyMessageContext reply_context,
392 const std::string& fsid,
393 int32_t error) {
394 if (error != PP_OK)
395 fileapi::IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
396 reply_context.params.set_result(error);
397 host()->SendReply(reply_context,
398 PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply());
401 void PepperFileSystemBrowserHost::SetFileSystemContext(
402 scoped_refptr<fileapi::FileSystemContext> file_system_context) {
403 file_system_context_ = file_system_context;
404 if (type_ != PP_FILESYSTEMTYPE_EXTERNAL) {
405 file_system_operation_runner_ =
406 file_system_context_->CreateFileSystemOperationRunner();
410 int32_t PepperFileSystemBrowserHost::CreateQuotaReservation(
411 const base::Closure& callback) {
412 if (!ppapi::FileSystemTypeHasQuota(type_))
413 return PP_OK;
415 DCHECK(root_url_.is_valid());
416 base::PostTaskAndReplyWithResult(
417 file_system_context_->default_file_task_runner(),
418 FROM_HERE,
419 base::Bind(&QuotaReservation::Create,
420 file_system_context_,
421 root_url_.GetOrigin(),
422 ppapi::PepperFileSystemTypeToFileSystemType(type_)),
423 base::Bind(&PepperFileSystemBrowserHost::GotQuotaReservation,
424 weak_factory_.GetWeakPtr(),
425 callback));
426 return PP_OK_COMPLETIONPENDING;
429 void PepperFileSystemBrowserHost::GotQuotaReservation(
430 const base::Closure& callback,
431 scoped_refptr<QuotaReservation> quota_reservation) {
432 quota_reservation_ = quota_reservation;
433 callback.Run();
436 void PepperFileSystemBrowserHost::ReserveQuota(int32_t amount) {
437 DCHECK(!reserving_quota_);
438 reserving_quota_ = true;
440 // Get the max_written_offset for each open file.
441 QuotaReservation::OffsetMap max_written_offsets;
442 for (FileMap::iterator it = files_.begin(); it != files_.end(); ++ it) {
443 max_written_offsets.insert(
444 std::make_pair(it->first, it->second->max_written_offset()));
447 int64_t reservation_amount = std::max<int64_t>(kMinimumQuotaReservationSize,
448 amount);
449 file_system_context_->default_file_task_runner()->PostTask(
450 FROM_HERE,
451 base::Bind(&QuotaReservation::ReserveQuota,
452 quota_reservation_,
453 reservation_amount,
454 max_written_offsets,
455 base::Bind(&PepperFileSystemBrowserHost::GotReservedQuota,
456 weak_factory_.GetWeakPtr())));
459 void PepperFileSystemBrowserHost::GotReservedQuota(
460 int64_t amount,
461 const QuotaReservation::OffsetMap& max_written_offsets) {
462 DCHECK(reserving_quota_);
463 reserving_quota_ = false;
464 reserved_quota_ = amount;
466 // Update open files with their new base sizes. This won't write over any
467 // updates since the files are waiting for quota and can't write.
468 for (FileMap::iterator it = files_.begin(); it != files_.end(); ++ it) {
469 QuotaReservation::OffsetMap::const_iterator offset_it =
470 max_written_offsets.find(it->first);
471 if (offset_it != max_written_offsets.end())
472 it->second->set_max_written_offset(offset_it->second);
473 else
474 NOTREACHED();
477 DCHECK(!pending_quota_requests_.empty());
478 // If we can't grant the first request after refreshing reserved_quota_, then
479 // fail all pending quota requests to avoid an infinite refresh/fail loop.
480 bool fail_all = reserved_quota_ < pending_quota_requests_.front().amount;
481 while (!pending_quota_requests_.empty()) {
482 QuotaRequest& request = pending_quota_requests_.front();
483 if (fail_all) {
484 request.callback.Run(0);
485 pending_quota_requests_.pop();
486 } else if (reserved_quota_ >= request.amount) {
487 reserved_quota_ -= request.amount;
488 request.callback.Run(request.amount);
489 pending_quota_requests_.pop();
490 } else {
491 // Refresh the quota reservation for the first pending request that we
492 // can't satisfy.
493 ReserveQuota(request.amount);
494 break;
499 std::string PepperFileSystemBrowserHost::GetPluginMimeType() const {
500 base::FilePath plugin_path = browser_ppapi_host_->GetPluginPath();
501 PepperPluginInfo* info =
502 PluginService::GetInstance()->GetRegisteredPpapiPluginInfo(plugin_path);
503 if (!info || info->mime_types.empty())
504 return std::string();
505 // Use the first element in |info->mime_types| even if several elements exist.
506 return info->mime_types[0].mime_type;
509 std::string PepperFileSystemBrowserHost::GeneratePluginId(
510 const std::string& mime_type) const {
511 // TODO(nhiroki): This function is very specialized for specific plugins (MIME
512 // types). If we bring this API to stable, we might have to make it more
513 // general.
515 if (!net::IsMimeType(mime_type))
516 return std::string();
517 std::string output = mime_type;
519 // Replace a slash used for type/subtype separator with an underscore.
520 // NOTE: This assumes there is only one slash in the MIME type.
521 ReplaceFirstSubstringAfterOffset(&output, 0, "/", "_");
523 // Verify |output| contains only alphabets, digits, or "._-".
524 for (std::string::const_iterator it = output.begin();
525 it != output.end(); ++it) {
526 if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) &&
527 *it != '.' && *it != '_' && *it != '-') {
528 LOG(WARNING) << "Failed to generate a plugin id.";
529 return std::string();
532 return output;
535 } // namespace content