Home | History | Annotate | Download | only in pepper
      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.
      4 
      5 #include "content/browser/renderer_host/pepper/pepper_file_system_browser_host.h"
      6 
      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_proxy.h"
     27 #include "webkit/common/fileapi/file_system_util.h"
     28 #include "webkit/common/quota/quota_types.h"
     29 
     30 namespace content {
     31 
     32 namespace {
     33 
     34 // This is the minimum amount of quota we reserve per file system.
     35 const int64_t kMinimumQuotaReservationSize = 1024 * 1024;  // 1 MB
     36 
     37 scoped_refptr<fileapi::FileSystemContext> GetFileSystemContextFromRenderId(
     38     int render_process_id) {
     39   DCHECK_CURRENTLY_ON(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();
     47 }
     48 
     49 }  // namespace
     50 
     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) {}
     64 
     65 PepperFileSystemBrowserHost::~PepperFileSystemBrowserHost() {
     66   // If |files_| is not empty, the plugin failed to close some files. It must
     67   // have crashed.
     68   if (!files_.empty()) {
     69     file_system_context_->default_file_task_runner()->PostTask(
     70         FROM_HERE,
     71         base::Bind(&QuotaReservation::OnClientCrash, quota_reservation_));
     72   }
     73 
     74   // All FileRefs and FileIOs that reference us must have been destroyed. Cancel
     75   // all pending file system operations.
     76   if (file_system_operation_runner_)
     77     file_system_operation_runner_->Shutdown();
     78 }
     79 
     80 void PepperFileSystemBrowserHost::OpenExisting(const GURL& root_url,
     81                                                const base::Closure& callback) {
     82   root_url_ = root_url;
     83   int render_process_id = 0;
     84   int unused;
     85   if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(
     86           pp_instance(), &render_process_id, &unused)) {
     87     NOTREACHED();
     88   }
     89   called_open_ = true;
     90   // Get the file system context asynchronously, and then complete the Open
     91   // operation by calling |callback|.
     92   BrowserThread::PostTaskAndReplyWithResult(
     93       BrowserThread::UI,
     94       FROM_HERE,
     95       base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
     96       base::Bind(&PepperFileSystemBrowserHost::OpenExistingFileSystem,
     97                  weak_factory_.GetWeakPtr(),
     98                  callback));
     99 }
    100 
    101 int32_t PepperFileSystemBrowserHost::OnResourceMessageReceived(
    102     const IPC::Message& msg,
    103     ppapi::host::HostMessageContext* context) {
    104   PPAPI_BEGIN_MESSAGE_MAP(PepperFileSystemBrowserHost, msg)
    105     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileSystem_Open,
    106                                       OnHostMsgOpen)
    107     PPAPI_DISPATCH_HOST_RESOURCE_CALL(
    108         PpapiHostMsg_FileSystem_InitIsolatedFileSystem,
    109         OnHostMsgInitIsolatedFileSystem)
    110     PPAPI_DISPATCH_HOST_RESOURCE_CALL(PpapiHostMsg_FileSystem_ReserveQuota,
    111                                       OnHostMsgReserveQuota)
    112   PPAPI_END_MESSAGE_MAP()
    113   return PP_ERROR_FAILED;
    114 }
    115 
    116 bool PepperFileSystemBrowserHost::IsFileSystemHost() { return true; }
    117 
    118 void PepperFileSystemBrowserHost::OpenQuotaFile(
    119     PepperFileIOHost* file_io_host,
    120     const fileapi::FileSystemURL& url,
    121     const OpenQuotaFileCallback& callback) {
    122   int32_t id = file_io_host->pp_resource();
    123   std::pair<FileMap::iterator, bool> insert_result =
    124       files_.insert(std::make_pair(id, file_io_host));
    125   if (insert_result.second) {
    126     base::PostTaskAndReplyWithResult(
    127         file_system_context_->default_file_task_runner(),
    128         FROM_HERE,
    129         base::Bind(&QuotaReservation::OpenFile, quota_reservation_, id, url),
    130         callback);
    131   } else {
    132     NOTREACHED();
    133   }
    134 }
    135 
    136 void PepperFileSystemBrowserHost::CloseQuotaFile(
    137     PepperFileIOHost* file_io_host,
    138     const ppapi::FileGrowth& file_growth) {
    139   int32_t id = file_io_host->pp_resource();
    140   FileMap::iterator it = files_.find(id);
    141   if (it != files_.end()) {
    142     files_.erase(it);
    143   } else {
    144     NOTREACHED();
    145     return;
    146   }
    147 
    148   file_system_context_->default_file_task_runner()->PostTask(
    149       FROM_HERE,
    150       base::Bind(
    151           &QuotaReservation::CloseFile, quota_reservation_, id, file_growth));
    152 }
    153 
    154 int32_t PepperFileSystemBrowserHost::OnHostMsgOpen(
    155     ppapi::host::HostMessageContext* context,
    156     int64_t /* unused */) {
    157   // TODO(raymes): The file system size is now unused by FileSystemDispatcher.
    158   // Figure out why. Why is the file system size signed?
    159 
    160   // Not allow multiple opens.
    161   if (called_open_)
    162     return PP_ERROR_INPROGRESS;
    163   called_open_ = true;
    164 
    165   fileapi::FileSystemType file_system_type =
    166       ppapi::PepperFileSystemTypeToFileSystemType(type_);
    167   if (file_system_type == fileapi::kFileSystemTypeUnknown)
    168     return PP_ERROR_FAILED;
    169 
    170   int render_process_id = 0;
    171   int unused;
    172   if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(
    173           pp_instance(), &render_process_id, &unused)) {
    174     return PP_ERROR_FAILED;
    175   }
    176 
    177   BrowserThread::PostTaskAndReplyWithResult(
    178       BrowserThread::UI,
    179       FROM_HERE,
    180       base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
    181       base::Bind(&PepperFileSystemBrowserHost::OpenFileSystem,
    182                  weak_factory_.GetWeakPtr(),
    183                  context->MakeReplyMessageContext(),
    184                  file_system_type));
    185   return PP_OK_COMPLETIONPENDING;
    186 }
    187 
    188 void PepperFileSystemBrowserHost::OpenExistingFileSystem(
    189     const base::Closure& callback,
    190     scoped_refptr<fileapi::FileSystemContext> file_system_context) {
    191   if (file_system_context.get()) {
    192     opened_ = true;
    193   } else {
    194     // If there is no file system context, we log a warning and continue with an
    195     // invalid resource (which will produce errors when used), since we have no
    196     // way to communicate the error to the caller.
    197     LOG(WARNING) << "Could not retrieve file system context.";
    198   }
    199   SetFileSystemContext(file_system_context);
    200 
    201   if (ShouldCreateQuotaReservation())
    202     CreateQuotaReservation(callback);
    203   else
    204     callback.Run();
    205 }
    206 
    207 void PepperFileSystemBrowserHost::OpenFileSystem(
    208     ppapi::host::ReplyMessageContext reply_context,
    209     fileapi::FileSystemType file_system_type,
    210     scoped_refptr<fileapi::FileSystemContext> file_system_context) {
    211   if (!file_system_context.get()) {
    212     OpenFileSystemComplete(
    213         reply_context, GURL(), std::string(), base::File::FILE_ERROR_FAILED);
    214     return;
    215   }
    216 
    217   SetFileSystemContext(file_system_context);
    218 
    219   GURL origin =
    220       browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin();
    221   file_system_context_->OpenFileSystem(
    222       origin,
    223       file_system_type,
    224       fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
    225       base::Bind(&PepperFileSystemBrowserHost::OpenFileSystemComplete,
    226                  weak_factory_.GetWeakPtr(),
    227                  reply_context));
    228 }
    229 
    230 void PepperFileSystemBrowserHost::OpenFileSystemComplete(
    231     ppapi::host::ReplyMessageContext reply_context,
    232     const GURL& root,
    233     const std::string& /* unused */,
    234     base::File::Error error) {
    235   int32 pp_error = ppapi::FileErrorToPepperError(error);
    236   if (pp_error == PP_OK) {
    237     opened_ = true;
    238     root_url_ = root;
    239 
    240     if (ShouldCreateQuotaReservation()) {
    241       CreateQuotaReservation(
    242           base::Bind(&PepperFileSystemBrowserHost::SendReplyForFileSystem,
    243                      weak_factory_.GetWeakPtr(),
    244                      reply_context,
    245                      static_cast<int32_t>(PP_OK)));
    246       return;
    247     }
    248   }
    249   SendReplyForFileSystem(reply_context, pp_error);
    250 }
    251 
    252 void PepperFileSystemBrowserHost::OpenIsolatedFileSystem(
    253     ppapi::host::ReplyMessageContext reply_context,
    254     const std::string& fsid,
    255     PP_IsolatedFileSystemType_Private type,
    256     scoped_refptr<fileapi::FileSystemContext> file_system_context) {
    257   if (!file_system_context.get()) {
    258     SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
    259     return;
    260   }
    261   SetFileSystemContext(file_system_context);
    262 
    263   root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString(
    264       browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
    265       fsid,
    266       ppapi::IsolatedFileSystemTypeToRootName(type)));
    267   if (!root_url_.is_valid()) {
    268     SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
    269     return;
    270   }
    271 
    272   switch (type) {
    273     case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_CRX:
    274       opened_ = true;
    275       SendReplyForIsolatedFileSystem(reply_context, fsid, PP_OK);
    276       return;
    277     case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE:
    278       OpenPluginPrivateFileSystem(reply_context, fsid, file_system_context_);
    279       return;
    280     default:
    281       NOTREACHED();
    282       SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_BADARGUMENT);
    283       return;
    284   }
    285 }
    286 
    287 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystem(
    288     ppapi::host::ReplyMessageContext reply_context,
    289     const std::string& fsid,
    290     scoped_refptr<fileapi::FileSystemContext> file_system_context) {
    291   GURL origin =
    292       browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin();
    293   if (!origin.is_valid()) {
    294     SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
    295     return;
    296   }
    297 
    298   const std::string& plugin_id = GeneratePluginId(GetPluginMimeType());
    299   if (plugin_id.empty()) {
    300     SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_BADARGUMENT);
    301     return;
    302   }
    303 
    304   file_system_context->OpenPluginPrivateFileSystem(
    305       origin,
    306       fileapi::kFileSystemTypePluginPrivate,
    307       fsid,
    308       plugin_id,
    309       fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
    310       base::Bind(
    311           &PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete,
    312           weak_factory_.GetWeakPtr(),
    313           reply_context,
    314           fsid));
    315 }
    316 
    317 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete(
    318     ppapi::host::ReplyMessageContext reply_context,
    319     const std::string& fsid,
    320     base::File::Error error) {
    321   int32 pp_error = ppapi::FileErrorToPepperError(error);
    322   if (pp_error == PP_OK)
    323     opened_ = true;
    324   SendReplyForIsolatedFileSystem(reply_context, fsid, pp_error);
    325 }
    326 
    327 int32_t PepperFileSystemBrowserHost::OnHostMsgInitIsolatedFileSystem(
    328     ppapi::host::HostMessageContext* context,
    329     const std::string& fsid,
    330     PP_IsolatedFileSystemType_Private type) {
    331   // Do not allow multiple opens.
    332   if (called_open_)
    333     return PP_ERROR_INPROGRESS;
    334   called_open_ = true;
    335 
    336   // Do a sanity check.
    337   if (!fileapi::ValidateIsolatedFileSystemId(fsid))
    338     return PP_ERROR_BADARGUMENT;
    339 
    340   int render_process_id = 0;
    341   int unused;
    342   if (!browser_ppapi_host_->GetRenderFrameIDsForInstance(
    343           pp_instance(), &render_process_id, &unused)) {
    344     fileapi::IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
    345     return PP_ERROR_FAILED;
    346   }
    347 
    348   root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString(
    349       browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
    350       fsid,
    351       ppapi::IsolatedFileSystemTypeToRootName(type)));
    352 
    353   BrowserThread::PostTaskAndReplyWithResult(
    354       BrowserThread::UI,
    355       FROM_HERE,
    356       base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
    357       base::Bind(&PepperFileSystemBrowserHost::OpenIsolatedFileSystem,
    358                  weak_factory_.GetWeakPtr(),
    359                  context->MakeReplyMessageContext(),
    360                  fsid,
    361                  type));
    362   return PP_OK_COMPLETIONPENDING;
    363 }
    364 
    365 int32_t PepperFileSystemBrowserHost::OnHostMsgReserveQuota(
    366     ppapi::host::HostMessageContext* context,
    367     int64_t amount,
    368     const ppapi::FileGrowthMap& file_growths) {
    369   DCHECK(ChecksQuota());
    370   DCHECK_GT(amount, 0);
    371 
    372   if (reserving_quota_)
    373     return PP_ERROR_INPROGRESS;
    374   reserving_quota_ = true;
    375 
    376   int64_t reservation_amount =
    377       std::max<int64_t>(kMinimumQuotaReservationSize, 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                  file_growths,
    384                  base::Bind(&PepperFileSystemBrowserHost::GotReservedQuota,
    385                             weak_factory_.GetWeakPtr(),
    386                             context->MakeReplyMessageContext())));
    387 
    388   return PP_OK_COMPLETIONPENDING;
    389 }
    390 
    391 void PepperFileSystemBrowserHost::SendReplyForFileSystem(
    392     ppapi::host::ReplyMessageContext reply_context,
    393     int32_t pp_error) {
    394   reply_context.params.set_result(pp_error);
    395   host()->SendReply(reply_context, PpapiPluginMsg_FileSystem_OpenReply());
    396 }
    397 
    398 void PepperFileSystemBrowserHost::SendReplyForIsolatedFileSystem(
    399     ppapi::host::ReplyMessageContext reply_context,
    400     const std::string& fsid,
    401     int32_t error) {
    402   if (error != PP_OK)
    403     fileapi::IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
    404   reply_context.params.set_result(error);
    405   host()->SendReply(reply_context,
    406                     PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply());
    407 }
    408 
    409 void PepperFileSystemBrowserHost::SetFileSystemContext(
    410     scoped_refptr<fileapi::FileSystemContext> file_system_context) {
    411   file_system_context_ = file_system_context;
    412   if (type_ != PP_FILESYSTEMTYPE_EXTERNAL || root_url_.is_valid()) {
    413     file_system_operation_runner_ =
    414         file_system_context_->CreateFileSystemOperationRunner();
    415   }
    416 }
    417 
    418 bool PepperFileSystemBrowserHost::ShouldCreateQuotaReservation() const {
    419   // Some file system types don't have quota.
    420   if (!ppapi::FileSystemTypeHasQuota(type_))
    421     return false;
    422 
    423   // For file system types with quota, some origins have unlimited storage.
    424   quota::QuotaManagerProxy* quota_manager_proxy =
    425       file_system_context_->quota_manager_proxy();
    426   CHECK(quota_manager_proxy);
    427   CHECK(quota_manager_proxy->quota_manager());
    428   fileapi::FileSystemType file_system_type =
    429       ppapi::PepperFileSystemTypeToFileSystemType(type_);
    430   return !quota_manager_proxy->quota_manager()->IsStorageUnlimited(
    431       root_url_.GetOrigin(),
    432       fileapi::FileSystemTypeToQuotaStorageType(file_system_type));
    433 }
    434 
    435 void PepperFileSystemBrowserHost::CreateQuotaReservation(
    436     const base::Closure& callback) {
    437   DCHECK(root_url_.is_valid());
    438   base::PostTaskAndReplyWithResult(
    439       file_system_context_->default_file_task_runner(),
    440       FROM_HERE,
    441       base::Bind(&QuotaReservation::Create,
    442                  file_system_context_,
    443                  root_url_.GetOrigin(),
    444                  ppapi::PepperFileSystemTypeToFileSystemType(type_)),
    445       base::Bind(&PepperFileSystemBrowserHost::GotQuotaReservation,
    446                  weak_factory_.GetWeakPtr(),
    447                  callback));
    448 }
    449 
    450 void PepperFileSystemBrowserHost::GotQuotaReservation(
    451     const base::Closure& callback,
    452     scoped_refptr<QuotaReservation> quota_reservation) {
    453   quota_reservation_ = quota_reservation;
    454   callback.Run();
    455 }
    456 
    457 void PepperFileSystemBrowserHost::GotReservedQuota(
    458     ppapi::host::ReplyMessageContext reply_context,
    459     int64_t amount,
    460     const ppapi::FileSizeMap& file_sizes) {
    461   DCHECK(reserving_quota_);
    462   reserving_quota_ = false;
    463   reserved_quota_ = amount;
    464 
    465   reply_context.params.set_result(PP_OK);
    466   host()->SendReply(
    467       reply_context,
    468       PpapiPluginMsg_FileSystem_ReserveQuotaReply(amount, file_sizes));
    469 }
    470 
    471 std::string PepperFileSystemBrowserHost::GetPluginMimeType() const {
    472   base::FilePath plugin_path = browser_ppapi_host_->GetPluginPath();
    473   PepperPluginInfo* info =
    474       PluginService::GetInstance()->GetRegisteredPpapiPluginInfo(plugin_path);
    475   if (!info || info->mime_types.empty())
    476     return std::string();
    477   // Use the first element in |info->mime_types| even if several elements exist.
    478   return info->mime_types[0].mime_type;
    479 }
    480 
    481 std::string PepperFileSystemBrowserHost::GeneratePluginId(
    482     const std::string& mime_type) const {
    483   // TODO(nhiroki): This function is very specialized for specific plugins (MIME
    484   // types).  If we bring this API to stable, we might have to make it more
    485   // general.
    486 
    487   std::string top_level_type;
    488   std::string subtype;
    489   if (!net::ParseMimeTypeWithoutParameter(
    490           mime_type, &top_level_type, &subtype) ||
    491       !net::IsValidTopLevelMimeType(top_level_type))
    492     return std::string();
    493 
    494   // Replace a slash used for type/subtype separator with an underscore.
    495   std::string output = top_level_type + "_" + subtype;
    496 
    497   // Verify |output| contains only alphabets, digits, or "._-".
    498   for (std::string::const_iterator it = output.begin(); it != output.end();
    499        ++it) {
    500     if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) && *it != '.' && *it != '_' &&
    501         *it != '-') {
    502       LOG(WARNING) << "Failed to generate a plugin id.";
    503       return std::string();
    504     }
    505   }
    506   return output;
    507 }
    508 
    509 }  // namespace content
    510