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