Home | History | Annotate | Download | only in proxy
      1 // Copyright (c) 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.
      4 
      5 #include "ppapi/proxy/file_system_resource.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/stl_util.h"
      9 #include "ipc/ipc_message.h"
     10 #include "ppapi/c/pp_errors.h"
     11 #include "ppapi/proxy/ppapi_messages.h"
     12 #include "ppapi/shared_impl/file_growth.h"
     13 #include "ppapi/shared_impl/tracked_callback.h"
     14 #include "ppapi/thunk/enter.h"
     15 #include "ppapi/thunk/ppb_file_io_api.h"
     16 
     17 using ppapi::thunk::EnterResourceNoLock;
     18 using ppapi::thunk::PPB_FileIO_API;
     19 using ppapi::thunk::PPB_FileSystem_API;
     20 
     21 namespace ppapi {
     22 namespace proxy {
     23 
     24 FileSystemResource::QuotaRequest::QuotaRequest(
     25     int64_t amount_arg,
     26     const RequestQuotaCallback& callback_arg)
     27     : amount(amount_arg),
     28       callback(callback_arg) {
     29 }
     30 
     31 FileSystemResource::QuotaRequest::~QuotaRequest() {
     32 }
     33 
     34 FileSystemResource::FileSystemResource(Connection connection,
     35                                        PP_Instance instance,
     36                                        PP_FileSystemType type)
     37     : PluginResource(connection, instance),
     38       type_(type),
     39       called_open_(false),
     40       callback_count_(0),
     41       callback_result_(PP_OK),
     42       reserved_quota_(0),
     43       reserving_quota_(false) {
     44   DCHECK(type_ != PP_FILESYSTEMTYPE_INVALID);
     45   SendCreate(RENDERER, PpapiHostMsg_FileSystem_Create(type_));
     46   SendCreate(BROWSER, PpapiHostMsg_FileSystem_Create(type_));
     47 }
     48 
     49 FileSystemResource::FileSystemResource(Connection connection,
     50                                        PP_Instance instance,
     51                                        int pending_renderer_id,
     52                                        int pending_browser_id,
     53                                        PP_FileSystemType type)
     54     : PluginResource(connection, instance),
     55       type_(type),
     56       called_open_(true),
     57       callback_count_(0),
     58       callback_result_(PP_OK),
     59       reserved_quota_(0),
     60       reserving_quota_(false) {
     61   DCHECK(type_ != PP_FILESYSTEMTYPE_INVALID);
     62   AttachToPendingHost(RENDERER, pending_renderer_id);
     63   AttachToPendingHost(BROWSER, pending_browser_id);
     64 }
     65 
     66 FileSystemResource::~FileSystemResource() {
     67 }
     68 
     69 PPB_FileSystem_API* FileSystemResource::AsPPB_FileSystem_API() {
     70   return this;
     71 }
     72 
     73 int32_t FileSystemResource::Open(int64_t expected_size,
     74                                  scoped_refptr<TrackedCallback> callback) {
     75   DCHECK(type_ != PP_FILESYSTEMTYPE_ISOLATED);
     76   if (called_open_)
     77     return PP_ERROR_FAILED;
     78   called_open_ = true;
     79 
     80   Call<PpapiPluginMsg_FileSystem_OpenReply>(RENDERER,
     81       PpapiHostMsg_FileSystem_Open(expected_size),
     82       base::Bind(&FileSystemResource::OpenComplete,
     83                  this,
     84                  callback));
     85   Call<PpapiPluginMsg_FileSystem_OpenReply>(BROWSER,
     86       PpapiHostMsg_FileSystem_Open(expected_size),
     87       base::Bind(&FileSystemResource::OpenComplete,
     88                  this,
     89                  callback));
     90   return PP_OK_COMPLETIONPENDING;
     91 }
     92 
     93 PP_FileSystemType FileSystemResource::GetType() {
     94   return type_;
     95 }
     96 
     97 void FileSystemResource::OpenQuotaFile(PP_Resource file_io) {
     98   DCHECK(!ContainsKey(files_, file_io));
     99   files_.insert(file_io);
    100 }
    101 
    102 void FileSystemResource::CloseQuotaFile(PP_Resource file_io) {
    103   DCHECK(ContainsKey(files_, file_io));
    104   files_.erase(file_io);
    105 }
    106 
    107 int64_t FileSystemResource::RequestQuota(
    108     int64_t amount,
    109     const RequestQuotaCallback& callback) {
    110   DCHECK(amount >= 0);
    111   if (!reserving_quota_ && reserved_quota_ >= amount) {
    112     reserved_quota_ -= amount;
    113     return amount;
    114   }
    115 
    116   // Queue up a pending quota request.
    117   pending_quota_requests_.push(QuotaRequest(amount, callback));
    118 
    119   // Reserve more quota if we haven't already.
    120   if (!reserving_quota_)
    121     ReserveQuota(amount);
    122 
    123   return PP_OK_COMPLETIONPENDING;
    124 }
    125 
    126 int32_t FileSystemResource::InitIsolatedFileSystem(
    127     const std::string& fsid,
    128     PP_IsolatedFileSystemType_Private type,
    129     const base::Callback<void(int32_t)>& callback) {
    130   // This call is mutually exclusive with Open() above, so we can reuse the
    131   // called_open state.
    132   DCHECK(type_ == PP_FILESYSTEMTYPE_ISOLATED);
    133   if (called_open_)
    134     return PP_ERROR_FAILED;
    135   called_open_ = true;
    136 
    137   Call<PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply>(RENDERER,
    138       PpapiHostMsg_FileSystem_InitIsolatedFileSystem(fsid, type),
    139       base::Bind(&FileSystemResource::InitIsolatedFileSystemComplete,
    140       this,
    141       callback));
    142   Call<PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply>(BROWSER,
    143       PpapiHostMsg_FileSystem_InitIsolatedFileSystem(fsid, type),
    144       base::Bind(&FileSystemResource::InitIsolatedFileSystemComplete,
    145       this,
    146       callback));
    147   return PP_OK_COMPLETIONPENDING;
    148 }
    149 
    150 void FileSystemResource::OpenComplete(
    151     scoped_refptr<TrackedCallback> callback,
    152     const ResourceMessageReplyParams& params) {
    153   ++callback_count_;
    154   // Prioritize worse result since only one status can be returned.
    155   if (params.result() != PP_OK)
    156     callback_result_ = params.result();
    157   // Received callback from browser and renderer.
    158   if (callback_count_ == 2)
    159     callback->Run(callback_result_);
    160 }
    161 
    162 void FileSystemResource::InitIsolatedFileSystemComplete(
    163     const base::Callback<void(int32_t)>& callback,
    164     const ResourceMessageReplyParams& params) {
    165   ++callback_count_;
    166   // Prioritize worse result since only one status can be returned.
    167   if (params.result() != PP_OK)
    168     callback_result_ = params.result();
    169   // Received callback from browser and renderer.
    170   if (callback_count_ == 2)
    171     callback.Run(callback_result_);
    172 }
    173 
    174 void FileSystemResource::ReserveQuota(int64_t amount) {
    175   DCHECK(!reserving_quota_);
    176   reserving_quota_ = true;
    177 
    178   FileGrowthMap file_growths;
    179   for (std::set<PP_Resource>::iterator it = files_.begin();
    180        it != files_.end(); ++it) {
    181     EnterResourceNoLock<PPB_FileIO_API> enter(*it, true);
    182     if (enter.failed()) {
    183       NOTREACHED();
    184       continue;
    185     }
    186     PPB_FileIO_API* file_io_api = enter.object();
    187     file_growths[*it] = FileGrowth(
    188         file_io_api->GetMaxWrittenOffset(),
    189         file_io_api->GetAppendModeWriteAmount());
    190   }
    191   Call<PpapiPluginMsg_FileSystem_ReserveQuotaReply>(BROWSER,
    192       PpapiHostMsg_FileSystem_ReserveQuota(amount, file_growths),
    193       base::Bind(&FileSystemResource::ReserveQuotaComplete,
    194                  this));
    195 }
    196 
    197 void FileSystemResource::ReserveQuotaComplete(
    198     const ResourceMessageReplyParams& params,
    199     int64_t amount,
    200     const FileSizeMap& file_sizes) {
    201   DCHECK(reserving_quota_);
    202   reserving_quota_ = false;
    203   reserved_quota_ = amount;
    204 
    205   for (FileSizeMap::const_iterator it = file_sizes.begin();
    206        it != file_sizes.end(); ++it) {
    207     EnterResourceNoLock<PPB_FileIO_API> enter(it->first, true);
    208 
    209     // It is possible that the host has sent an offset for a file that has been
    210     // destroyed in the plugin. Ignore it.
    211     if (enter.failed())
    212       continue;
    213     PPB_FileIO_API* file_io_api = enter.object();
    214     file_io_api->SetMaxWrittenOffset(it->second);
    215     file_io_api->SetAppendModeWriteAmount(0);
    216   }
    217 
    218   DCHECK(!pending_quota_requests_.empty());
    219   // If we can't grant the first request after refreshing reserved_quota_, then
    220   // fail all pending quota requests to avoid an infinite refresh/fail loop.
    221   bool fail_all = reserved_quota_ < pending_quota_requests_.front().amount;
    222   while (!pending_quota_requests_.empty()) {
    223     QuotaRequest& request = pending_quota_requests_.front();
    224     if (fail_all) {
    225       request.callback.Run(0);
    226       pending_quota_requests_.pop();
    227     } else if (reserved_quota_ >= request.amount) {
    228       reserved_quota_ -= request.amount;
    229       request.callback.Run(request.amount);
    230       pending_quota_requests_.pop();
    231     } else {
    232       // Refresh the quota reservation for the first pending request that we
    233       // can't satisfy.
    234       ReserveQuota(request.amount);
    235       break;
    236     }
    237   }
    238 }
    239 
    240 }  // namespace proxy
    241 }  // namespace ppapi
    242