| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |