Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(285)

Side by Side Diff: chrome/browser/extensions/api/media_galleries_private/gallery_watch_manager.cc

Issue 11535008: Implement mediaGalleriesPrivate api to notify extensions about gallery changed events. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Rebase + Addressed review comments Created 7 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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/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/time.h"
19 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_ private_event_router.h"
20 #include "content/public/browser/browser_thread.h"
21
22 namespace extensions {
23
24 namespace {
25
26 using content::BrowserThread;
27
28 // Map to keep track of profile specific GalleryWatchManager objects.
29 // Key: Profile identifier.
30 // Value: GalleryWatchManager*.
31 // This map owns the GalleryWatchManager object.
32 typedef std::map<void*, extensions::GalleryWatchManager*> WatchManagerMap;
33 WatchManagerMap* g_gallery_watch_managers = NULL;
34
35 // Dispatches the gallery changed event on the UI thread.
36 void SendGalleryChangedEventOnUIThread(
37 base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router,
38 chrome::MediaGalleryPrefId gallery_id,
39 const std::set<std::string>& extension_ids) {
40 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
41 if (event_router.get())
42 event_router->OnGalleryChanged(gallery_id, extension_ids);
43 }
44
45 } // namespace
46
47 ///////////////////////////////////////////////////////////////////////////////
48 // GalleryWatchManager::GalleryFilePathWatcher //
49 ///////////////////////////////////////////////////////////////////////////////
50
51 // This class does a recursive watch on the gallery file path and holds a list
52 // of extensions that are watching the gallery. When there is a file system
53 // activity within the gallery, GalleryFilePathWatcher notifies the interested
54 // extensions. This class lives on the file thread.
55 class GalleryWatchManager::GalleryFilePathWatcher
56 : public base::RefCounted<GalleryFilePathWatcher> {
57 public:
58 // |on_destroyed_callback| is called when the last GalleryFilePathWatcher
59 // reference goes away.
60 GalleryFilePathWatcher(
61 base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router,
62 chrome::MediaGalleryPrefId gallery_id,
63 const FilePath& path,
64 const std::string& extension_id,
65 const base::Closure& on_destroyed_callback);
66
67 // Adds the extension reference to the watched gallery.
68 void AddExtension(const std::string& extension_id);
69
70 // Removes the extension reference to the watched gallery.
71 void RemoveExtension(const std::string& extension_id);
72
73 // Handles the extension unloaded/uninstalled/destroyed event.
74 void OnExtensionDestroyed(const std::string& extension_id);
75
76 // Sets up the watch operation for the specified |gallery_path_|. On
77 // success, returns true.
78 bool SetupWatch();
79
80 // Removes all the extension references when the browser profile is in
81 // shutdown mode.
82 void RemoveAllWatchReferences();
83
84 private:
85 friend class base::RefCounted<GalleryFilePathWatcher>;
86
87 // Keeps track of extension watch details.
88 struct ExtensionWatchInfo {
89 ExtensionWatchInfo();
90
91 // Number of watches in this extension, e.g "3"
92 int watch_count;
93
94 // Used to manage the gallery changed events.
95 base::Time last_gallery_changed_event;
96 };
97
98 typedef std::map<std::string, ExtensionWatchInfo> ExtensionWatchInfoMap;
99
100 // Private because GalleryFilePathWatcher is ref-counted.
101 virtual ~GalleryFilePathWatcher();
102
103 // FilePathWatcher callback.
104 void OnFilePathChanged(const FilePath& path, bool error);
105
106 // Remove the watch references for the extension specified by the
107 // |extension_id|.
108 void RemoveExtensionReferences(const std::string& extension_id);
109
110 // Used to notify the interested extensions about the gallery changed event.
111 base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router_;
112
113 // The gallery identifier, e.g "1".
114 chrome::MediaGalleryPrefId gallery_id_;
115
116 // The gallery file path watcher.
117 base::files::FilePathWatcher file_watcher_;
118
119 // The gallery file path, e.g "C:\My Pictures".
120 FilePath gallery_path_;
121
122 // A callback to call when |this| object is destroyed.
123 base::Closure on_destroyed_callback_;
124
125 // Map to keep track of the extension and its corresponding watch count.
126 // Key: Extension identifier, e.g "qoueruoweuroiwueroiwujkshdf".
127 // Value: Watch information.
128 ExtensionWatchInfoMap extension_watch_info_map_;
129
130 // Used to provide a weak pointer to FilePathWatcher callback.
131 base::WeakPtrFactory<GalleryFilePathWatcher> weak_ptr_factory_;
132
133 DISALLOW_COPY_AND_ASSIGN(GalleryFilePathWatcher);
134 };
135
136 GalleryWatchManager::GalleryFilePathWatcher::GalleryFilePathWatcher(
137 base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router,
138 chrome::MediaGalleryPrefId gallery_id,
139 const FilePath& path,
140 const std::string& extension_id,
141 const base::Closure& on_destroyed_callback)
142 : event_router_(event_router),
143 gallery_id_(gallery_id),
144 on_destroyed_callback_(on_destroyed_callback),
145 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
146 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
147 gallery_path_ = path;
148 AddExtension(extension_id);
149 }
150
151 void GalleryWatchManager::GalleryFilePathWatcher::AddExtension(
152 const std::string& extension_id) {
153 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
154 ExtensionWatchInfoMap::iterator it =
Matt Perry 2013/01/04 18:02:38 lines 154-159 are unnecessary. map[key] automatica
kmadhusu 2013/01/04 18:44:04 Done.
155 extension_watch_info_map_.find(extension_id);
156 if (it == extension_watch_info_map_.end()) {
157 extension_watch_info_map_.insert(
158 ExtensionWatchInfoMap::value_type(extension_id, ExtensionWatchInfo()));
159 }
160 extension_watch_info_map_[extension_id].watch_count++;
161 AddRef();
162 }
163
164 void GalleryWatchManager::GalleryFilePathWatcher::RemoveExtension(
165 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 GalleryWatchManager::GalleryFilePathWatcher::OnExtensionDestroyed(
179 const std::string& extension_id) {
180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
181 RemoveExtensionReferences(extension_id);
182 }
183
184 bool GalleryWatchManager::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 GalleryWatchManager::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 GalleryWatchManager::GalleryFilePathWatcher::~GalleryFilePathWatcher() {
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
206 on_destroyed_callback_.Run();
207 }
208
209 GalleryWatchManager::GalleryFilePathWatcher::ExtensionWatchInfo::
210 ExtensionWatchInfo()
211 : watch_count(0) {
212 }
213
214 void GalleryWatchManager::GalleryFilePathWatcher::OnFilePathChanged(
215 const FilePath& path,
216 bool error) {
217 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
218 if (error || (path != gallery_path_))
219 return;
220
221 std::set<std::string> extension_ids;
222 for (ExtensionWatchInfoMap::iterator iter = extension_watch_info_map_.begin();
223 iter != extension_watch_info_map_.end(); ++iter) {
224 if (!iter->second.last_gallery_changed_event.is_null()) {
225 // Ignore gallery change event if it is received too frequently.
226 // For example, when an user copies/deletes 1000 media files from a
227 // gallery, this callback is called 1000 times within a span of 10ms.
228 // GalleryWatchManager should not send 1000 gallery changed events to
229 // the watching extension.
230 const int kMinSecondsToIgnoreGalleryChangedEvent = 3;
231 base::TimeDelta diff =
232 base::Time::Now() - iter->second.last_gallery_changed_event;
233 if (diff.InSeconds() < kMinSecondsToIgnoreGalleryChangedEvent)
234 continue;
235 }
236 iter->second.last_gallery_changed_event = base::Time::Now();
237 extension_ids.insert(iter->first);
238 }
239 if (!extension_ids.empty()) {
240 content::BrowserThread::PostTask(
241 content::BrowserThread::UI, FROM_HERE,
242 base::Bind(SendGalleryChangedEventOnUIThread, event_router_,
243 gallery_id_, extension_ids));
244 }
245 }
246
247 void GalleryWatchManager::GalleryFilePathWatcher::RemoveExtensionReferences(
248 const std::string& extension_id) {
249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
250 ExtensionWatchInfoMap::iterator it =
251 extension_watch_info_map_.find(extension_id);
252 if (it == extension_watch_info_map_.end())
253 return;
254 int watch_count = it->second.watch_count;
255 extension_watch_info_map_.erase(it);
256 for (int i = 0; i < watch_count; ++i)
257 Release();
258 }
259
260 ///////////////////////////////////////////////////////////////////////////////
261 // GalleryWatchManager //
262 ///////////////////////////////////////////////////////////////////////////////
263
264 // static
265 GalleryWatchManager* GalleryWatchManager::GetForProfile(
266 void* profile_id) {
267 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
268 DCHECK(profile_id);
269 bool has_watch_manager = (g_gallery_watch_managers &&
270 GalleryWatchManager::HasForProfile(profile_id));
271 if (!g_gallery_watch_managers)
272 g_gallery_watch_managers = new WatchManagerMap;
273 if (!has_watch_manager)
274 (*g_gallery_watch_managers)[profile_id] = new GalleryWatchManager;
275 return (*g_gallery_watch_managers)[profile_id];
276 }
277
278 // static
279 bool GalleryWatchManager::HasForProfile(void* profile_id) {
280 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
281 DCHECK(profile_id);
282 if (!g_gallery_watch_managers)
283 return false;
284 WatchManagerMap::const_iterator it =
285 g_gallery_watch_managers->find(profile_id);
286 return (it != g_gallery_watch_managers->end());
287 }
288
289 // static
290 void GalleryWatchManager::OnProfileShutdown(void* profile_id) {
291 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
292 DCHECK(profile_id);
293 if (!g_gallery_watch_managers || g_gallery_watch_managers->empty())
294 return;
295 WatchManagerMap::iterator it = g_gallery_watch_managers->find(profile_id);
296 if (it == g_gallery_watch_managers->end())
297 return;
298 delete it->second;
299 g_gallery_watch_managers->erase(it);
300 if (g_gallery_watch_managers->empty())
301 delete g_gallery_watch_managers;
302 }
303
304 GalleryWatchManager::~GalleryWatchManager() {
305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
306 DeleteAllWatchers();
307 }
308
309 bool GalleryWatchManager::StartGalleryWatch(
310 chrome::MediaGalleryPrefId gallery_id,
311 const FilePath& watch_path,
312 const std::string& extension_id,
313 base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router) {
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(
325 event_router, gallery_id, watch_path, extension_id,
326 base::Bind(&GalleryWatchManager::RemoveGalleryFilePathWatcherEntry,
327 base::Unretained(this),
328 watch_path)));
329 if (!watch->SetupWatch())
330 return false;
331 gallery_watchers_[watch_path] = watch.get();
332 return true;
333 }
334
335 void GalleryWatchManager::StopGalleryWatch(
336 const FilePath& watch_path,
337 const std::string& extension_id) {
338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
339 WatcherMap::iterator iter = gallery_watchers_.find(watch_path);
340 if (iter == gallery_watchers_.end())
341 return;
342 // Remove the renderer process for this watch.
343 iter->second->RemoveExtension(extension_id);
344 }
345
346 void GalleryWatchManager::OnExtensionDestroyed(
347 const std::string& extension_id) {
348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
349 std::list<FilePath> watchers_to_notify;
350 for (WatcherMap::iterator iter = gallery_watchers_.begin();
351 iter != gallery_watchers_.end(); ++iter)
352 watchers_to_notify.push_back(iter->first);
353
354 for (std::list<FilePath>::const_iterator path = watchers_to_notify.begin();
355 path != watchers_to_notify.end(); ++path) {
356 WatcherMap::iterator iter = gallery_watchers_.find(*path);
357 if (iter == gallery_watchers_.end())
358 continue;
359 iter->second->OnExtensionDestroyed(extension_id);
360 }
361 }
362
363 GalleryWatchManager::GalleryWatchManager() {
364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
365 }
366
367 void GalleryWatchManager::DeleteAllWatchers() {
368 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
369 if (gallery_watchers_.empty())
370 return;
371
372 for (WatcherMap::iterator iter = gallery_watchers_.begin();
373 iter != gallery_watchers_.end(); ++iter)
374 iter->second->RemoveAllWatchReferences();
375 }
376
377 void GalleryWatchManager::RemoveGalleryFilePathWatcherEntry(
378 const FilePath& watch_path) {
379 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
380 gallery_watchers_.erase(watch_path);
381 }
382
383 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698