Home | History | Annotate | Download | only in file_system
      1 // Copyright 2014 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 "chrome/browser/extensions/api/file_system/entry_watcher_service.h"
      6 
      7 #include "base/thread_task_runner_handle.h"
      8 #include "chrome/browser/extensions/extension_util.h"
      9 #include "chrome/browser/profiles/profile.h"
     10 #include "chrome/common/extensions/api/file_system.h"
     11 #include "components/keyed_service/content/browser_context_dependency_manager.h"
     12 #include "content/public/browser/browser_context.h"
     13 #include "content/public/browser/browser_thread.h"
     14 #include "content/public/browser/storage_partition.h"
     15 #include "extensions/browser/event_router.h"
     16 #include "storage/browser/fileapi/file_system_context.h"
     17 
     18 namespace extensions {
     19 namespace {
     20 
     21 // Default implementation for dispatching an event. Can be replaced for unit
     22 // tests by EntryWatcherService::SetDispatchEventImplForTesting().
     23 void DispatchEventImpl(EventRouter* event_router,
     24                        const std::string& extension_id,
     25                        scoped_ptr<Event> event) {
     26   event_router->DispatchEventToExtension(extension_id, event.Pass());
     27 }
     28 
     29 // Default implementation for acquiring a file system context for a specific
     30 // |extension_id| and |context|.
     31 storage::FileSystemContext* GetFileSystemContextImpl(
     32     const std::string& extension_id,
     33     content::BrowserContext* context) {
     34   const GURL site = util::GetSiteForExtensionId(extension_id, context);
     35   return content::BrowserContext::GetStoragePartitionForSite(context, site)
     36       ->GetFileSystemContext();
     37 }
     38 
     39 }  // namespace
     40 
     41 EntryWatcherService::EntryWatcherService(content::BrowserContext* context)
     42     : context_(context),
     43       dispatch_event_impl_(
     44           base::Bind(&DispatchEventImpl, EventRouter::Get(context))),
     45       get_file_system_context_impl_(base::Bind(&GetFileSystemContextImpl)),
     46       observing_(this),
     47       weak_ptr_factory_(this) {
     48   // TODO(mtomasz): Restore persistent watchers.
     49 }
     50 
     51 EntryWatcherService::~EntryWatcherService() {
     52 }
     53 
     54 void EntryWatcherService::SetDispatchEventImplForTesting(
     55     const DispatchEventImplCallback& callback) {
     56   dispatch_event_impl_ = callback;
     57 }
     58 
     59 void EntryWatcherService::SetGetFileSystemContextImplForTesting(
     60     const GetFileSystemContextImplCallback& callback) {
     61   get_file_system_context_impl_ = callback;
     62 }
     63 
     64 void EntryWatcherService::WatchDirectory(
     65     const std::string& extension_id,
     66     const storage::FileSystemURL& url,
     67     bool recursive,
     68     const storage::WatcherManager::StatusCallback& callback) {
     69   // TODO(mtomasz): Add support for recursive watchers.
     70   if (recursive) {
     71     base::ThreadTaskRunnerHandle::Get()->PostTask(
     72         FROM_HERE,
     73         base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION));
     74     return;
     75   }
     76 
     77   storage::FileSystemContext* const context =
     78       get_file_system_context_impl_.Run(extension_id, context_);
     79   DCHECK(context);
     80 
     81   storage::WatcherManager* const watcher_manager =
     82       context->GetWatcherManager(url.type());
     83   if (!watcher_manager) {
     84     // Post a task instead of calling the callback directly, since the caller
     85     // may expect the callback to be called asynchronously.
     86     base::ThreadTaskRunnerHandle::Get()->PostTask(
     87         FROM_HERE,
     88         base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION));
     89     return;
     90   }
     91 
     92   // Passing a pointer to WatcherManager is safe, since the pointer is required
     93   // to be valid until shutdown.
     94   context->default_file_task_runner()->PostNonNestableTask(
     95       FROM_HERE,
     96       base::Bind(&storage::WatcherManager::WatchDirectory,
     97                  base::Unretained(watcher_manager),  // Outlives the service.
     98                  url,
     99                  recursive,
    100                  base::Bind(&EntryWatcherService::OnWatchDirectoryCompleted,
    101                             weak_ptr_factory_.GetWeakPtr(),
    102                             watcher_manager,  // Outlives the service.
    103                             extension_id,
    104                             url,
    105                             recursive,
    106                             callback)));
    107 }
    108 
    109 void EntryWatcherService::UnwatchEntry(
    110     const std::string& extension_id,
    111     const storage::FileSystemURL& url,
    112     const storage::WatcherManager::StatusCallback& callback) {
    113   storage::FileSystemContext* const context =
    114       get_file_system_context_impl_.Run(extension_id, context_);
    115   DCHECK(context);
    116 
    117   storage::WatcherManager* const watcher_manager =
    118       context->GetWatcherManager(url.type());
    119   if (!watcher_manager) {
    120     base::ThreadTaskRunnerHandle::Get()->PostTask(
    121         FROM_HERE,
    122         base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION));
    123     return;
    124   }
    125 
    126   // Passing a pointer to WatcherManager is safe, since the pointer is required
    127   // to be valid until shutdown.
    128   context->default_file_task_runner()->PostNonNestableTask(
    129       FROM_HERE,
    130       base::Bind(&storage::WatcherManager::UnwatchEntry,
    131                  base::Unretained(watcher_manager),  // Outlives the service.
    132                  url,
    133                  base::Bind(&EntryWatcherService::OnUnwatchEntryCompleted,
    134                             weak_ptr_factory_.GetWeakPtr(),
    135                             extension_id,
    136                             url,
    137                             callback)));
    138 }
    139 
    140 std::vector<storage::FileSystemURL> EntryWatcherService::GetWatchedEntries(
    141     const std::string& extension_id) {
    142   std::vector<storage::FileSystemURL> result;
    143   for (WatcherMap::const_iterator it = watchers_.begin(); it != watchers_.end();
    144        ++it) {
    145     const std::map<std::string, EntryWatcher>::const_iterator watcher_it =
    146         it->second.find(extension_id);
    147     if (watcher_it != it->second.end())
    148       result.push_back(watcher_it->second.url);
    149   }
    150 
    151   return result;
    152 }
    153 
    154 void EntryWatcherService::OnEntryChanged(const storage::FileSystemURL& url) {
    155   const WatcherMap::const_iterator it = watchers_.find(url);
    156   DCHECK(it != watchers_.end());
    157   for (std::map<std::string, EntryWatcher>::const_iterator watcher_it =
    158            it->second.begin();
    159        watcher_it != it->second.end();
    160        ++watcher_it) {
    161     const std::string& extension_id = watcher_it->first;
    162     api::file_system::EntryChangedEvent event;
    163     dispatch_event_impl_.Run(
    164         extension_id,
    165         make_scoped_ptr(
    166             new Event(api::file_system::OnEntryChanged::kEventName,
    167                       api::file_system::OnEntryChanged::Create(event))));
    168   }
    169 }
    170 
    171 void EntryWatcherService::OnEntryRemoved(const storage::FileSystemURL& url) {
    172   WatcherMap::const_iterator it = watchers_.find(url);
    173   DCHECK(it != watchers_.end());
    174   for (std::map<std::string, EntryWatcher>::const_iterator watcher_it =
    175            it->second.begin();
    176        watcher_it != it->second.end();
    177        ++watcher_it) {
    178     const std::string& extension_id = watcher_it->first;
    179     api::file_system::EntryRemovedEvent event;
    180     dispatch_event_impl_.Run(
    181         extension_id,
    182         make_scoped_ptr(
    183             new Event(api::file_system::OnEntryRemoved::kEventName,
    184                       api::file_system::OnEntryRemoved::Create(event))));
    185   }
    186 }
    187 
    188 void EntryWatcherService::OnWatchDirectoryCompleted(
    189     storage::WatcherManager* watcher_manager,
    190     const std::string& extension_id,
    191     const storage::FileSystemURL& url,
    192     bool recursive,
    193     const storage::WatcherManager::StatusCallback& callback,
    194     base::File::Error result) {
    195   if (result != base::File::FILE_OK) {
    196     callback.Run(result);
    197     return;
    198   }
    199 
    200   storage::FileSystemContext* const context =
    201       get_file_system_context_impl_.Run(extension_id, context_);
    202   DCHECK(context);
    203 
    204   // Observe the manager if not observed yet.
    205   if (!observing_.IsObserving(watcher_manager))
    206     observing_.Add(watcher_manager);
    207 
    208   watchers_[url][extension_id] =
    209       EntryWatcher(url, true /* directory */, recursive);
    210 
    211   // TODO(mtomasz): Save in preferences.
    212 
    213   callback.Run(base::File::FILE_OK);
    214 }
    215 
    216 void EntryWatcherService::OnUnwatchEntryCompleted(
    217     const std::string& extension_id,
    218     const storage::FileSystemURL& url,
    219     const storage::WatcherManager::StatusCallback& callback,
    220     base::File::Error result) {
    221   if (result != base::File::FILE_OK) {
    222     callback.Run(result);
    223     return;
    224   }
    225 
    226   if (watchers_[url].erase(extension_id) == 0) {
    227     callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    228     return;
    229   }
    230 
    231   if (watchers_[url].empty())
    232     watchers_.erase(url);
    233 
    234   // TODO(mtomasz): Save in preferences.
    235 
    236   callback.Run(base::File::FILE_OK);
    237 }
    238 
    239 EntryWatcherService::EntryWatcher::EntryWatcher()
    240     : directory(false), recursive(false) {
    241 }
    242 
    243 EntryWatcherService::EntryWatcher::EntryWatcher(
    244     const storage::FileSystemURL& url,
    245     bool directory,
    246     bool recursive)
    247     : url(url), directory(directory), recursive(recursive) {
    248 }
    249 
    250 EntryWatcherService::EntryWatcher::~EntryWatcher() {
    251 }
    252 
    253 }  // namespace extensions
    254