• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.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(int render_process_id)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();
47 }
48 
49 }  // namespace
50 
QuotaRequest(int32_t amount_arg,const RequestQuotaCallback & callback_arg)51 PepperFileSystemBrowserHost::QuotaRequest::QuotaRequest(
52     int32_t amount_arg,
53     const RequestQuotaCallback& callback_arg)
54     : amount(amount_arg),
55       callback(callback_arg) {
56 }
57 
~QuotaRequest()58 PepperFileSystemBrowserHost::QuotaRequest::~QuotaRequest() {
59 }
60 
PepperFileSystemBrowserHost(BrowserPpapiHost * host,PP_Instance instance,PP_Resource resource,PP_FileSystemType type)61 PepperFileSystemBrowserHost::PepperFileSystemBrowserHost(BrowserPpapiHost* host,
62                                                          PP_Instance instance,
63                                                          PP_Resource resource,
64                                                          PP_FileSystemType type)
65     : ResourceHost(host->GetPpapiHost(), instance, resource),
66       browser_ppapi_host_(host),
67       type_(type),
68       called_open_(false),
69       opened_(false),
70       file_system_context_(NULL),
71       reserved_quota_(0),
72       reserving_quota_(false),
73       weak_factory_(this) {
74 }
75 
~PepperFileSystemBrowserHost()76 PepperFileSystemBrowserHost::~PepperFileSystemBrowserHost() {
77   // All FileRefs and FileIOs that reference us must have been destroyed. Cancel
78   // all pending file system operations.
79   if (file_system_operation_runner_)
80     file_system_operation_runner_->Shutdown();
81 }
82 
OpenExisting(const GURL & root_url,const base::Closure & callback)83 void PepperFileSystemBrowserHost::OpenExisting(const GURL& root_url,
84                                                const base::Closure& callback) {
85   root_url_ = root_url;
86   int render_process_id = 0;
87   int unused;
88   if (!browser_ppapi_host_->GetRenderViewIDsForInstance(
89            pp_instance(), &render_process_id, &unused)) {
90     NOTREACHED();
91   }
92   called_open_ = true;
93   // Get the file system context asynchronously, and then complete the Open
94   // operation by calling |callback|.
95   BrowserThread::PostTaskAndReplyWithResult(
96       BrowserThread::UI,
97       FROM_HERE,
98       base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
99       base::Bind(&PepperFileSystemBrowserHost::OpenExistingFileSystem,
100                  weak_factory_.GetWeakPtr(), callback));
101 }
102 
OnResourceMessageReceived(const IPC::Message & msg,ppapi::host::HostMessageContext * context)103 int32_t PepperFileSystemBrowserHost::OnResourceMessageReceived(
104     const IPC::Message& msg,
105     ppapi::host::HostMessageContext* context) {
106   IPC_BEGIN_MESSAGE_MAP(PepperFileSystemBrowserHost, msg)
107     PPAPI_DISPATCH_HOST_RESOURCE_CALL(
108         PpapiHostMsg_FileSystem_Open,
109         OnHostMsgOpen)
110     PPAPI_DISPATCH_HOST_RESOURCE_CALL(
111         PpapiHostMsg_FileSystem_InitIsolatedFileSystem,
112         OnHostMsgInitIsolatedFileSystem)
113   IPC_END_MESSAGE_MAP()
114   return PP_ERROR_FAILED;
115 }
116 
IsFileSystemHost()117 bool PepperFileSystemBrowserHost::IsFileSystemHost() {
118   return true;
119 }
120 
OpenQuotaFile(PepperFileIOHost * file_io_host,const fileapi::FileSystemURL & url,const OpenQuotaFileCallback & callback)121 void PepperFileSystemBrowserHost::OpenQuotaFile(
122     PepperFileIOHost* file_io_host,
123     const fileapi::FileSystemURL& url,
124     const OpenQuotaFileCallback& callback) {
125   int32_t id = file_io_host->pp_resource();
126   std::pair<FileMap::iterator, bool> insert_result =
127       files_.insert(std::make_pair(id, file_io_host));
128   if (insert_result.second) {
129     base::PostTaskAndReplyWithResult(
130         file_system_context_->default_file_task_runner(),
131         FROM_HERE,
132         base::Bind(&QuotaReservation::OpenFile,
133                    quota_reservation_,
134                    id,
135                    url),
136         callback);
137   } else {
138     NOTREACHED();
139   }
140 }
141 
CloseQuotaFile(PepperFileIOHost * file_io_host)142 void PepperFileSystemBrowserHost::CloseQuotaFile(
143     PepperFileIOHost* file_io_host) {
144   int32_t id = file_io_host->pp_resource();
145   int64_t max_written_offset = 0;
146   FileMap::iterator it = files_.find(id);
147   if (it != files_.end()) {
148     max_written_offset = file_io_host->max_written_offset();
149     files_.erase(it);
150   } else {
151     NOTREACHED();
152     return;
153   }
154 
155   file_system_context_->default_file_task_runner()->PostTask(
156       FROM_HERE,
157       base::Bind(&QuotaReservation::CloseFile,
158                  quota_reservation_,
159                  id,
160                  max_written_offset));
161 }
162 
RequestQuota(int32_t amount,const RequestQuotaCallback & callback)163 int32_t PepperFileSystemBrowserHost::RequestQuota(
164     int32_t amount,
165     const RequestQuotaCallback& callback) {
166   DCHECK(amount >= 0);
167   if (!reserving_quota_ && reserved_quota_ >= amount) {
168     reserved_quota_ -= amount;
169     return amount;
170   }
171 
172   // Queue up a pending quota request.
173   pending_quota_requests_.push(QuotaRequest(amount, callback));
174 
175   // Reserve more quota if we haven't already.
176   if (!reserving_quota_)
177     ReserveQuota(amount);
178 
179   return PP_OK_COMPLETIONPENDING;
180 }
181 
OnHostMsgOpen(ppapi::host::HostMessageContext * context,int64_t)182 int32_t PepperFileSystemBrowserHost::OnHostMsgOpen(
183     ppapi::host::HostMessageContext* context,
184     int64_t /* unused */) {
185   // TODO(raymes): The file system size is now unused by FileSystemDispatcher.
186   // Figure out why. Why is the file system size signed?
187 
188   // Not allow multiple opens.
189   if (called_open_)
190     return PP_ERROR_INPROGRESS;
191   called_open_ = true;
192 
193   fileapi::FileSystemType file_system_type =
194       ppapi::PepperFileSystemTypeToFileSystemType(type_);
195   if (file_system_type == fileapi::kFileSystemTypeUnknown)
196     return PP_ERROR_FAILED;
197 
198   int render_process_id = 0;
199   int unused;
200   if (!browser_ppapi_host_->GetRenderViewIDsForInstance(pp_instance(),
201                                                         &render_process_id,
202                                                         &unused)) {
203       return PP_ERROR_FAILED;
204   }
205 
206   BrowserThread::PostTaskAndReplyWithResult(
207       BrowserThread::UI,
208       FROM_HERE,
209       base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
210       base::Bind(&PepperFileSystemBrowserHost::OpenFileSystem,
211                  weak_factory_.GetWeakPtr(),
212                  context->MakeReplyMessageContext(),
213                  file_system_type));
214   return PP_OK_COMPLETIONPENDING;
215 }
216 
OpenExistingFileSystem(const base::Closure & callback,scoped_refptr<fileapi::FileSystemContext> file_system_context)217 void PepperFileSystemBrowserHost::OpenExistingFileSystem(
218     const base::Closure& callback,
219     scoped_refptr<fileapi::FileSystemContext> file_system_context) {
220   if (file_system_context.get()) {
221     opened_ = true;
222   } else {
223     // If there is no file system context, we log a warning and continue with an
224     // invalid resource (which will produce errors when used), since we have no
225     // way to communicate the error to the caller.
226     LOG(WARNING) << "Could not retrieve file system context.";
227   }
228   SetFileSystemContext(file_system_context);
229 
230   if (ShouldCreateQuotaReservation())
231     CreateQuotaReservation(callback);
232   else
233     callback.Run();
234 }
235 
OpenFileSystem(ppapi::host::ReplyMessageContext reply_context,fileapi::FileSystemType file_system_type,scoped_refptr<fileapi::FileSystemContext> file_system_context)236 void PepperFileSystemBrowserHost::OpenFileSystem(
237     ppapi::host::ReplyMessageContext reply_context,
238     fileapi::FileSystemType file_system_type,
239     scoped_refptr<fileapi::FileSystemContext> file_system_context) {
240   if (!file_system_context.get()) {
241     OpenFileSystemComplete(
242         reply_context, GURL(), std::string(), base::PLATFORM_FILE_ERROR_FAILED);
243     return;
244   }
245 
246   SetFileSystemContext(file_system_context);
247 
248   GURL origin = browser_ppapi_host_->GetDocumentURLForInstance(
249       pp_instance()).GetOrigin();
250   file_system_context_->OpenFileSystem(origin, file_system_type,
251       fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
252       base::Bind(&PepperFileSystemBrowserHost::OpenFileSystemComplete,
253                  weak_factory_.GetWeakPtr(),
254                  reply_context));
255 }
256 
OpenFileSystemComplete(ppapi::host::ReplyMessageContext reply_context,const GURL & root,const std::string &,base::PlatformFileError error)257 void PepperFileSystemBrowserHost::OpenFileSystemComplete(
258     ppapi::host::ReplyMessageContext reply_context,
259     const GURL& root,
260     const std::string& /* unused */,
261     base::PlatformFileError error) {
262   int32 pp_error = ppapi::PlatformFileErrorToPepperError(error);
263   if (pp_error == PP_OK) {
264     opened_ = true;
265     root_url_ = root;
266 
267     if (ShouldCreateQuotaReservation()) {
268       CreateQuotaReservation(
269           base::Bind(&PepperFileSystemBrowserHost::SendReplyForFileSystem,
270                      weak_factory_.GetWeakPtr(),
271                      reply_context,
272                      static_cast<int32_t>(PP_OK)));
273       return;
274     }
275   }
276   SendReplyForFileSystem(reply_context, pp_error);
277 }
278 
OpenIsolatedFileSystem(ppapi::host::ReplyMessageContext reply_context,const std::string & fsid,PP_IsolatedFileSystemType_Private type,scoped_refptr<fileapi::FileSystemContext> file_system_context)279 void PepperFileSystemBrowserHost::OpenIsolatedFileSystem(
280     ppapi::host::ReplyMessageContext reply_context,
281     const std::string& fsid,
282     PP_IsolatedFileSystemType_Private type,
283     scoped_refptr<fileapi::FileSystemContext> file_system_context) {
284   if (!file_system_context.get()) {
285     SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
286     return;
287   }
288   SetFileSystemContext(file_system_context);
289 
290   root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString(
291       browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
292       fsid, ppapi::IsolatedFileSystemTypeToRootName(type)));
293   if (!root_url_.is_valid()) {
294     SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
295     return;
296   }
297 
298   switch (type) {
299     case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_CRX:
300       opened_ = true;
301       SendReplyForIsolatedFileSystem(reply_context, fsid, PP_OK);
302       return;
303     case PP_ISOLATEDFILESYSTEMTYPE_PRIVATE_PLUGINPRIVATE:
304       OpenPluginPrivateFileSystem(reply_context, fsid, file_system_context_);
305       return;
306     default:
307       NOTREACHED();
308       SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_BADARGUMENT);
309       return;
310   }
311 }
312 
OpenPluginPrivateFileSystem(ppapi::host::ReplyMessageContext reply_context,const std::string & fsid,scoped_refptr<fileapi::FileSystemContext> file_system_context)313 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystem(
314     ppapi::host::ReplyMessageContext reply_context,
315     const std::string& fsid,
316     scoped_refptr<fileapi::FileSystemContext> file_system_context) {
317   GURL origin = browser_ppapi_host_->GetDocumentURLForInstance(
318       pp_instance()).GetOrigin();
319   if (!origin.is_valid()) {
320     SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_FAILED);
321     return;
322   }
323 
324   const std::string& plugin_id = GeneratePluginId(GetPluginMimeType());
325   if (plugin_id.empty()) {
326     SendReplyForIsolatedFileSystem(reply_context, fsid, PP_ERROR_BADARGUMENT);
327     return;
328   }
329 
330   file_system_context->OpenPluginPrivateFileSystem(
331       origin, fileapi::kFileSystemTypePluginPrivate, fsid, plugin_id,
332       fileapi::OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT,
333       base::Bind(
334           &PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete,
335           weak_factory_.GetWeakPtr(), reply_context, fsid));
336 }
337 
OpenPluginPrivateFileSystemComplete(ppapi::host::ReplyMessageContext reply_context,const std::string & fsid,base::PlatformFileError error)338 void PepperFileSystemBrowserHost::OpenPluginPrivateFileSystemComplete(
339     ppapi::host::ReplyMessageContext reply_context,
340     const std::string& fsid,
341     base::PlatformFileError error) {
342   int32 pp_error = ppapi::PlatformFileErrorToPepperError(error);
343   if (pp_error == PP_OK)
344     opened_ = true;
345   SendReplyForIsolatedFileSystem(reply_context, fsid, pp_error);
346 }
347 
OnHostMsgInitIsolatedFileSystem(ppapi::host::HostMessageContext * context,const std::string & fsid,PP_IsolatedFileSystemType_Private type)348 int32_t PepperFileSystemBrowserHost::OnHostMsgInitIsolatedFileSystem(
349     ppapi::host::HostMessageContext* context,
350     const std::string& fsid,
351     PP_IsolatedFileSystemType_Private type) {
352   // Do not allow multiple opens.
353   if (called_open_)
354     return PP_ERROR_INPROGRESS;
355   called_open_ = true;
356 
357   // Do a sanity check.
358   if (!fileapi::ValidateIsolatedFileSystemId(fsid))
359     return PP_ERROR_BADARGUMENT;
360 
361   int render_process_id = 0;
362   int unused;
363   if (!browser_ppapi_host_->GetRenderViewIDsForInstance(pp_instance(),
364                                                         &render_process_id,
365                                                         &unused)) {
366     fileapi::IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
367     return PP_ERROR_FAILED;
368   }
369 
370   root_url_ = GURL(fileapi::GetIsolatedFileSystemRootURIString(
371       browser_ppapi_host_->GetDocumentURLForInstance(pp_instance()).GetOrigin(),
372       fsid, ppapi::IsolatedFileSystemTypeToRootName(type)));
373 
374   BrowserThread::PostTaskAndReplyWithResult(
375       BrowserThread::UI,
376       FROM_HERE,
377       base::Bind(&GetFileSystemContextFromRenderId, render_process_id),
378       base::Bind(&PepperFileSystemBrowserHost::OpenIsolatedFileSystem,
379                  weak_factory_.GetWeakPtr(),
380                  context->MakeReplyMessageContext(), fsid, type));
381   return PP_OK_COMPLETIONPENDING;
382 }
383 
SendReplyForFileSystem(ppapi::host::ReplyMessageContext reply_context,int32_t pp_error)384 void PepperFileSystemBrowserHost::SendReplyForFileSystem(
385     ppapi::host::ReplyMessageContext reply_context,
386     int32_t pp_error) {
387   reply_context.params.set_result(pp_error);
388   host()->SendReply(reply_context, PpapiPluginMsg_FileSystem_OpenReply());
389 }
390 
SendReplyForIsolatedFileSystem(ppapi::host::ReplyMessageContext reply_context,const std::string & fsid,int32_t error)391 void PepperFileSystemBrowserHost::SendReplyForIsolatedFileSystem(
392     ppapi::host::ReplyMessageContext reply_context,
393     const std::string& fsid,
394     int32_t error) {
395   if (error != PP_OK)
396     fileapi::IsolatedContext::GetInstance()->RevokeFileSystem(fsid);
397   reply_context.params.set_result(error);
398   host()->SendReply(reply_context,
399                     PpapiPluginMsg_FileSystem_InitIsolatedFileSystemReply());
400 }
401 
SetFileSystemContext(scoped_refptr<fileapi::FileSystemContext> file_system_context)402 void PepperFileSystemBrowserHost::SetFileSystemContext(
403     scoped_refptr<fileapi::FileSystemContext> file_system_context) {
404   file_system_context_ = file_system_context;
405   if (type_ != PP_FILESYSTEMTYPE_EXTERNAL) {
406     file_system_operation_runner_ =
407         file_system_context_->CreateFileSystemOperationRunner();
408   }
409 }
410 
ShouldCreateQuotaReservation() const411 bool PepperFileSystemBrowserHost::ShouldCreateQuotaReservation() const {
412   // Some file system types don't have quota.
413   if (!ppapi::FileSystemTypeHasQuota(type_))
414     return false;
415 
416   // For file system types with quota, ome origins have unlimited storage.
417   quota::QuotaManagerProxy* quota_manager_proxy =
418       file_system_context_->quota_manager_proxy();
419   CHECK(quota_manager_proxy);
420   CHECK(quota_manager_proxy->quota_manager());
421   fileapi::FileSystemType file_system_type =
422       ppapi::PepperFileSystemTypeToFileSystemType(type_);
423   return !quota_manager_proxy->quota_manager()->IsStorageUnlimited(
424       root_url_.GetOrigin(),
425       fileapi::FileSystemTypeToQuotaStorageType(file_system_type));
426 }
427 
CreateQuotaReservation(const base::Closure & callback)428 void PepperFileSystemBrowserHost::CreateQuotaReservation(
429     const base::Closure& callback) {
430   DCHECK(root_url_.is_valid());
431   base::PostTaskAndReplyWithResult(
432       file_system_context_->default_file_task_runner(),
433       FROM_HERE,
434       base::Bind(&QuotaReservation::Create,
435                  file_system_context_,
436                  root_url_.GetOrigin(),
437                  ppapi::PepperFileSystemTypeToFileSystemType(type_)),
438       base::Bind(&PepperFileSystemBrowserHost::GotQuotaReservation,
439                  weak_factory_.GetWeakPtr(),
440                  callback));
441 }
442 
GotQuotaReservation(const base::Closure & callback,scoped_refptr<QuotaReservation> quota_reservation)443 void PepperFileSystemBrowserHost::GotQuotaReservation(
444     const base::Closure& callback,
445     scoped_refptr<QuotaReservation> quota_reservation) {
446   quota_reservation_ = quota_reservation;
447   callback.Run();
448 }
449 
ReserveQuota(int32_t amount)450 void PepperFileSystemBrowserHost::ReserveQuota(int32_t amount) {
451   DCHECK(!reserving_quota_);
452   reserving_quota_ = true;
453 
454   // Get the max_written_offset for each open file.
455   QuotaReservation::OffsetMap max_written_offsets;
456   for (FileMap::iterator it = files_.begin(); it != files_.end(); ++ it) {
457     max_written_offsets.insert(
458         std::make_pair(it->first, it->second->max_written_offset()));
459   }
460 
461   int64_t reservation_amount = std::max<int64_t>(kMinimumQuotaReservationSize,
462                                                  amount);
463   file_system_context_->default_file_task_runner()->PostTask(
464       FROM_HERE,
465       base::Bind(&QuotaReservation::ReserveQuota,
466                  quota_reservation_,
467                  reservation_amount,
468                  max_written_offsets,
469                  base::Bind(&PepperFileSystemBrowserHost::GotReservedQuota,
470                             weak_factory_.GetWeakPtr())));
471 }
472 
GotReservedQuota(int64_t amount,const QuotaReservation::OffsetMap & max_written_offsets)473 void PepperFileSystemBrowserHost::GotReservedQuota(
474     int64_t amount,
475     const QuotaReservation::OffsetMap& max_written_offsets) {
476   DCHECK(reserving_quota_);
477   reserving_quota_ = false;
478   reserved_quota_ = amount;
479 
480   // Update open files with their new base sizes. This won't write over any
481   // updates since the files are waiting for quota and can't write.
482   for (FileMap::iterator it = files_.begin(); it != files_.end(); ++ it) {
483     QuotaReservation::OffsetMap::const_iterator offset_it =
484         max_written_offsets.find(it->first);
485     if (offset_it != max_written_offsets.end())
486       it->second->set_max_written_offset(offset_it->second);
487     else
488       NOTREACHED();
489   }
490 
491   DCHECK(!pending_quota_requests_.empty());
492   // If we can't grant the first request after refreshing reserved_quota_, then
493   // fail all pending quota requests to avoid an infinite refresh/fail loop.
494   bool fail_all = reserved_quota_ < pending_quota_requests_.front().amount;
495   while (!pending_quota_requests_.empty()) {
496     QuotaRequest& request = pending_quota_requests_.front();
497     if (fail_all) {
498       request.callback.Run(0);
499       pending_quota_requests_.pop();
500     } else if (reserved_quota_ >= request.amount) {
501       reserved_quota_ -= request.amount;
502       request.callback.Run(request.amount);
503       pending_quota_requests_.pop();
504     } else {
505       // Refresh the quota reservation for the first pending request that we
506       // can't satisfy.
507       ReserveQuota(request.amount);
508       break;
509     }
510   }
511 }
512 
GetPluginMimeType() const513 std::string PepperFileSystemBrowserHost::GetPluginMimeType() const {
514   base::FilePath plugin_path = browser_ppapi_host_->GetPluginPath();
515   PepperPluginInfo* info =
516       PluginService::GetInstance()->GetRegisteredPpapiPluginInfo(plugin_path);
517   if (!info || info->mime_types.empty())
518     return std::string();
519   // Use the first element in |info->mime_types| even if several elements exist.
520   return info->mime_types[0].mime_type;
521 }
522 
GeneratePluginId(const std::string & mime_type) const523 std::string PepperFileSystemBrowserHost::GeneratePluginId(
524     const std::string& mime_type) const {
525   // TODO(nhiroki): This function is very specialized for specific plugins (MIME
526   // types).  If we bring this API to stable, we might have to make it more
527   // general.
528 
529   if (!net::IsMimeType(mime_type))
530     return std::string();
531   std::string output = mime_type;
532 
533   // Replace a slash used for type/subtype separator with an underscore.
534   // NOTE: This assumes there is only one slash in the MIME type.
535   ReplaceFirstSubstringAfterOffset(&output, 0, "/", "_");
536 
537   // Verify |output| contains only alphabets, digits, or "._-".
538   for (std::string::const_iterator it = output.begin();
539        it != output.end(); ++it) {
540     if (!IsAsciiAlpha(*it) && !IsAsciiDigit(*it) &&
541         *it != '.' && *it != '_' && *it != '-') {
542       LOG(WARNING) << "Failed to generate a plugin id.";
543       return std::string();
544     }
545   }
546   return output;
547 }
548 
549 }  // namespace content
550