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

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

Powered by Google App Engine
This is Rietveld 408576698