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.
5 // GalleryWatchManager implementation.
7 #include "chrome/browser/extensions/api/media_galleries_private/gallery_watch_manager.h"
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/compiler_specific.h"
15 #include "base/files/file_path_watcher.h"
16 #include "base/location.h"
17 #include "base/memory/ref_counted.h"
18 #include "base/stl_util.h"
19 #include "base/time/time.h"
20 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_event_router.h"
21 #include "content/public/browser/browser_thread.h"
23 namespace extensions {
27 using content::BrowserThread;
29 // Map to keep track of profile specific GalleryWatchManager objects.
30 // Key: Profile identifier.
31 // Value: GalleryWatchManager*.
32 // This map owns the GalleryWatchManager object.
33 typedef std::map<void*, extensions::GalleryWatchManager*> WatchManagerMap;
34 WatchManagerMap* g_gallery_watch_managers = NULL;
36 // Dispatches the gallery changed event on the UI thread.
37 void SendGalleryChangedEventOnUIThread(
38 base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router,
39 MediaGalleryPrefId gallery_id,
40 const std::set<std::string>& extension_ids) {
41 DCHECK_CURRENTLY_ON(BrowserThread::UI);
42 if (event_router.get())
43 event_router->OnGalleryChanged(gallery_id, extension_ids);
48 ///////////////////////////////////////////////////////////////////////////////
49 // GalleryWatchManager::GalleryFilePathWatcher //
50 ///////////////////////////////////////////////////////////////////////////////
52 // This class does a recursive watch on the gallery file path and holds a list
53 // of extensions that are watching the gallery. When there is a file system
54 // activity within the gallery, GalleryFilePathWatcher notifies the interested
55 // extensions. This class lives on the file thread.
56 class GalleryWatchManager::GalleryFilePathWatcher
57 : public base::RefCounted<GalleryFilePathWatcher> {
59 // |on_destroyed_callback| is called when the last GalleryFilePathWatcher
60 // reference goes away.
61 GalleryFilePathWatcher(
62 base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router,
63 MediaGalleryPrefId gallery_id,
64 const base::FilePath& path,
65 const std::string& extension_id,
66 const base::Closure& on_destroyed_callback);
68 // Adds the extension reference to the watched gallery.
69 void AddExtension(const std::string& extension_id);
71 // Removes the extension reference to the watched gallery.
72 void RemoveExtension(const std::string& extension_id);
74 // Handles the extension unloaded/uninstalled event.
75 void OnExtensionUnloaded(const std::string& extension_id);
77 // Sets up the watch operation for the specified |gallery_path_|. On
78 // success, returns true.
81 // Removes all the extension references when the browser profile is in
83 void RemoveAllWatchReferences();
86 friend class base::RefCounted<GalleryFilePathWatcher>;
88 // Key: Extension identifier, e.g "qoueruoweuroiwueroiwujkshdf".
89 // Value: Time at which the last gallery changed event is dispatched.
90 // Initialized to null Time value.
91 typedef std::map<std::string, base::Time> ExtensionWatchInfoMap;
93 // Private because GalleryFilePathWatcher is ref-counted.
94 virtual ~GalleryFilePathWatcher();
96 // FilePathWatcher callback.
97 void OnFilePathChanged(const base::FilePath& path, bool error);
99 // Remove the watch references for the extension specified by the
101 void RemoveExtensionReferences(const std::string& extension_id);
103 // Used to notify the interested extensions about the gallery changed event.
104 base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router_;
106 // The gallery identifier, e.g "1".
107 MediaGalleryPrefId gallery_id_;
109 // The gallery file path watcher.
110 base::FilePathWatcher file_watcher_;
112 // The gallery file path, e.g "C:\My Pictures".
113 base::FilePath gallery_path_;
115 // A callback to call when |this| object is destroyed.
116 base::Closure on_destroyed_callback_;
118 // Map to keep track of the extension and its corresponding watch count.
119 ExtensionWatchInfoMap extension_watch_info_map_;
121 // Used to provide a weak pointer to FilePathWatcher callback.
122 base::WeakPtrFactory<GalleryFilePathWatcher> weak_ptr_factory_;
124 DISALLOW_COPY_AND_ASSIGN(GalleryFilePathWatcher);
127 GalleryWatchManager::GalleryFilePathWatcher::GalleryFilePathWatcher(
128 base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router,
129 MediaGalleryPrefId gallery_id,
130 const base::FilePath& path,
131 const std::string& extension_id,
132 const base::Closure& on_destroyed_callback)
133 : event_router_(event_router),
134 gallery_id_(gallery_id),
135 on_destroyed_callback_(on_destroyed_callback),
136 weak_ptr_factory_(this) {
137 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
138 gallery_path_ = path;
139 AddExtension(extension_id);
142 void GalleryWatchManager::GalleryFilePathWatcher::AddExtension(
143 const std::string& extension_id) {
144 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
145 if (ContainsKey(extension_watch_info_map_, extension_id))
147 extension_watch_info_map_[extension_id] = base::Time();
151 void GalleryWatchManager::GalleryFilePathWatcher::RemoveExtension(
152 const std::string& extension_id) {
153 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
154 if (extension_watch_info_map_.erase(extension_id) == 1)
158 void GalleryWatchManager::GalleryFilePathWatcher::OnExtensionUnloaded(
159 const std::string& extension_id) {
160 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
161 RemoveExtensionReferences(extension_id);
164 bool GalleryWatchManager::GalleryFilePathWatcher::SetupWatch() {
165 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
166 return file_watcher_.Watch(
168 base::Bind(&GalleryFilePathWatcher::OnFilePathChanged,
169 weak_ptr_factory_.GetWeakPtr()));
172 void GalleryWatchManager::GalleryFilePathWatcher::RemoveAllWatchReferences() {
173 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
174 std::set<std::string> extension_ids;
175 for (ExtensionWatchInfoMap::iterator iter = extension_watch_info_map_.begin();
176 iter != extension_watch_info_map_.end(); ++iter)
177 extension_ids.insert(iter->first);
179 for (std::set<std::string>::const_iterator it = extension_ids.begin();
180 it != extension_ids.end(); ++it)
181 RemoveExtensionReferences(*it);
184 GalleryWatchManager::GalleryFilePathWatcher::~GalleryFilePathWatcher() {
185 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
186 on_destroyed_callback_.Run();
189 void GalleryWatchManager::GalleryFilePathWatcher::OnFilePathChanged(
190 const base::FilePath& path,
192 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
193 if (error || (path != gallery_path_))
196 std::set<std::string> extension_ids;
197 for (ExtensionWatchInfoMap::iterator iter = extension_watch_info_map_.begin();
198 iter != extension_watch_info_map_.end(); ++iter) {
199 if (!iter->second.is_null()) {
200 // Ignore gallery change event if it is received too frequently.
201 // For example, when an user copies/deletes 1000 media files from a
202 // gallery, this callback is called 1000 times within a span of 10ms.
203 // GalleryWatchManager should not send 1000 gallery changed events to
204 // the watching extension.
205 const int kMinSecondsToIgnoreGalleryChangedEvent = 3;
206 base::TimeDelta diff = base::Time::Now() - iter->second;
207 if (diff.InSeconds() < kMinSecondsToIgnoreGalleryChangedEvent)
210 iter->second = base::Time::Now();
211 extension_ids.insert(iter->first);
213 if (!extension_ids.empty()) {
214 content::BrowserThread::PostTask(
215 content::BrowserThread::UI, FROM_HERE,
216 base::Bind(SendGalleryChangedEventOnUIThread, event_router_,
217 gallery_id_, extension_ids));
221 void GalleryWatchManager::GalleryFilePathWatcher::RemoveExtensionReferences(
222 const std::string& extension_id) {
223 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
224 ExtensionWatchInfoMap::iterator it =
225 extension_watch_info_map_.find(extension_id);
226 if (it == extension_watch_info_map_.end())
228 extension_watch_info_map_.erase(it);
232 ///////////////////////////////////////////////////////////////////////////////
233 // GalleryWatchManager //
234 ///////////////////////////////////////////////////////////////////////////////
237 GalleryWatchManager* GalleryWatchManager::GetForProfile(
239 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
241 bool has_watch_manager = (g_gallery_watch_managers &&
242 GalleryWatchManager::HasForProfile(profile_id));
243 if (!g_gallery_watch_managers)
244 g_gallery_watch_managers = new WatchManagerMap;
245 if (!has_watch_manager)
246 (*g_gallery_watch_managers)[profile_id] = new GalleryWatchManager;
247 return (*g_gallery_watch_managers)[profile_id];
251 bool GalleryWatchManager::HasForProfile(void* profile_id) {
252 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
254 if (!g_gallery_watch_managers)
256 WatchManagerMap::const_iterator it =
257 g_gallery_watch_managers->find(profile_id);
258 return (it != g_gallery_watch_managers->end());
262 void GalleryWatchManager::OnProfileShutdown(void* profile_id) {
263 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
265 if (!g_gallery_watch_managers || g_gallery_watch_managers->empty())
267 WatchManagerMap::iterator it = g_gallery_watch_managers->find(profile_id);
268 if (it == g_gallery_watch_managers->end())
271 g_gallery_watch_managers->erase(it);
272 if (g_gallery_watch_managers->empty()) {
273 delete g_gallery_watch_managers;
274 g_gallery_watch_managers = NULL;
279 bool GalleryWatchManager::SetupGalleryWatch(
281 MediaGalleryPrefId gallery_id,
282 const base::FilePath& watch_path,
283 const std::string& extension_id,
284 base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router) {
285 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
286 return GalleryWatchManager::GetForProfile(profile_id)->StartGalleryWatch(
287 gallery_id, watch_path, extension_id, event_router);
291 void GalleryWatchManager::RemoveGalleryWatch(void* profile_id,
292 const base::FilePath& watch_path,
293 const std::string& extension_id) {
294 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
295 if (!GalleryWatchManager::HasForProfile(profile_id))
297 GalleryWatchManager::GetForProfile(profile_id)->StopGalleryWatch(
298 watch_path, extension_id);
301 void GalleryWatchManager::OnExtensionUnloaded(void* profile_id,
302 const std::string& extension_id) {
303 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
304 if (!GalleryWatchManager::HasForProfile(profile_id))
306 GalleryWatchManager::GetForProfile(profile_id)->HandleExtensionUnloadedEvent(
310 GalleryWatchManager::GalleryWatchManager() {
311 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
314 GalleryWatchManager::~GalleryWatchManager() {
315 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
319 bool GalleryWatchManager::StartGalleryWatch(
320 MediaGalleryPrefId gallery_id,
321 const base::FilePath& watch_path,
322 const std::string& extension_id,
323 base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router) {
324 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
325 WatcherMap::const_iterator iter = gallery_watchers_.find(watch_path);
326 if (iter != gallery_watchers_.end()) {
328 iter->second->AddExtension(extension_id);
332 // Need to add a new watcher.
333 scoped_refptr<GalleryFilePathWatcher> watch(
334 new GalleryFilePathWatcher(
335 event_router, gallery_id, watch_path, extension_id,
336 base::Bind(&GalleryWatchManager::RemoveGalleryFilePathWatcherEntry,
337 base::Unretained(this),
339 if (!watch->SetupWatch())
341 gallery_watchers_[watch_path] = watch.get();
345 void GalleryWatchManager::StopGalleryWatch(
346 const base::FilePath& watch_path,
347 const std::string& extension_id) {
348 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
349 WatcherMap::iterator iter = gallery_watchers_.find(watch_path);
350 if (iter == gallery_watchers_.end())
352 // Remove the renderer process for this watch.
353 iter->second->RemoveExtension(extension_id);
356 void GalleryWatchManager::HandleExtensionUnloadedEvent(
357 const std::string& extension_id) {
358 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
359 std::list<base::FilePath> watchers_to_notify;
360 for (WatcherMap::iterator iter = gallery_watchers_.begin();
361 iter != gallery_watchers_.end(); ++iter)
362 watchers_to_notify.push_back(iter->first);
364 for (std::list<base::FilePath>::const_iterator path =
365 watchers_to_notify.begin();
366 path != watchers_to_notify.end(); ++path) {
367 WatcherMap::iterator iter = gallery_watchers_.find(*path);
368 if (iter == gallery_watchers_.end())
370 iter->second->OnExtensionUnloaded(extension_id);
374 void GalleryWatchManager::DeleteAllWatchers() {
375 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
376 if (gallery_watchers_.empty())
379 // Create a copy of |gallery_watchers_| to delete because
380 // GalleryFilePathWatcher::RemoveAllWatchReferences will
381 // eventually call GalleryWatchManager::RemoveGalleryFilePathWatcherEntry()
382 // and modify |gallery_watchers_|.
383 WatcherMap watchers_to_delete(gallery_watchers_);
384 for (WatcherMap::const_iterator iter = watchers_to_delete.begin();
385 iter != watchers_to_delete.end(); ++iter)
386 iter->second->RemoveAllWatchReferences();
389 void GalleryWatchManager::RemoveGalleryFilePathWatcherEntry(
390 const base::FilePath& watch_path) {
391 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
392 gallery_watchers_.erase(watch_path);
395 } // namespace extensions