Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / media_galleries_private / gallery_watch_manager.cc
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_manager.h"
8
9 #include <list>
10 #include <set>
11
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"
22
23 namespace extensions {
24
25 namespace {
26
27 using content::BrowserThread;
28
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;
35
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);
44 }
45
46 }  // namespace
47
48 ///////////////////////////////////////////////////////////////////////////////
49 //            GalleryWatchManager::GalleryFilePathWatcher                    //
50 ///////////////////////////////////////////////////////////////////////////////
51
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> {
58  public:
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);
67
68   // Adds the extension reference to the watched gallery.
69   void AddExtension(const std::string& extension_id);
70
71   // Removes the extension reference to the watched gallery.
72   void RemoveExtension(const std::string& extension_id);
73
74   // Handles the extension unloaded/uninstalled event.
75   void OnExtensionUnloaded(const std::string& extension_id);
76
77   // Sets up the watch operation for the specified |gallery_path_|. On
78   // success, returns true.
79   bool SetupWatch();
80
81   // Removes all the extension references when the browser profile is in
82   // shutdown mode.
83   void RemoveAllWatchReferences();
84
85  private:
86   friend class base::RefCounted<GalleryFilePathWatcher>;
87
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;
92
93   // Private because GalleryFilePathWatcher is ref-counted.
94   virtual ~GalleryFilePathWatcher();
95
96   // FilePathWatcher callback.
97   void OnFilePathChanged(const base::FilePath& path, bool error);
98
99   // Remove the watch references for the extension specified by the
100   // |extension_id|.
101   void RemoveExtensionReferences(const std::string& extension_id);
102
103   // Used to notify the interested extensions about the gallery changed event.
104   base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router_;
105
106   // The gallery identifier, e.g "1".
107   MediaGalleryPrefId gallery_id_;
108
109   // The gallery file path watcher.
110   base::FilePathWatcher file_watcher_;
111
112   // The gallery file path, e.g "C:\My Pictures".
113   base::FilePath gallery_path_;
114
115   // A callback to call when |this| object is destroyed.
116   base::Closure on_destroyed_callback_;
117
118   // Map to keep track of the extension and its corresponding watch count.
119   ExtensionWatchInfoMap extension_watch_info_map_;
120
121   // Used to provide a weak pointer to FilePathWatcher callback.
122   base::WeakPtrFactory<GalleryFilePathWatcher> weak_ptr_factory_;
123
124   DISALLOW_COPY_AND_ASSIGN(GalleryFilePathWatcher);
125 };
126
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);
140 }
141
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))
146     return;
147   extension_watch_info_map_[extension_id] = base::Time();
148   AddRef();
149 }
150
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)
155     Release();
156 }
157
158 void GalleryWatchManager::GalleryFilePathWatcher::OnExtensionUnloaded(
159     const std::string& extension_id) {
160   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
161   RemoveExtensionReferences(extension_id);
162 }
163
164 bool GalleryWatchManager::GalleryFilePathWatcher::SetupWatch() {
165   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
166   return file_watcher_.Watch(
167       gallery_path_, true,
168       base::Bind(&GalleryFilePathWatcher::OnFilePathChanged,
169                  weak_ptr_factory_.GetWeakPtr()));
170 }
171
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);
178
179   for (std::set<std::string>::const_iterator it = extension_ids.begin();
180        it != extension_ids.end(); ++it)
181     RemoveExtensionReferences(*it);
182 }
183
184 GalleryWatchManager::GalleryFilePathWatcher::~GalleryFilePathWatcher() {
185   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
186   on_destroyed_callback_.Run();
187 }
188
189 void GalleryWatchManager::GalleryFilePathWatcher::OnFilePathChanged(
190     const base::FilePath& path,
191     bool error) {
192   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
193   if (error || (path != gallery_path_))
194     return;
195
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)
208         continue;
209     }
210     iter->second = base::Time::Now();
211     extension_ids.insert(iter->first);
212   }
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));
218   }
219 }
220
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())
227     return;
228   extension_watch_info_map_.erase(it);
229   Release();
230 }
231
232 ///////////////////////////////////////////////////////////////////////////////
233 //                       GalleryWatchManager                                 //
234 ///////////////////////////////////////////////////////////////////////////////
235
236 // static
237 GalleryWatchManager* GalleryWatchManager::GetForProfile(
238     void* profile_id) {
239   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
240   DCHECK(profile_id);
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];
248 }
249
250 // static
251 bool GalleryWatchManager::HasForProfile(void* profile_id) {
252   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
253   DCHECK(profile_id);
254   if (!g_gallery_watch_managers)
255     return false;
256   WatchManagerMap::const_iterator it =
257       g_gallery_watch_managers->find(profile_id);
258   return (it != g_gallery_watch_managers->end());
259 }
260
261 // static
262 void GalleryWatchManager::OnProfileShutdown(void* profile_id) {
263   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
264   DCHECK(profile_id);
265   if (!g_gallery_watch_managers || g_gallery_watch_managers->empty())
266     return;
267   WatchManagerMap::iterator it = g_gallery_watch_managers->find(profile_id);
268   if (it == g_gallery_watch_managers->end())
269     return;
270   delete it->second;
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;
275   }
276 }
277
278 // static
279 bool GalleryWatchManager::SetupGalleryWatch(
280     void* profile_id,
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);
288 }
289
290 // static
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))
296     return;
297   GalleryWatchManager::GetForProfile(profile_id)->StopGalleryWatch(
298       watch_path, extension_id);
299 }
300
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))
305     return;
306   GalleryWatchManager::GetForProfile(profile_id)->HandleExtensionUnloadedEvent(
307       extension_id);
308 }
309
310 GalleryWatchManager::GalleryWatchManager() {
311   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
312 }
313
314 GalleryWatchManager::~GalleryWatchManager() {
315   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
316   DeleteAllWatchers();
317 }
318
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()) {
327     // Already watched.
328     iter->second->AddExtension(extension_id);
329     return true;
330   }
331
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),
338                      watch_path)));
339   if (!watch->SetupWatch())
340     return false;
341   gallery_watchers_[watch_path] = watch.get();
342   return true;
343 }
344
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())
351     return;
352   // Remove the renderer process for this watch.
353   iter->second->RemoveExtension(extension_id);
354 }
355
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);
363
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())
369        continue;
370      iter->second->OnExtensionUnloaded(extension_id);
371   }
372 }
373
374 void GalleryWatchManager::DeleteAllWatchers() {
375   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
376   if (gallery_watchers_.empty())
377     return;
378
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();
387 }
388
389 void GalleryWatchManager::RemoveGalleryFilePathWatcherEntry(
390     const base::FilePath& watch_path) {
391   DCHECK_CURRENTLY_ON(BrowserThread::FILE);
392   gallery_watchers_.erase(watch_path);
393 }
394
395 }  // namespace extensions