- add sources.
[platform/framework/web/crosswalk.git] / src / webkit / browser / fileapi / sandbox_file_system_backend_delegate.cc
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 "webkit/browser/fileapi/sandbox_file_system_backend_delegate.h"
6
7 #include "base/command_line.h"
8 #include "base/file_util.h"
9 #include "base/metrics/histogram.h"
10 #include "base/stl_util.h"
11 #include "base/task_runner_util.h"
12 #include "net/base/net_util.h"
13 #include "webkit/browser/blob/file_stream_reader.h"
14 #include "webkit/browser/fileapi/async_file_util_adapter.h"
15 #include "webkit/browser/fileapi/file_system_context.h"
16 #include "webkit/browser/fileapi/file_system_operation_context.h"
17 #include "webkit/browser/fileapi/file_system_url.h"
18 #include "webkit/browser/fileapi/file_system_usage_cache.h"
19 #include "webkit/browser/fileapi/obfuscated_file_util.h"
20 #include "webkit/browser/fileapi/sandbox_file_stream_writer.h"
21 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
22 #include "webkit/browser/fileapi/sandbox_quota_observer.h"
23 #include "webkit/browser/quota/quota_manager.h"
24 #include "webkit/common/fileapi/file_system_util.h"
25
26 namespace fileapi {
27
28 namespace {
29
30 const char kTemporaryOriginsCountLabel[] = "FileSystem.TemporaryOriginsCount";
31 const char kPersistentOriginsCountLabel[] = "FileSystem.PersistentOriginsCount";
32
33 const char kOpenFileSystemLabel[] = "FileSystem.OpenFileSystem";
34 const char kOpenFileSystemDetailLabel[] = "FileSystem.OpenFileSystemDetail";
35 const char kOpenFileSystemDetailNonThrottledLabel[] =
36     "FileSystem.OpenFileSystemDetailNonthrottled";
37 int64 kMinimumStatsCollectionIntervalHours = 1;
38
39 // For type directory names in ObfuscatedFileUtil.
40 // TODO(kinuko,nhiroki): Each type string registration should be done
41 // via its own backend.
42 const char kTemporaryDirectoryName[] = "t";
43 const char kPersistentDirectoryName[] = "p";
44 const char kSyncableDirectoryName[] = "s";
45
46 const char* kPrepopulateTypes[] = {
47   kPersistentDirectoryName,
48   kTemporaryDirectoryName
49 };
50
51 enum FileSystemError {
52   kOK = 0,
53   kIncognito,
54   kInvalidSchemeError,
55   kCreateDirectoryError,
56   kNotFound,
57   kUnknownError,
58   kFileSystemErrorMax,
59 };
60
61 // Restricted names.
62 // http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions
63 const base::FilePath::CharType* const kRestrictedNames[] = {
64   FILE_PATH_LITERAL("."), FILE_PATH_LITERAL(".."),
65 };
66
67 // Restricted chars.
68 const base::FilePath::CharType kRestrictedChars[] = {
69   FILE_PATH_LITERAL('/'), FILE_PATH_LITERAL('\\'),
70 };
71
72 std::string GetTypeStringForURL(const FileSystemURL& url) {
73   return SandboxFileSystemBackendDelegate::GetTypeString(url.type());
74 }
75
76 std::set<std::string> GetKnownTypeStrings() {
77   std::set<std::string> known_type_strings;
78   known_type_strings.insert(kTemporaryDirectoryName);
79   known_type_strings.insert(kPersistentDirectoryName);
80   known_type_strings.insert(kSyncableDirectoryName);
81   return known_type_strings;
82 }
83
84 class ObfuscatedOriginEnumerator
85     : public SandboxFileSystemBackendDelegate::OriginEnumerator {
86  public:
87   explicit ObfuscatedOriginEnumerator(ObfuscatedFileUtil* file_util) {
88     enum_.reset(file_util->CreateOriginEnumerator());
89   }
90   virtual ~ObfuscatedOriginEnumerator() {}
91
92   virtual GURL Next() OVERRIDE {
93     return enum_->Next();
94   }
95
96   virtual bool HasFileSystemType(FileSystemType type) const OVERRIDE {
97     return enum_->HasTypeDirectory(
98         SandboxFileSystemBackendDelegate::GetTypeString(type));
99   }
100
101  private:
102   scoped_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator> enum_;
103 };
104
105 void OpenFileSystemOnFileThread(
106     ObfuscatedFileUtil* file_util,
107     const GURL& origin_url,
108     FileSystemType type,
109     OpenFileSystemMode mode,
110     base::PlatformFileError* error_ptr) {
111   DCHECK(error_ptr);
112   const bool create = (mode == OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT);
113   file_util->GetDirectoryForOriginAndType(
114       origin_url, SandboxFileSystemBackendDelegate::GetTypeString(type),
115       create, error_ptr);
116   if (*error_ptr != base::PLATFORM_FILE_OK) {
117     UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemLabel,
118                               kCreateDirectoryError,
119                               kFileSystemErrorMax);
120   } else {
121     UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemLabel, kOK, kFileSystemErrorMax);
122   }
123   // The reference of file_util will be derefed on the FILE thread
124   // when the storage of this callback gets deleted regardless of whether
125   // this method is called or not.
126 }
127
128 void DidOpenFileSystem(
129     base::WeakPtr<SandboxFileSystemBackendDelegate> delegate,
130     const base::Callback<void(base::PlatformFileError error)>& callback,
131     base::PlatformFileError* error) {
132   if (delegate.get())
133     delegate.get()->CollectOpenFileSystemMetrics(*error);
134   callback.Run(*error);
135 }
136
137 template <typename T>
138 void DeleteSoon(base::SequencedTaskRunner* runner, T* ptr) {
139   if (!runner->DeleteSoon(FROM_HERE, ptr))
140     delete ptr;
141 }
142
143 }  // namespace
144
145 const base::FilePath::CharType
146 SandboxFileSystemBackendDelegate::kFileSystemDirectory[] =
147     FILE_PATH_LITERAL("File System");
148
149 // static
150 std::string SandboxFileSystemBackendDelegate::GetTypeString(
151     FileSystemType type) {
152   switch (type) {
153     case kFileSystemTypeTemporary:
154       return kTemporaryDirectoryName;
155     case kFileSystemTypePersistent:
156       return kPersistentDirectoryName;
157     case kFileSystemTypeSyncable:
158     case kFileSystemTypeSyncableForInternalSync:
159       return kSyncableDirectoryName;
160     case kFileSystemTypeUnknown:
161     default:
162       NOTREACHED() << "Unknown filesystem type requested:" << type;
163       return std::string();
164   }
165 }
166
167 SandboxFileSystemBackendDelegate::SandboxFileSystemBackendDelegate(
168     quota::QuotaManagerProxy* quota_manager_proxy,
169     base::SequencedTaskRunner* file_task_runner,
170     const base::FilePath& profile_path,
171     quota::SpecialStoragePolicy* special_storage_policy,
172     const FileSystemOptions& file_system_options)
173     : file_task_runner_(file_task_runner),
174       sandbox_file_util_(new AsyncFileUtilAdapter(
175           new ObfuscatedFileUtil(
176               special_storage_policy,
177               profile_path.Append(kFileSystemDirectory),
178               file_task_runner,
179               base::Bind(&GetTypeStringForURL),
180               GetKnownTypeStrings(),
181               this))),
182       file_system_usage_cache_(new FileSystemUsageCache(file_task_runner)),
183       quota_observer_(new SandboxQuotaObserver(
184           quota_manager_proxy,
185           file_task_runner,
186           obfuscated_file_util(),
187           usage_cache())),
188       special_storage_policy_(special_storage_policy),
189       file_system_options_(file_system_options),
190       is_filesystem_opened_(false),
191       weak_factory_(this) {
192   // Prepopulate database only if it can run asynchronously (i.e. the current
193   // thread is not file_task_runner). Usually this is the case but may not
194   // in test code.
195   if (!file_task_runner_->RunsTasksOnCurrentThread()) {
196     std::vector<std::string> types_to_prepopulate(
197         &kPrepopulateTypes[0],
198         &kPrepopulateTypes[arraysize(kPrepopulateTypes)]);
199     file_task_runner_->PostTask(
200         FROM_HERE,
201         base::Bind(&ObfuscatedFileUtil::MaybePrepopulateDatabase,
202                   base::Unretained(obfuscated_file_util()),
203                   types_to_prepopulate));
204   }
205 }
206
207 SandboxFileSystemBackendDelegate::~SandboxFileSystemBackendDelegate() {
208   io_thread_checker_.DetachFromThread();
209
210   if (!file_task_runner_->RunsTasksOnCurrentThread()) {
211     DeleteSoon(file_task_runner_.get(), sandbox_file_util_.release());
212     DeleteSoon(file_task_runner_.get(), quota_observer_.release());
213     DeleteSoon(file_task_runner_.get(), file_system_usage_cache_.release());
214   }
215 }
216
217 SandboxFileSystemBackendDelegate::OriginEnumerator*
218 SandboxFileSystemBackendDelegate::CreateOriginEnumerator() {
219   return new ObfuscatedOriginEnumerator(obfuscated_file_util());
220 }
221
222 base::FilePath
223 SandboxFileSystemBackendDelegate::GetBaseDirectoryForOriginAndType(
224     const GURL& origin_url,
225     FileSystemType type,
226     bool create) {
227   base::PlatformFileError error = base::PLATFORM_FILE_OK;
228   base::FilePath path = obfuscated_file_util()->GetDirectoryForOriginAndType(
229       origin_url, GetTypeString(type), create, &error);
230   if (error != base::PLATFORM_FILE_OK)
231     return base::FilePath();
232   return path;
233 }
234
235 void SandboxFileSystemBackendDelegate::OpenFileSystem(
236     const GURL& origin_url,
237     FileSystemType type,
238     OpenFileSystemMode mode,
239     const OpenFileSystemCallback& callback,
240     const GURL& root_url) {
241   if (!IsAllowedScheme(origin_url)) {
242     callback.Run(GURL(), std::string(), base::PLATFORM_FILE_ERROR_SECURITY);
243     return;
244   }
245
246   std::string name = GetFileSystemName(origin_url, type);
247
248   base::PlatformFileError* error_ptr = new base::PlatformFileError;
249   file_task_runner_->PostTaskAndReply(
250       FROM_HERE,
251       base::Bind(&OpenFileSystemOnFileThread,
252                  obfuscated_file_util(), origin_url, type, mode,
253                  base::Unretained(error_ptr)),
254       base::Bind(&DidOpenFileSystem,
255                  weak_factory_.GetWeakPtr(),
256                  base::Bind(callback, root_url, name),
257                  base::Owned(error_ptr)));
258
259   io_thread_checker_.DetachFromThread();
260   is_filesystem_opened_ = true;
261 }
262
263 scoped_ptr<FileSystemOperationContext>
264 SandboxFileSystemBackendDelegate::CreateFileSystemOperationContext(
265     const FileSystemURL& url,
266     FileSystemContext* context,
267     base::PlatformFileError* error_code) const {
268   if (!IsAccessValid(url)) {
269     *error_code = base::PLATFORM_FILE_ERROR_SECURITY;
270     return scoped_ptr<FileSystemOperationContext>();
271   }
272
273   const UpdateObserverList* update_observers = GetUpdateObservers(url.type());
274   const ChangeObserverList* change_observers = GetChangeObservers(url.type());
275   DCHECK(update_observers);
276
277   scoped_ptr<FileSystemOperationContext> operation_context(
278       new FileSystemOperationContext(context));
279   operation_context->set_update_observers(*update_observers);
280   operation_context->set_change_observers(
281       change_observers ? *change_observers : ChangeObserverList());
282
283   return operation_context.Pass();
284 }
285
286 scoped_ptr<webkit_blob::FileStreamReader>
287 SandboxFileSystemBackendDelegate::CreateFileStreamReader(
288     const FileSystemURL& url,
289     int64 offset,
290     const base::Time& expected_modification_time,
291     FileSystemContext* context) const {
292   if (!IsAccessValid(url))
293     return scoped_ptr<webkit_blob::FileStreamReader>();
294   return scoped_ptr<webkit_blob::FileStreamReader>(
295       webkit_blob::FileStreamReader::CreateForFileSystemFile(
296           context, url, offset, expected_modification_time));
297 }
298
299 scoped_ptr<FileStreamWriter>
300 SandboxFileSystemBackendDelegate::CreateFileStreamWriter(
301     const FileSystemURL& url,
302     int64 offset,
303     FileSystemContext* context,
304     FileSystemType type) const {
305   if (!IsAccessValid(url))
306     return scoped_ptr<FileStreamWriter>();
307   const UpdateObserverList* observers = GetUpdateObservers(type);
308   DCHECK(observers);
309   return scoped_ptr<FileStreamWriter>(
310       new SandboxFileStreamWriter(context, url, offset, *observers));
311 }
312
313 base::PlatformFileError
314 SandboxFileSystemBackendDelegate::DeleteOriginDataOnFileThread(
315     FileSystemContext* file_system_context,
316     quota::QuotaManagerProxy* proxy,
317     const GURL& origin_url,
318     FileSystemType type) {
319   int64 usage = GetOriginUsageOnFileThread(
320       file_system_context, origin_url, type);
321   usage_cache()->CloseCacheFiles();
322   bool result = obfuscated_file_util()->DeleteDirectoryForOriginAndType(
323       origin_url, GetTypeString(type));
324   if (result && proxy) {
325     proxy->NotifyStorageModified(
326         quota::QuotaClient::kFileSystem,
327         origin_url,
328         FileSystemTypeToQuotaStorageType(type),
329         -usage);
330   }
331
332   if (result)
333     return base::PLATFORM_FILE_OK;
334   return base::PLATFORM_FILE_ERROR_FAILED;
335 }
336
337 void SandboxFileSystemBackendDelegate::GetOriginsForTypeOnFileThread(
338     FileSystemType type, std::set<GURL>* origins) {
339   DCHECK(origins);
340   scoped_ptr<OriginEnumerator> enumerator(CreateOriginEnumerator());
341   GURL origin;
342   while (!(origin = enumerator->Next()).is_empty()) {
343     if (enumerator->HasFileSystemType(type))
344       origins->insert(origin);
345   }
346   switch (type) {
347     case kFileSystemTypeTemporary:
348       UMA_HISTOGRAM_COUNTS(kTemporaryOriginsCountLabel, origins->size());
349       break;
350     case kFileSystemTypePersistent:
351       UMA_HISTOGRAM_COUNTS(kPersistentOriginsCountLabel, origins->size());
352       break;
353     default:
354       break;
355   }
356 }
357
358 void SandboxFileSystemBackendDelegate::GetOriginsForHostOnFileThread(
359     FileSystemType type, const std::string& host,
360     std::set<GURL>* origins) {
361   DCHECK(origins);
362   scoped_ptr<OriginEnumerator> enumerator(CreateOriginEnumerator());
363   GURL origin;
364   while (!(origin = enumerator->Next()).is_empty()) {
365     if (host == net::GetHostOrSpecFromURL(origin) &&
366         enumerator->HasFileSystemType(type))
367       origins->insert(origin);
368   }
369 }
370
371 int64 SandboxFileSystemBackendDelegate::GetOriginUsageOnFileThread(
372     FileSystemContext* file_system_context,
373     const GURL& origin_url,
374     FileSystemType type) {
375   // Don't use usage cache and return recalculated usage for sticky invalidated
376   // origins.
377   if (ContainsKey(sticky_dirty_origins_, std::make_pair(origin_url, type)))
378     return RecalculateUsage(file_system_context, origin_url, type);
379
380   base::FilePath base_path =
381       GetBaseDirectoryForOriginAndType(origin_url, type, false);
382   if (base_path.empty() || !base::DirectoryExists(base_path))
383     return 0;
384   base::FilePath usage_file_path =
385       base_path.Append(FileSystemUsageCache::kUsageFileName);
386
387   bool is_valid = usage_cache()->IsValid(usage_file_path);
388   uint32 dirty_status = 0;
389   bool dirty_status_available =
390       usage_cache()->GetDirty(usage_file_path, &dirty_status);
391   bool visited = !visited_origins_.insert(origin_url).second;
392   if (is_valid && (dirty_status == 0 || (dirty_status_available && visited))) {
393     // The usage cache is clean (dirty == 0) or the origin is already
394     // initialized and running.  Read the cache file to get the usage.
395     int64 usage = 0;
396     return usage_cache()->GetUsage(usage_file_path, &usage) ? usage : -1;
397   }
398   // The usage cache has not been initialized or the cache is dirty.
399   // Get the directory size now and update the cache.
400   usage_cache()->Delete(usage_file_path);
401
402   int64 usage = RecalculateUsage(file_system_context, origin_url, type);
403
404   // This clears the dirty flag too.
405   usage_cache()->UpdateUsage(usage_file_path, usage);
406   return usage;
407 }
408
409 void SandboxFileSystemBackendDelegate::AddFileUpdateObserver(
410     FileSystemType type,
411     FileUpdateObserver* observer,
412     base::SequencedTaskRunner* task_runner) {
413   DCHECK(!is_filesystem_opened_ || io_thread_checker_.CalledOnValidThread());
414   update_observers_[type] =
415       update_observers_[type].AddObserver(observer, task_runner);
416 }
417
418 void SandboxFileSystemBackendDelegate::AddFileChangeObserver(
419     FileSystemType type,
420     FileChangeObserver* observer,
421     base::SequencedTaskRunner* task_runner) {
422   DCHECK(!is_filesystem_opened_ || io_thread_checker_.CalledOnValidThread());
423   change_observers_[type] =
424       change_observers_[type].AddObserver(observer, task_runner);
425 }
426
427 void SandboxFileSystemBackendDelegate::AddFileAccessObserver(
428     FileSystemType type,
429     FileAccessObserver* observer,
430     base::SequencedTaskRunner* task_runner) {
431   DCHECK(!is_filesystem_opened_ || io_thread_checker_.CalledOnValidThread());
432   access_observers_[type] =
433       access_observers_[type].AddObserver(observer, task_runner);
434 }
435
436 const UpdateObserverList* SandboxFileSystemBackendDelegate::GetUpdateObservers(
437     FileSystemType type) const {
438   std::map<FileSystemType, UpdateObserverList>::const_iterator iter =
439       update_observers_.find(type);
440   if (iter == update_observers_.end())
441     return NULL;
442   return &iter->second;
443 }
444
445 const ChangeObserverList* SandboxFileSystemBackendDelegate::GetChangeObservers(
446     FileSystemType type) const {
447   std::map<FileSystemType, ChangeObserverList>::const_iterator iter =
448       change_observers_.find(type);
449   if (iter == change_observers_.end())
450     return NULL;
451   return &iter->second;
452 }
453
454 const AccessObserverList* SandboxFileSystemBackendDelegate::GetAccessObservers(
455     FileSystemType type) const {
456   std::map<FileSystemType, AccessObserverList>::const_iterator iter =
457       access_observers_.find(type);
458   if (iter == access_observers_.end())
459     return NULL;
460   return &iter->second;
461 }
462
463 void SandboxFileSystemBackendDelegate::RegisterQuotaUpdateObserver(
464     FileSystemType type) {
465   AddFileUpdateObserver(type, quota_observer_.get(), file_task_runner_.get());
466 }
467
468 void SandboxFileSystemBackendDelegate::InvalidateUsageCache(
469     const GURL& origin,
470     FileSystemType type) {
471   base::PlatformFileError error = base::PLATFORM_FILE_OK;
472   base::FilePath usage_file_path = GetUsageCachePathForOriginAndType(
473       obfuscated_file_util(), origin, type, &error);
474   if (error != base::PLATFORM_FILE_OK)
475     return;
476   usage_cache()->IncrementDirty(usage_file_path);
477 }
478
479 void SandboxFileSystemBackendDelegate::StickyInvalidateUsageCache(
480     const GURL& origin,
481     FileSystemType type) {
482   sticky_dirty_origins_.insert(std::make_pair(origin, type));
483   quota_observer()->SetUsageCacheEnabled(origin, type, false);
484   InvalidateUsageCache(origin, type);
485 }
486
487 FileSystemFileUtil* SandboxFileSystemBackendDelegate::sync_file_util() {
488   return static_cast<AsyncFileUtilAdapter*>(file_util())->sync_file_util();
489 }
490
491 bool SandboxFileSystemBackendDelegate::IsAccessValid(
492     const FileSystemURL& url) const {
493   if (!IsAllowedScheme(url.origin()))
494     return false;
495
496   if (url.path().ReferencesParent())
497     return false;
498
499   // Return earlier if the path is '/', because VirtualPath::BaseName()
500   // returns '/' for '/' and we fail the "basename != '/'" check below.
501   // (We exclude '.' because it's disallowed by spec.)
502   if (VirtualPath::IsRootPath(url.path()) &&
503       url.path() != base::FilePath(base::FilePath::kCurrentDirectory))
504     return true;
505
506   // Restricted names specified in
507   // http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions
508   base::FilePath filename = VirtualPath::BaseName(url.path());
509   // See if the name is allowed to create.
510   for (size_t i = 0; i < arraysize(kRestrictedNames); ++i) {
511     if (filename.value() == kRestrictedNames[i])
512       return false;
513   }
514   for (size_t i = 0; i < arraysize(kRestrictedChars); ++i) {
515     if (filename.value().find(kRestrictedChars[i]) !=
516         base::FilePath::StringType::npos)
517       return false;
518   }
519
520   return true;
521 }
522
523 bool SandboxFileSystemBackendDelegate::IsAllowedScheme(const GURL& url) const {
524   // Basically we only accept http or https. We allow file:// URLs
525   // only if --allow-file-access-from-files flag is given.
526   if (url.SchemeIsHTTPOrHTTPS())
527     return true;
528   if (url.SchemeIsFileSystem())
529     return url.inner_url() && IsAllowedScheme(*url.inner_url());
530
531   for (size_t i = 0;
532        i < file_system_options_.additional_allowed_schemes().size();
533        ++i) {
534     if (url.SchemeIs(
535             file_system_options_.additional_allowed_schemes()[i].c_str()))
536       return true;
537   }
538   return false;
539 }
540
541 base::FilePath
542 SandboxFileSystemBackendDelegate::GetUsageCachePathForOriginAndType(
543     const GURL& origin_url,
544     FileSystemType type) {
545   base::PlatformFileError error;
546   base::FilePath path = GetUsageCachePathForOriginAndType(
547       obfuscated_file_util(), origin_url, type, &error);
548   if (error != base::PLATFORM_FILE_OK)
549     return base::FilePath();
550   return path;
551 }
552
553 // static
554 base::FilePath
555 SandboxFileSystemBackendDelegate::GetUsageCachePathForOriginAndType(
556     ObfuscatedFileUtil* sandbox_file_util,
557     const GURL& origin_url,
558     FileSystemType type,
559     base::PlatformFileError* error_out) {
560   DCHECK(error_out);
561   *error_out = base::PLATFORM_FILE_OK;
562   base::FilePath base_path = sandbox_file_util->GetDirectoryForOriginAndType(
563       origin_url, GetTypeString(type), false /* create */, error_out);
564   if (*error_out != base::PLATFORM_FILE_OK)
565     return base::FilePath();
566   return base_path.Append(FileSystemUsageCache::kUsageFileName);
567 }
568
569 int64 SandboxFileSystemBackendDelegate::RecalculateUsage(
570     FileSystemContext* context,
571     const GURL& origin,
572     FileSystemType type) {
573   FileSystemOperationContext operation_context(context);
574   FileSystemURL url = context->CreateCrackedFileSystemURL(
575       origin, type, base::FilePath());
576   scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator(
577       obfuscated_file_util()->CreateFileEnumerator(
578           &operation_context, url, true));
579
580   base::FilePath file_path_each;
581   int64 usage = 0;
582
583   while (!(file_path_each = enumerator->Next()).empty()) {
584     usage += enumerator->Size();
585     usage += ObfuscatedFileUtil::ComputeFilePathCost(file_path_each);
586   }
587
588   return usage;
589 }
590
591 void SandboxFileSystemBackendDelegate::CollectOpenFileSystemMetrics(
592     base::PlatformFileError error_code) {
593   base::Time now = base::Time::Now();
594   bool throttled = now < next_release_time_for_open_filesystem_stat_;
595   if (!throttled) {
596     next_release_time_for_open_filesystem_stat_ =
597         now + base::TimeDelta::FromHours(kMinimumStatsCollectionIntervalHours);
598   }
599
600 #define REPORT(report_value)                                            \
601   UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemDetailLabel,                 \
602                             (report_value),                             \
603                             kFileSystemErrorMax);                       \
604   if (!throttled) {                                                     \
605     UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemDetailNonThrottledLabel,   \
606                               (report_value),                           \
607                               kFileSystemErrorMax);                     \
608   }
609
610   switch (error_code) {
611     case base::PLATFORM_FILE_OK:
612       REPORT(kOK);
613       break;
614     case base::PLATFORM_FILE_ERROR_INVALID_URL:
615       REPORT(kInvalidSchemeError);
616       break;
617     case base::PLATFORM_FILE_ERROR_NOT_FOUND:
618       REPORT(kNotFound);
619       break;
620     case base::PLATFORM_FILE_ERROR_FAILED:
621     default:
622       REPORT(kUnknownError);
623       break;
624   }
625 #undef REPORT
626 }
627
628 ObfuscatedFileUtil* SandboxFileSystemBackendDelegate::obfuscated_file_util() {
629   return static_cast<ObfuscatedFileUtil*>(sync_file_util());
630 }
631
632 // Declared in obfuscated_file_util.h.
633 // static
634 ObfuscatedFileUtil* ObfuscatedFileUtil::CreateForTesting(
635     quota::SpecialStoragePolicy* special_storage_policy,
636     const base::FilePath& file_system_directory,
637     base::SequencedTaskRunner* file_task_runner) {
638   return new ObfuscatedFileUtil(special_storage_policy,
639                                 file_system_directory,
640                                 file_task_runner,
641                                 base::Bind(&GetTypeStringForURL),
642                                 GetKnownTypeStrings(),
643                                 NULL);
644 }
645
646 }  // namespace fileapi