2da647a45d3fc104d482d23aef9c26f9ed14f682
[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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(BrowserThread::FILE));
161   RemoveExtensionReferences(extension_id);
162 }
163
164 bool GalleryWatchManager::GalleryFilePathWatcher::SetupWatch() {
165   DCHECK(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(BrowserThread::FILE));
186   on_destroyed_callback_.Run();
187 }
188
189 void GalleryWatchManager::GalleryFilePathWatcher::OnFilePathChanged(
190     const base::FilePath& path,
191     bool error) {
192   DCHECK(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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(BrowserThread::CurrentlyOn(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 }
275
276 // static
277 bool GalleryWatchManager::SetupGalleryWatch(
278     void* profile_id,
279     MediaGalleryPrefId gallery_id,
280     const base::FilePath& watch_path,
281     const std::string& extension_id,
282     base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router) {
283   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
284   return GalleryWatchManager::GetForProfile(profile_id)->StartGalleryWatch(
285       gallery_id, watch_path, extension_id, event_router);
286 }
287
288 // static
289 void GalleryWatchManager::RemoveGalleryWatch(void* profile_id,
290                                              const base::FilePath& watch_path,
291                                              const std::string& extension_id) {
292   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
293   if (!GalleryWatchManager::HasForProfile(profile_id))
294     return;
295   GalleryWatchManager::GetForProfile(profile_id)->StopGalleryWatch(
296       watch_path, extension_id);
297 }
298
299 void GalleryWatchManager::OnExtensionUnloaded(void* profile_id,
300                                               const std::string& extension_id) {
301   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
302   if (!GalleryWatchManager::HasForProfile(profile_id))
303     return;
304   GalleryWatchManager::GetForProfile(profile_id)->HandleExtensionUnloadedEvent(
305       extension_id);
306 }
307
308 GalleryWatchManager::GalleryWatchManager() {
309   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
310 }
311
312 GalleryWatchManager::~GalleryWatchManager() {
313   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
314   DeleteAllWatchers();
315 }
316
317 bool GalleryWatchManager::StartGalleryWatch(
318     MediaGalleryPrefId gallery_id,
319     const base::FilePath& watch_path,
320     const std::string& extension_id,
321     base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router) {
322   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
323   WatcherMap::const_iterator iter = gallery_watchers_.find(watch_path);
324   if (iter != gallery_watchers_.end()) {
325     // Already watched.
326     iter->second->AddExtension(extension_id);
327     return true;
328   }
329
330   // Need to add a new watcher.
331   scoped_refptr<GalleryFilePathWatcher> watch(
332       new GalleryFilePathWatcher(
333           event_router, gallery_id, watch_path, extension_id,
334           base::Bind(&GalleryWatchManager::RemoveGalleryFilePathWatcherEntry,
335                      base::Unretained(this),
336                      watch_path)));
337   if (!watch->SetupWatch())
338     return false;
339   gallery_watchers_[watch_path] = watch.get();
340   return true;
341 }
342
343 void GalleryWatchManager::StopGalleryWatch(
344     const base::FilePath& watch_path,
345     const std::string& extension_id) {
346   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
347   WatcherMap::iterator iter = gallery_watchers_.find(watch_path);
348   if (iter == gallery_watchers_.end())
349     return;
350   // Remove the renderer process for this watch.
351   iter->second->RemoveExtension(extension_id);
352 }
353
354 void GalleryWatchManager::HandleExtensionUnloadedEvent(
355     const std::string& extension_id) {
356   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
357   std::list<base::FilePath> watchers_to_notify;
358   for (WatcherMap::iterator iter = gallery_watchers_.begin();
359        iter != gallery_watchers_.end(); ++iter)
360     watchers_to_notify.push_back(iter->first);
361
362   for (std::list<base::FilePath>::const_iterator path =
363            watchers_to_notify.begin();
364        path != watchers_to_notify.end(); ++path) {
365      WatcherMap::iterator iter = gallery_watchers_.find(*path);
366      if (iter == gallery_watchers_.end())
367        continue;
368      iter->second->OnExtensionUnloaded(extension_id);
369   }
370 }
371
372 void GalleryWatchManager::DeleteAllWatchers() {
373   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
374   if (gallery_watchers_.empty())
375     return;
376
377   // Create a copy of |gallery_watchers_| to delete because
378   // GalleryFilePathWatcher::RemoveAllWatchReferences will
379   // eventually call GalleryWatchManager::RemoveGalleryFilePathWatcherEntry()
380   // and modify |gallery_watchers_|.
381   WatcherMap watchers_to_delete(gallery_watchers_);
382   for (WatcherMap::const_iterator iter = watchers_to_delete.begin();
383        iter != watchers_to_delete.end(); ++iter)
384     iter->second->RemoveAllWatchReferences();
385 }
386
387 void GalleryWatchManager::RemoveGalleryFilePathWatcherEntry(
388     const base::FilePath& watch_path) {
389   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
390   gallery_watchers_.erase(watch_path);
391 }
392
393 }  // namespace extensions