Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2012 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 // GalleryWatchManager implementation. | |
| 6 | |
| 7 #include "chrome/browser/extensions/api/media_galleries_private/gallery_watch_ma nager.h" | |
| 8 | |
| 9 #include <list> | |
| 10 #include <set> | |
| 11 | |
| 12 #include "base/bind.h" | |
| 13 #include "base/compiler_specific.h" | |
| 14 #include "base/files/file_path_watcher.h" | |
| 15 #include "base/location.h" | |
| 16 #include "base/memory/weak_ptr.h" | |
| 17 #include "base/time.h" | |
| 18 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_ private_api.h" | |
| 19 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_ private_api_factory.h" | |
| 20 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_ private_event_router.h" | |
| 21 #include "chrome/browser/profiles/profile.h" | |
| 22 #include "content/public/browser/browser_thread.h" | |
| 23 | |
| 24 namespace extensions { | |
| 25 | |
| 26 class GalleryWatchManager; | |
| 27 | |
| 28 namespace { | |
| 29 | |
| 30 using content::BrowserThread; | |
| 31 | |
| 32 // Map to keep track of profile specific GalleryWatchManager objects. | |
| 33 // Key: Profile*. | |
| 34 // Value: GalleryWatchManager*. | |
| 35 // This map owns the GalleryWatchManager object. | |
| 36 typedef std::map<const Profile*, extensions::GalleryWatchManager*> | |
| 37 WatchManagerMap; | |
| 38 WatchManagerMap* g_gallery_watch_managers = NULL; | |
| 39 | |
| 40 // Dispatches the gallery changed event on the UI thread. | |
| 41 void SendGalleryChangedEventOnUIThread( | |
| 42 const Profile* profile, | |
| 43 const std::string& gallery_id, | |
| 44 const std::set<std::string>& extension_ids) { | |
| 45 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 46 DCHECK(profile); | |
| 47 MediaGalleriesPrivateEventRouter* router = | |
| 48 MediaGalleriesPrivateAPIFactory::GetForProfile( | |
| 49 const_cast<Profile*>(profile))->event_router(); | |
|
Lei Zhang
2012/12/18 01:45:16
GalleryWatchManager shouldn't promise to use a con
kmadhusu
2012/12/18 21:32:39
Done.
| |
| 50 if (!router) | |
| 51 return; | |
| 52 router->OnGalleryChanged(gallery_id, extension_ids); | |
| 53 } | |
| 54 | |
| 55 } // namespace | |
| 56 | |
| 57 /////////////////////////////////////////////////////////////////////////////// | |
| 58 // GalleryFilePathWatcher // | |
| 59 /////////////////////////////////////////////////////////////////////////////// | |
| 60 | |
| 61 // Gallery file path watcher delegate to handle the gallery change | |
|
Lei Zhang
2012/12/18 01:45:16
From this comment, I still have trouble understand
kmadhusu
2012/12/18 21:32:39
How about this?
// This class does a recursive wat
Lei Zhang
2012/12/19 01:03:47
Your comment still does not answer the question of
kmadhusu
2012/12/19 21:55:55
sure. Done.
| |
| 62 // notifications. This class manages all the extension usage and forwards | |
| 63 // the gallery change notifications to the extensions. This class lives on | |
| 64 // the FILE thread. This class is instantiated per gallery watch path and does | |
| 65 // recursive watches. | |
| 66 class GalleryFilePathWatcher : public base::RefCounted<GalleryFilePathWatcher> { | |
| 67 public: | |
| 68 GalleryFilePathWatcher(const Profile* profile, | |
| 69 const std::string& gallery_id, | |
| 70 const FilePath& path, | |
| 71 const std::string& extension_id); | |
| 72 | |
| 73 // Adds the extension specified by the |extension_id| to the | |
| 74 // ExtensionWatchInfoMap and initiate the watch operation. | |
|
Lei Zhang
2012/12/18 01:45:16
What does "initiate the watch operation" mean? How
kmadhusu
2012/12/18 21:32:39
This function adds an extension reference to the w
| |
| 75 void AddExtension(const std::string& extension_id); | |
| 76 | |
| 77 // Cancels the watch for the extension specified by the |extension_id|. | |
| 78 void RemoveExtension(const std::string& extension_id); | |
| 79 | |
| 80 // Handles the extension unloaded/uninstalled/destroyed event. | |
| 81 void OnExtensionDestroyed(const std::string& extension_id); | |
| 82 | |
| 83 // Sets up the watch operation for the specified |gallery_path_|. On | |
| 84 // success, returns true. | |
| 85 bool SetupWatch(); | |
| 86 | |
| 87 // Removes all the extension references when the browser profile is in | |
| 88 // shutdown mode. | |
| 89 void RemoveAllWatchReferences(); | |
| 90 | |
| 91 private: | |
| 92 friend class base::RefCounted<GalleryFilePathWatcher>; | |
| 93 | |
| 94 // Keeps track of extension watch details. | |
| 95 struct ExtensionWatchInfo { | |
|
Lei Zhang
2012/12/18 01:45:16
Are you keeping track of how many times a particul
kmadhusu
2012/12/18 21:32:39
Yes
| |
| 96 ExtensionWatchInfo(); | |
| 97 | |
| 98 // Number of watches in this extension, e.g "3" | |
| 99 unsigned int watch_count; | |
| 100 | |
| 101 // Used to manage the gallery changed events. | |
| 102 base::Time last_gallery_changed_event; | |
| 103 }; | |
| 104 | |
| 105 typedef std::map<std::string, ExtensionWatchInfo> ExtensionWatchInfoMap; | |
| 106 | |
| 107 // Private because GalleryFilePathWatcher is ref-counted. | |
| 108 virtual ~GalleryFilePathWatcher(); | |
| 109 | |
| 110 // FilePathWatcher callback. | |
| 111 void OnFilePathChanged(const FilePath& path, bool error); | |
| 112 | |
| 113 // Remove the watch references for the extension specified by the | |
| 114 // |extension_id|. | |
| 115 void RemoveExtensionReferences(const std::string& extension_id); | |
| 116 | |
| 117 // Current profile. | |
| 118 const Profile* profile_; | |
| 119 | |
| 120 // The gallery identifier, e.g "1". | |
| 121 const std::string gallery_id_; | |
| 122 | |
| 123 // The gallery file path watcher. | |
| 124 base::files::FilePathWatcher file_watcher_; | |
| 125 | |
| 126 // The gallery file path, e.g "C:\My Pictures". | |
| 127 FilePath gallery_path_; | |
| 128 | |
| 129 // Map to keep track of the extension and its corresponding watch count. | |
| 130 // Key: Extension identifier, e.g "qoueruoweuroiwueroiwujkshdf". | |
| 131 // Value: Watch information. | |
| 132 ExtensionWatchInfoMap extension_watch_info_map_; | |
| 133 | |
| 134 // Used to provide a weak pointer to FilePathWatcher callback. | |
| 135 base::WeakPtrFactory<GalleryFilePathWatcher> weak_ptr_factory_; | |
| 136 | |
| 137 DISALLOW_COPY_AND_ASSIGN(GalleryFilePathWatcher); | |
| 138 }; | |
| 139 | |
| 140 GalleryFilePathWatcher::GalleryFilePathWatcher(const Profile* profile, | |
| 141 const std::string& gallery_id, | |
| 142 const FilePath& path, | |
| 143 const std::string& extension_id) | |
| 144 : profile_(profile), | |
| 145 gallery_id_(gallery_id), | |
| 146 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | |
| 147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 148 gallery_path_ = path; | |
| 149 AddExtension(extension_id); | |
| 150 } | |
| 151 | |
| 152 void GalleryFilePathWatcher::AddExtension(const std::string& extension_id) { | |
| 153 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 154 ExtensionWatchInfoMap::iterator it = | |
| 155 extension_watch_info_map_.find(extension_id); | |
| 156 if (it != extension_watch_info_map_.end()) { | |
| 157 it->second.watch_count++; | |
| 158 } else { | |
| 159 extension_watch_info_map_.insert( | |
| 160 ExtensionWatchInfoMap::value_type(extension_id, ExtensionWatchInfo())); | |
| 161 } | |
| 162 AddRef(); | |
| 163 } | |
| 164 | |
| 165 void GalleryFilePathWatcher::RemoveExtension(const std::string& extension_id) { | |
| 166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 167 ExtensionWatchInfoMap::iterator it = | |
| 168 extension_watch_info_map_.find(extension_id); | |
| 169 if (it == extension_watch_info_map_.end()) | |
| 170 return; | |
| 171 // If entry found - decrease it's count and remove if necessary | |
| 172 it->second.watch_count--; | |
| 173 if (0 == it->second.watch_count) | |
| 174 extension_watch_info_map_.erase(it); | |
| 175 Release(); | |
| 176 } | |
| 177 | |
| 178 void GalleryFilePathWatcher::OnExtensionDestroyed( | |
| 179 const std::string& extension_id) { | |
| 180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 181 RemoveExtensionReferences(extension_id); | |
| 182 } | |
| 183 | |
| 184 bool GalleryFilePathWatcher::SetupWatch() { | |
| 185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 186 return file_watcher_.Watch( | |
| 187 gallery_path_, true, | |
| 188 base::Bind(&GalleryFilePathWatcher::OnFilePathChanged, | |
| 189 weak_ptr_factory_.GetWeakPtr())); | |
| 190 } | |
| 191 | |
| 192 void GalleryFilePathWatcher::RemoveAllWatchReferences() { | |
| 193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 194 std::set<std::string> extension_ids; | |
| 195 for (ExtensionWatchInfoMap::iterator iter = extension_watch_info_map_.begin(); | |
| 196 iter != extension_watch_info_map_.end(); ++iter) | |
| 197 extension_ids.insert(iter->first); | |
| 198 | |
| 199 for (std::set<std::string>::const_iterator it = extension_ids.begin(); | |
| 200 it != extension_ids.end(); ++it) | |
| 201 RemoveExtensionReferences(*it); | |
| 202 } | |
| 203 | |
| 204 GalleryFilePathWatcher::~GalleryFilePathWatcher() { | |
| 205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 206 } | |
| 207 | |
| 208 GalleryFilePathWatcher::ExtensionWatchInfo::ExtensionWatchInfo() | |
| 209 : watch_count(1) { | |
| 210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
|
Lei Zhang
2012/12/18 01:45:16
Structs should not do anything other can carry val
kmadhusu
2012/12/18 21:32:39
Done.
| |
| 211 } | |
| 212 | |
| 213 void GalleryFilePathWatcher::OnFilePathChanged(const FilePath& path, | |
| 214 bool error) { | |
| 215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 216 if (error || (path != gallery_path_)) | |
| 217 return; | |
| 218 | |
| 219 std::set<std::string> extension_ids; | |
| 220 for (ExtensionWatchInfoMap::iterator iter = extension_watch_info_map_.begin(); | |
| 221 iter != extension_watch_info_map_.end(); ++iter) { | |
| 222 if (!iter->second.last_gallery_changed_event.is_null()) { | |
| 223 // Ignore gallery change event if it is received too frequently. | |
| 224 // For example, when an user copies/deletes 1000 media files from a | |
| 225 // gallery, this callback is called 1000 times within a span of 10ms. | |
| 226 // GalleryWatchManager should not send 1000 gallery changed events to | |
| 227 // the watching extension. | |
| 228 const int kMinSecondsToIgnoreGalleryChangedEvent = 3; | |
| 229 base::TimeDelta diff = | |
| 230 base::Time::Now() - iter->second.last_gallery_changed_event; | |
| 231 if (diff.InSeconds() < kMinSecondsToIgnoreGalleryChangedEvent) | |
| 232 continue; | |
| 233 } | |
| 234 iter->second.last_gallery_changed_event = base::Time::Now(); | |
| 235 extension_ids.insert(iter->first); | |
| 236 } | |
| 237 if (!extension_ids.empty()) { | |
| 238 content::BrowserThread::PostTask( | |
| 239 content::BrowserThread::UI, FROM_HERE, | |
| 240 base::Bind(SendGalleryChangedEventOnUIThread, profile_, gallery_id_, | |
| 241 extension_ids)); | |
| 242 } | |
| 243 } | |
| 244 | |
| 245 void GalleryFilePathWatcher::RemoveExtensionReferences( | |
| 246 const std::string& extension_id) { | |
| 247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 248 ExtensionWatchInfoMap::iterator it = | |
| 249 extension_watch_info_map_.find(extension_id); | |
| 250 if (it == extension_watch_info_map_.end()) | |
| 251 return; | |
| 252 const ExtensionWatchInfo watch_info = it->second; | |
| 253 for (unsigned int i = 0; i < watch_info.watch_count; ++i) | |
| 254 Release(); | |
| 255 extension_watch_info_map_.erase(it); | |
| 256 } | |
| 257 | |
| 258 /////////////////////////////////////////////////////////////////////////////// | |
| 259 // GalleryWatchManager // | |
| 260 /////////////////////////////////////////////////////////////////////////////// | |
| 261 | |
| 262 // static | |
| 263 GalleryWatchManager* GalleryWatchManager::GetForProfile( | |
| 264 const Profile* profile) { | |
| 265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 266 DCHECK(profile); | |
| 267 bool has_watch_manager = (g_gallery_watch_managers && | |
| 268 GalleryWatchManager::HasForProfile(profile)); | |
| 269 if (!g_gallery_watch_managers) | |
| 270 g_gallery_watch_managers = new WatchManagerMap; | |
| 271 if (!has_watch_manager) | |
| 272 (*g_gallery_watch_managers)[profile] = new GalleryWatchManager(profile); | |
| 273 return (*g_gallery_watch_managers)[profile]; | |
| 274 } | |
| 275 | |
| 276 // static | |
| 277 bool GalleryWatchManager::HasForProfile(const Profile* profile) { | |
| 278 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 279 DCHECK(profile); | |
| 280 if (!g_gallery_watch_managers) | |
| 281 return false; | |
| 282 WatchManagerMap::const_iterator it = g_gallery_watch_managers->find(profile); | |
| 283 return (it != g_gallery_watch_managers->end()); | |
| 284 } | |
| 285 | |
| 286 // static | |
| 287 void GalleryWatchManager::OnProfileShutdown(const Profile* profile) { | |
| 288 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 289 DCHECK(profile); | |
| 290 if (!g_gallery_watch_managers || g_gallery_watch_managers->empty()) | |
| 291 return; | |
| 292 WatchManagerMap::iterator it = g_gallery_watch_managers->find(profile); | |
| 293 if (it == g_gallery_watch_managers->end()) | |
| 294 return; | |
| 295 delete it->second; | |
| 296 g_gallery_watch_managers->erase(it); | |
| 297 if (g_gallery_watch_managers->empty()) | |
| 298 delete g_gallery_watch_managers; | |
| 299 } | |
| 300 | |
| 301 GalleryWatchManager::~GalleryWatchManager() { | |
| 302 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 303 if (!gallery_watchers_.empty()) { | |
| 304 // User closed the extension/browser without stoping the gallery watchers. | |
| 305 DeleteAllWatchers(); | |
| 306 } | |
| 307 DCHECK(gallery_watchers_.empty()); | |
| 308 } | |
| 309 | |
| 310 bool GalleryWatchManager::StartGalleryWatch( | |
| 311 const std::string& gallery_id, | |
| 312 const FilePath& watch_path, | |
| 313 const std::string& extension_id) { | |
| 314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 315 WatcherMap::const_iterator iter = gallery_watchers_.find(watch_path); | |
| 316 if (iter != gallery_watchers_.end()) { | |
| 317 // Already watched. | |
| 318 iter->second->AddExtension(extension_id); | |
| 319 return true; | |
| 320 } | |
| 321 | |
| 322 // Need to add a new watcher. | |
| 323 scoped_refptr<GalleryFilePathWatcher> watch( | |
| 324 new GalleryFilePathWatcher(profile_, gallery_id, watch_path, | |
| 325 extension_id)); | |
| 326 if (!watch->SetupWatch()) | |
| 327 return false; | |
| 328 gallery_watchers_[watch_path] = watch; | |
| 329 return true; | |
| 330 } | |
| 331 | |
| 332 void GalleryWatchManager::StopGalleryWatch( | |
| 333 const FilePath& watch_path, | |
| 334 const std::string& extension_id) { | |
| 335 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 336 WatcherMap::iterator iter = gallery_watchers_.find(watch_path); | |
| 337 if (iter == gallery_watchers_.end()) | |
| 338 return; | |
| 339 // Remove the renderer process for this watch. | |
| 340 iter->second->RemoveExtension(extension_id); | |
| 341 if (iter->second->HasOneRef()) { | |
| 342 // There are no references other than the one |gallery_watchers_| holds. | |
| 343 iter->second = NULL; | |
| 344 gallery_watchers_.erase(iter); | |
| 345 } | |
| 346 } | |
| 347 | |
| 348 void GalleryWatchManager::OnExtensionDestroyed( | |
| 349 const std::string& extension_id) { | |
| 350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 351 std::list<FilePath> watchers_to_erase; | |
| 352 for (WatcherMap::iterator iter = gallery_watchers_.begin(); | |
| 353 iter != gallery_watchers_.end(); ++iter) { | |
| 354 // Remove the renderer process for this watch. | |
| 355 iter->second->OnExtensionDestroyed(extension_id); | |
| 356 if (iter->second->HasOneRef()) { | |
| 357 // There are no references other than the one |gallery_watchers_| holds. | |
| 358 watchers_to_erase.push_back(iter->first); | |
| 359 } | |
| 360 } | |
| 361 | |
| 362 for (std::list<FilePath>::const_iterator path = watchers_to_erase.begin(); | |
| 363 path != watchers_to_erase.end(); ++path) { | |
| 364 WatcherMap::iterator iter = gallery_watchers_.find(*path); | |
| 365 DCHECK(iter != gallery_watchers_.end()); | |
| 366 iter->second = NULL; | |
| 367 gallery_watchers_.erase(iter); | |
| 368 } | |
| 369 } | |
| 370 | |
| 371 GalleryWatchManager::GalleryWatchManager(const Profile* profile) | |
| 372 : profile_(profile) { | |
| 373 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 374 } | |
| 375 | |
| 376 void GalleryWatchManager::DeleteAllWatchers() { | |
| 377 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); | |
| 378 std::list<FilePath> watchers_to_erase; | |
| 379 for (WatcherMap::iterator iter = gallery_watchers_.begin(); | |
| 380 iter != gallery_watchers_.end(); ++iter) { | |
| 381 iter->second->RemoveAllWatchReferences(); | |
| 382 // Verify that there are no references other than the one | |
| 383 // |gallery_watchers_| holds. | |
| 384 DCHECK(iter->second->HasOneRef()); | |
| 385 watchers_to_erase.push_back(iter->first); | |
| 386 } | |
| 387 | |
| 388 for (std::list<FilePath>::const_iterator path = watchers_to_erase.begin(); | |
| 389 path != watchers_to_erase.end(); ++path) { | |
| 390 WatcherMap::iterator iter = gallery_watchers_.find(*path); | |
| 391 DCHECK(iter != gallery_watchers_.end()); | |
| 392 iter->second = NULL; | |
| 393 gallery_watchers_.erase(iter); | |
| 394 } | |
| 395 } | |
| 396 | |
| 397 } // namespace extensions | |
| OLD | NEW |