Chromium Code Reviews| 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/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/storage_partition.h" | |
| 14 #include "extensions/browser/event_router.h" | |
| 15 #include "webkit/browser/fileapi/file_system_context.h" | |
| 16 | |
| 17 namespace extensions { | |
| 18 namespace { | |
| 19 | |
| 20 // Default implementation for dispatching an event. Can be replaced for unit | |
| 21 // tests by EntryWatcherService::SetDispatchEventImplForTesting(). | |
| 22 bool DispatchEventImpl(extensions::EventRouter* event_router, | |
| 23 const std::string& extension_id, | |
| 24 scoped_ptr<extensions::Event> event) { | |
| 25 if (!event_router->ExtensionHasEventListener(extension_id, event->event_name)) | |
|
benwells
2014/08/13 05:50:47
Is it necessary to check this and return a bool?
mtomasz
2014/08/13 07:02:08
Right. Removed.
| |
| 26 return false; | |
| 27 | |
| 28 event_router->DispatchEventToExtension(extension_id, event.Pass()); | |
| 29 return true; | |
| 30 } | |
| 31 | |
| 32 // Default implementation for acquiring a file system context for a specific | |
| 33 // |extension_id| and |context|. | |
| 34 fileapi::FileSystemContext* GetFileSystemContextImpl( | |
| 35 const std::string& extension_id, | |
| 36 content::BrowserContext* context) { | |
| 37 const GURL site = | |
| 38 extensions::util::GetSiteForExtensionId(extension_id, context); | |
| 39 return content::BrowserContext::GetStoragePartitionForSite(context, site) | |
| 40 ->GetFileSystemContext(); | |
| 41 } | |
| 42 | |
| 43 } // namespace | |
| 44 | |
| 45 EntryWatcherService::EntryWatcherService(content::BrowserContext* context) | |
| 46 : context_(context), | |
| 47 dispatch_event_impl_(base::Bind(&DispatchEventImpl, | |
| 48 extensions::EventRouter::Get(context))), | |
| 49 get_file_system_context_impl_(base::Bind(&GetFileSystemContextImpl)), | |
| 50 observing_(this), | |
| 51 weak_ptr_factory_(this) { | |
| 52 // TODO(mtomasz): Restore persistent watchers. | |
| 53 } | |
| 54 | |
| 55 EntryWatcherService::~EntryWatcherService() { | |
| 56 } | |
| 57 | |
| 58 void EntryWatcherService::SetDispatchEventImplForTesting( | |
| 59 const DispatchEventImplCallback& callback) { | |
| 60 dispatch_event_impl_ = callback; | |
| 61 } | |
| 62 | |
| 63 void EntryWatcherService::SetGetFileSystemContextImplForTesting( | |
| 64 const GetFileSystemContextImplCallback& callback) { | |
| 65 get_file_system_context_impl_ = callback; | |
| 66 } | |
| 67 | |
| 68 void EntryWatcherService::WatchDirectory( | |
| 69 const std::string& extension_id, | |
| 70 const fileapi::FileSystemURL& url, | |
| 71 bool recursive, | |
| 72 const fileapi::WatcherManager::StatusCallback& callback) { | |
| 73 // TODO(mtomasz): Add support for recursive watchers. | |
| 74 if (recursive) { | |
| 75 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 76 FROM_HERE, | |
| 77 base::Bind(callback, base::File::FILE_ERROR_INVALID_OPERATION)); | |
| 78 return; | |
| 79 } | |
| 80 | |
| 81 fileapi::FileSystemContext* const context = | |
| 82 get_file_system_context_impl_.Run(extension_id, context_); | |
| 83 DCHECK(context); | |
| 84 | |
| 85 fileapi::WatcherManager* const watcher_manager = | |
| 86 context->GetWatcherManager(url.type()); | |
| 87 if (!watcher_manager) { | |
| 88 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
|
benwells
2014/08/13 05:50:47
Why do you post this and not just run it immediate
mtomasz
2014/08/13 07:02:09
Several times during reviews I was asked to post c
benwells
2014/08/14 05:16:32
OK, makes sense. Could you add a comment explainin
mtomasz
2014/08/15 05:35:57
Done.
| |
| 89 FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_SECURITY)); | |
|
benwells
2014/08/13 05:50:47
Why is this a security error?
mtomasz
2014/08/13 07:02:09
Changed to base::File::FILE_ERROR_INVALID_OPERATIO
| |
| 90 return; | |
| 91 } | |
| 92 | |
| 93 watcher_manager->WatchDirectory( | |
| 94 url, | |
| 95 recursive, | |
| 96 base::Bind(&EntryWatcherService::OnWatchDirectoryCompleted, | |
| 97 weak_ptr_factory_.GetWeakPtr(), | |
| 98 extension_id, | |
| 99 url, | |
| 100 recursive, | |
| 101 callback)); | |
| 102 } | |
| 103 | |
| 104 void EntryWatcherService::UnwatchEntry( | |
| 105 const std::string& extension_id, | |
| 106 const fileapi::FileSystemURL& url, | |
| 107 const fileapi::WatcherManager::StatusCallback& callback) { | |
| 108 fileapi::FileSystemContext* const context = | |
| 109 get_file_system_context_impl_.Run(extension_id, context_); | |
| 110 DCHECK(context); | |
| 111 | |
| 112 fileapi::WatcherManager* const watcher_manager = | |
| 113 context->GetWatcherManager(url.type()); | |
| 114 if (!watcher_manager) { | |
| 115 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 116 FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_SECURITY)); | |
| 117 return; | |
| 118 } | |
| 119 | |
| 120 watcher_manager->UnwatchEntry( | |
| 121 url, | |
| 122 base::Bind(&EntryWatcherService::OnUnwatchEntryCompleted, | |
| 123 weak_ptr_factory_.GetWeakPtr(), | |
| 124 extension_id, | |
| 125 url, | |
| 126 callback)); | |
| 127 } | |
| 128 | |
| 129 std::vector<fileapi::FileSystemURL> EntryWatcherService::GetWatchedEntries( | |
| 130 const std::string& extension_id) { | |
| 131 std::vector<fileapi::FileSystemURL> result; | |
| 132 for (WatcherMap::const_iterator it = watchers_.begin(); it != watchers_.end(); | |
| 133 ++it) { | |
| 134 const std::map<std::string, EntryWatcher>::const_iterator watcher_it = | |
| 135 it->second.find(extension_id); | |
| 136 if (watcher_it != it->second.end()) | |
| 137 result.push_back(watcher_it->second.url); | |
| 138 } | |
| 139 | |
| 140 return result; | |
| 141 } | |
| 142 | |
| 143 void EntryWatcherService::OnEntryChanged(const fileapi::FileSystemURL& url) { | |
| 144 const WatcherMap::const_iterator it = watchers_.find(url.ToGURL()); | |
| 145 DCHECK(it != watchers_.end()); | |
| 146 for (std::map<std::string, EntryWatcher>::const_iterator watcher_it = | |
| 147 it->second.begin(); | |
| 148 watcher_it != it->second.end(); | |
| 149 ++watcher_it) { | |
| 150 const std::string& extension_id = watcher_it->first; | |
| 151 api::file_system::EntryChangedEvent event; | |
| 152 dispatch_event_impl_.Run( | |
| 153 extension_id, | |
| 154 make_scoped_ptr( | |
| 155 new Event(api::file_system::OnEntryChanged::kEventName, | |
| 156 api::file_system::OnEntryChanged::Create(event)))); | |
| 157 } | |
| 158 } | |
| 159 | |
| 160 void EntryWatcherService::OnEntryRemoved(const fileapi::FileSystemURL& url) { | |
| 161 WatcherMap::const_iterator it = watchers_.find(url.ToGURL()); | |
| 162 DCHECK(it != watchers_.end()); | |
| 163 for (std::map<std::string, EntryWatcher>::const_iterator watcher_it = | |
| 164 it->second.begin(); | |
| 165 watcher_it != it->second.end(); | |
| 166 ++watcher_it) { | |
| 167 const std::string& extension_id = watcher_it->first; | |
| 168 api::file_system::EntryRemovedEvent event; | |
| 169 dispatch_event_impl_.Run( | |
| 170 extension_id, | |
| 171 make_scoped_ptr( | |
| 172 new Event(api::file_system::OnEntryRemoved::kEventName, | |
| 173 api::file_system::OnEntryRemoved::Create(event)))); | |
| 174 } | |
| 175 } | |
| 176 | |
| 177 void EntryWatcherService::OnWatchDirectoryCompleted( | |
| 178 const std::string& extension_id, | |
| 179 const fileapi::FileSystemURL& url, | |
| 180 bool recursive, | |
| 181 const fileapi::WatcherManager::StatusCallback& callback, | |
| 182 base::File::Error result) { | |
| 183 if (result != base::File::FILE_OK) { | |
| 184 callback.Run(result); | |
| 185 return; | |
| 186 } | |
| 187 | |
| 188 DCHECK_EQ(base::File::FILE_OK, result); | |
|
benwells
2014/08/13 05:50:47
Is this DCHECK necessary?
mtomasz
2014/08/13 07:02:09
Removed.
| |
| 189 | |
| 190 fileapi::FileSystemContext* const context = | |
| 191 get_file_system_context_impl_.Run(extension_id, context_); | |
| 192 DCHECK(context); | |
| 193 | |
| 194 fileapi::WatcherManager* const watcher_manager = | |
| 195 context->GetWatcherManager(url.type()); | |
| 196 if (!watcher_manager) { | |
| 197 callback.Run(base::File::FILE_ERROR_SECURITY); | |
|
benwells
2014/08/13 05:50:47
Given that you should have already successfully go
mtomasz
2014/08/13 07:02:09
GetWatcherManager() may return NULL at any point.
benwells
2014/08/14 05:16:32
That seems odd, are you thinking about the cases w
mtomasz
2014/08/15 05:35:56
I don't think it's weird. I can imagine a backend
mtomasz
2014/08/15 05:35:57
I reorganized the code. How about now?
benwells
2014/08/18 22:58:32
I think that is better, but the lifetime issues ar
mtomasz
2014/08/19 06:50:36
Done.
| |
| 198 return; | |
| 199 } | |
| 200 | |
| 201 // Observe the manager if not observed yet. | |
| 202 if (!observing_.IsObserving(watcher_manager)) | |
| 203 observing_.Add(watcher_manager); | |
| 204 | |
| 205 const GURL gurl = url.ToGURL(); | |
| 206 watchers_[gurl][extension_id] = | |
| 207 EntryWatcher(url, true /* directory */, recursive); | |
| 208 | |
| 209 // TODO(mtomasz): Save in preferences. | |
| 210 | |
| 211 callback.Run(base::File::FILE_OK); | |
| 212 } | |
| 213 | |
| 214 void EntryWatcherService::OnUnwatchEntryCompleted( | |
| 215 const std::string& extension_id, | |
| 216 const fileapi::FileSystemURL& url, | |
| 217 const fileapi::WatcherManager::StatusCallback& callback, | |
| 218 base::File::Error result) { | |
| 219 if (result != base::File::FILE_OK) { | |
| 220 callback.Run(result); | |
| 221 return; | |
| 222 } | |
| 223 | |
| 224 DCHECK_EQ(base::File::FILE_OK, result); | |
| 225 | |
| 226 const GURL gurl = url.ToGURL(); | |
| 227 if (watchers_[gurl].erase(extension_id) == 0) { | |
| 228 callback.Run(base::File::FILE_ERROR_NOT_FOUND); | |
| 229 return; | |
| 230 } | |
| 231 | |
| 232 if (watchers_[gurl].empty()) | |
| 233 watchers_.erase(gurl); | |
| 234 | |
| 235 fileapi::FileSystemContext* const context = | |
| 236 get_file_system_context_impl_.Run(extension_id, context_); | |
| 237 DCHECK(context); | |
| 238 | |
| 239 fileapi::WatcherManager* const watcher_manager = | |
|
benwells
2014/08/13 05:50:47
Why do you get the watcher manager here?
mtomasz
2014/08/13 07:02:09
Not needed. Removed.
| |
| 240 context->GetWatcherManager(url.type()); | |
| 241 if (!watcher_manager) { | |
| 242 callback.Run(base::File::FILE_ERROR_SECURITY); | |
| 243 return; | |
| 244 } | |
| 245 | |
| 246 // TODO(mtomasz): Save in preferences. | |
| 247 | |
| 248 callback.Run(base::File::FILE_OK); | |
| 249 } | |
| 250 | |
| 251 EntryWatcherService::EntryWatcher::EntryWatcher() | |
| 252 : directory(false), recursive(false) { | |
| 253 } | |
| 254 | |
| 255 EntryWatcherService::EntryWatcher::EntryWatcher( | |
| 256 const fileapi::FileSystemURL& url, | |
| 257 bool directory, | |
| 258 bool recursive) | |
| 259 : url(url), directory(directory), recursive(recursive) { | |
| 260 } | |
| 261 | |
| 262 EntryWatcherService::EntryWatcher::~EntryWatcher() { | |
| 263 } | |
| 264 | |
| 265 // static | |
| 266 EntryWatcherService* EntryWatcherServiceFactory::Get( | |
| 267 content::BrowserContext* context) { | |
| 268 return static_cast<EntryWatcherService*>( | |
| 269 GetInstance()->GetServiceForBrowserContext(context, true)); | |
| 270 } | |
| 271 | |
| 272 // static | |
| 273 EntryWatcherService* EntryWatcherServiceFactory::FindExisting( | |
| 274 content::BrowserContext* context) { | |
| 275 return static_cast<EntryWatcherService*>( | |
| 276 GetInstance()->GetServiceForBrowserContext(context, false)); | |
| 277 } | |
| 278 | |
| 279 EntryWatcherServiceFactory* EntryWatcherServiceFactory::GetInstance() { | |
| 280 return Singleton<EntryWatcherServiceFactory>::get(); | |
| 281 } | |
| 282 | |
| 283 EntryWatcherServiceFactory::EntryWatcherServiceFactory() | |
| 284 : BrowserContextKeyedServiceFactory( | |
| 285 "EntryWatcherService", | |
| 286 BrowserContextDependencyManager::GetInstance()) { | |
| 287 } | |
| 288 | |
| 289 EntryWatcherServiceFactory::~EntryWatcherServiceFactory() { | |
| 290 } | |
| 291 | |
| 292 KeyedService* EntryWatcherServiceFactory::BuildServiceInstanceFor( | |
| 293 content::BrowserContext* context) const { | |
| 294 return new EntryWatcherService(Profile::FromBrowserContext(context)); | |
| 295 } | |
| 296 | |
| 297 bool EntryWatcherServiceFactory::ServiceIsCreatedWithBrowserContext() const { | |
| 298 return true; | |
|
benwells
2014/08/13 05:50:47
Could you add a comment about why this is needed?
mtomasz
2014/08/13 07:02:09
Done.
| |
| 299 } | |
| 300 | |
| 301 } // namespace extensions | |
| OLD | NEW |