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

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/compiler_specific.h"
14 #include "base/files/file_path_watcher.h"
15 #include "base/location.h"
16 #include "base/memory/weak_ptr.h"
17 #include "base/time.h"
18 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_ private_api.h"
19 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_ private_api_factory.h"
20 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_ private_event_router.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "content/public/browser/browser_thread.h"
23
24 namespace extensions {
25
26 class GalleryWatchManager;
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<const 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 const Profile* profile,
43 const std::string& 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(
49 const_cast<Profile*>(profile))->event_router();
Lei Zhang 2012/12/18 01:45:16 GalleryWatchManager shouldn't promise to use a con
kmadhusu 2012/12/18 21:32:39 Done.
50 if (!router)
51 return;
52 router->OnGalleryChanged(gallery_id, extension_ids);
53 }
54
55 } // namespace
56
57 ///////////////////////////////////////////////////////////////////////////////
58 // GalleryFilePathWatcher //
59 ///////////////////////////////////////////////////////////////////////////////
60
61 // Gallery file path watcher delegate to handle the gallery change
Lei Zhang 2012/12/18 01:45:16 From this comment, I still have trouble understand
kmadhusu 2012/12/18 21:32:39 How about this? // This class does a recursive wat
Lei Zhang 2012/12/19 01:03:47 Your comment still does not answer the question of
kmadhusu 2012/12/19 21:55:55 sure. Done.
62 // notifications. This class manages all the extension usage and forwards
63 // the gallery change notifications to the extensions. This class lives on
64 // the FILE thread. This class is instantiated per gallery watch path and does
65 // recursive watches.
66 class GalleryFilePathWatcher : public base::RefCounted<GalleryFilePathWatcher> {
67 public:
68 GalleryFilePathWatcher(const Profile* profile,
69 const std::string& gallery_id,
70 const FilePath& path,
71 const std::string& extension_id);
72
73 // Adds the extension specified by the |extension_id| to the
74 // ExtensionWatchInfoMap and initiate the watch operation.
Lei Zhang 2012/12/18 01:45:16 What does "initiate the watch operation" mean? How
kmadhusu 2012/12/18 21:32:39 This function adds an extension reference to the w
75 void AddExtension(const std::string& extension_id);
76
77 // Cancels the watch for the extension specified by the |extension_id|.
78 void RemoveExtension(const std::string& extension_id);
79
80 // Handles the extension unloaded/uninstalled/destroyed event.
81 void OnExtensionDestroyed(const std::string& extension_id);
82
83 // Sets up the watch operation for the specified |gallery_path_|. On
84 // success, returns true.
85 bool SetupWatch();
86
87 // Removes all the extension references when the browser profile is in
88 // shutdown mode.
89 void RemoveAllWatchReferences();
90
91 private:
92 friend class base::RefCounted<GalleryFilePathWatcher>;
93
94 // Keeps track of extension watch details.
95 struct ExtensionWatchInfo {
Lei Zhang 2012/12/18 01:45:16 Are you keeping track of how many times a particul
kmadhusu 2012/12/18 21:32:39 Yes
96 ExtensionWatchInfo();
97
98 // Number of watches in this extension, e.g "3"
99 unsigned int watch_count;
100
101 // Used to manage the gallery changed events.
102 base::Time last_gallery_changed_event;
103 };
104
105 typedef std::map<std::string, ExtensionWatchInfo> ExtensionWatchInfoMap;
106
107 // Private because GalleryFilePathWatcher is ref-counted.
108 virtual ~GalleryFilePathWatcher();
109
110 // FilePathWatcher callback.
111 void OnFilePathChanged(const FilePath& path, bool error);
112
113 // Remove the watch references for the extension specified by the
114 // |extension_id|.
115 void RemoveExtensionReferences(const std::string& extension_id);
116
117 // Current profile.
118 const Profile* profile_;
119
120 // The gallery identifier, e.g "1".
121 const std::string gallery_id_;
122
123 // The gallery file path watcher.
124 base::files::FilePathWatcher file_watcher_;
125
126 // The gallery file path, e.g "C:\My Pictures".
127 FilePath gallery_path_;
128
129 // Map to keep track of the extension and its corresponding watch count.
130 // Key: Extension identifier, e.g "qoueruoweuroiwueroiwujkshdf".
131 // Value: Watch information.
132 ExtensionWatchInfoMap extension_watch_info_map_;
133
134 // Used to provide a weak pointer to FilePathWatcher callback.
135 base::WeakPtrFactory<GalleryFilePathWatcher> weak_ptr_factory_;
136
137 DISALLOW_COPY_AND_ASSIGN(GalleryFilePathWatcher);
138 };
139
140 GalleryFilePathWatcher::GalleryFilePathWatcher(const Profile* profile,
141 const std::string& gallery_id,
142 const FilePath& path,
143 const std::string& extension_id)
144 : profile_(profile),
145 gallery_id_(gallery_id),
146 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
147 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
148 gallery_path_ = path;
149 AddExtension(extension_id);
150 }
151
152 void GalleryFilePathWatcher::AddExtension(const std::string& extension_id) {
153 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
154 ExtensionWatchInfoMap::iterator it =
155 extension_watch_info_map_.find(extension_id);
156 if (it != extension_watch_info_map_.end()) {
157 it->second.watch_count++;
158 } else {
159 extension_watch_info_map_.insert(
160 ExtensionWatchInfoMap::value_type(extension_id, ExtensionWatchInfo()));
161 }
162 AddRef();
163 }
164
165 void GalleryFilePathWatcher::RemoveExtension(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 GalleryFilePathWatcher::OnExtensionDestroyed(
179 const std::string& extension_id) {
180 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
181 RemoveExtensionReferences(extension_id);
182 }
183
184 bool 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 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 GalleryFilePathWatcher::~GalleryFilePathWatcher() {
205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
206 }
207
208 GalleryFilePathWatcher::ExtensionWatchInfo::ExtensionWatchInfo()
209 : watch_count(1) {
210 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
Lei Zhang 2012/12/18 01:45:16 Structs should not do anything other can carry val
kmadhusu 2012/12/18 21:32:39 Done.
211 }
212
213 void GalleryFilePathWatcher::OnFilePathChanged(const FilePath& path,
214 bool error) {
215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
216 if (error || (path != gallery_path_))
217 return;
218
219 std::set<std::string> extension_ids;
220 for (ExtensionWatchInfoMap::iterator iter = extension_watch_info_map_.begin();
221 iter != extension_watch_info_map_.end(); ++iter) {
222 if (!iter->second.last_gallery_changed_event.is_null()) {
223 // Ignore gallery change event if it is received too frequently.
224 // For example, when an user copies/deletes 1000 media files from a
225 // gallery, this callback is called 1000 times within a span of 10ms.
226 // GalleryWatchManager should not send 1000 gallery changed events to
227 // the watching extension.
228 const int kMinSecondsToIgnoreGalleryChangedEvent = 3;
229 base::TimeDelta diff =
230 base::Time::Now() - iter->second.last_gallery_changed_event;
231 if (diff.InSeconds() < kMinSecondsToIgnoreGalleryChangedEvent)
232 continue;
233 }
234 iter->second.last_gallery_changed_event = base::Time::Now();
235 extension_ids.insert(iter->first);
236 }
237 if (!extension_ids.empty()) {
238 content::BrowserThread::PostTask(
239 content::BrowserThread::UI, FROM_HERE,
240 base::Bind(SendGalleryChangedEventOnUIThread, profile_, gallery_id_,
241 extension_ids));
242 }
243 }
244
245 void GalleryFilePathWatcher::RemoveExtensionReferences(
246 const std::string& extension_id) {
247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
248 ExtensionWatchInfoMap::iterator it =
249 extension_watch_info_map_.find(extension_id);
250 if (it == extension_watch_info_map_.end())
251 return;
252 const ExtensionWatchInfo watch_info = it->second;
253 for (unsigned int i = 0; i < watch_info.watch_count; ++i)
254 Release();
255 extension_watch_info_map_.erase(it);
256 }
257
258 ///////////////////////////////////////////////////////////////////////////////
259 // GalleryWatchManager //
260 ///////////////////////////////////////////////////////////////////////////////
261
262 // static
263 GalleryWatchManager* GalleryWatchManager::GetForProfile(
264 const Profile* profile) {
265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
266 DCHECK(profile);
267 bool has_watch_manager = (g_gallery_watch_managers &&
268 GalleryWatchManager::HasForProfile(profile));
269 if (!g_gallery_watch_managers)
270 g_gallery_watch_managers = new WatchManagerMap;
271 if (!has_watch_manager)
272 (*g_gallery_watch_managers)[profile] = new GalleryWatchManager(profile);
273 return (*g_gallery_watch_managers)[profile];
274 }
275
276 // static
277 bool GalleryWatchManager::HasForProfile(const Profile* profile) {
278 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
279 DCHECK(profile);
280 if (!g_gallery_watch_managers)
281 return false;
282 WatchManagerMap::const_iterator it = g_gallery_watch_managers->find(profile);
283 return (it != g_gallery_watch_managers->end());
284 }
285
286 // static
287 void GalleryWatchManager::OnProfileShutdown(const Profile* profile) {
288 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
289 DCHECK(profile);
290 if (!g_gallery_watch_managers || g_gallery_watch_managers->empty())
291 return;
292 WatchManagerMap::iterator it = g_gallery_watch_managers->find(profile);
293 if (it == g_gallery_watch_managers->end())
294 return;
295 delete it->second;
296 g_gallery_watch_managers->erase(it);
297 if (g_gallery_watch_managers->empty())
298 delete g_gallery_watch_managers;
299 }
300
301 GalleryWatchManager::~GalleryWatchManager() {
302 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
303 if (!gallery_watchers_.empty()) {
304 // User closed the extension/browser without stoping the gallery watchers.
305 DeleteAllWatchers();
306 }
307 DCHECK(gallery_watchers_.empty());
308 }
309
310 bool GalleryWatchManager::StartGalleryWatch(
311 const std::string& gallery_id,
312 const FilePath& watch_path,
313 const std::string& extension_id) {
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(profile_, gallery_id, watch_path,
325 extension_id));
326 if (!watch->SetupWatch())
327 return false;
328 gallery_watchers_[watch_path] = watch;
329 return true;
330 }
331
332 void GalleryWatchManager::StopGalleryWatch(
333 const FilePath& watch_path,
334 const std::string& extension_id) {
335 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
336 WatcherMap::iterator iter = gallery_watchers_.find(watch_path);
337 if (iter == gallery_watchers_.end())
338 return;
339 // Remove the renderer process for this watch.
340 iter->second->RemoveExtension(extension_id);
341 if (iter->second->HasOneRef()) {
342 // There are no references other than the one |gallery_watchers_| holds.
343 iter->second = NULL;
344 gallery_watchers_.erase(iter);
345 }
346 }
347
348 void GalleryWatchManager::OnExtensionDestroyed(
349 const std::string& extension_id) {
350 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
351 std::list<FilePath> watchers_to_erase;
352 for (WatcherMap::iterator iter = gallery_watchers_.begin();
353 iter != gallery_watchers_.end(); ++iter) {
354 // Remove the renderer process for this watch.
355 iter->second->OnExtensionDestroyed(extension_id);
356 if (iter->second->HasOneRef()) {
357 // There are no references other than the one |gallery_watchers_| holds.
358 watchers_to_erase.push_back(iter->first);
359 }
360 }
361
362 for (std::list<FilePath>::const_iterator path = watchers_to_erase.begin();
363 path != watchers_to_erase.end(); ++path) {
364 WatcherMap::iterator iter = gallery_watchers_.find(*path);
365 DCHECK(iter != gallery_watchers_.end());
366 iter->second = NULL;
367 gallery_watchers_.erase(iter);
368 }
369 }
370
371 GalleryWatchManager::GalleryWatchManager(const Profile* profile)
372 : profile_(profile) {
373 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
374 }
375
376 void GalleryWatchManager::DeleteAllWatchers() {
377 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
378 std::list<FilePath> watchers_to_erase;
379 for (WatcherMap::iterator iter = gallery_watchers_.begin();
380 iter != gallery_watchers_.end(); ++iter) {
381 iter->second->RemoveAllWatchReferences();
382 // Verify that there are no references other than the one
383 // |gallery_watchers_| holds.
384 DCHECK(iter->second->HasOneRef());
385 watchers_to_erase.push_back(iter->first);
386 }
387
388 for (std::list<FilePath>::const_iterator path = watchers_to_erase.begin();
389 path != watchers_to_erase.end(); ++path) {
390 WatcherMap::iterator iter = gallery_watchers_.find(*path);
391 DCHECK(iter != gallery_watchers_.end());
392 iter->second = NULL;
393 gallery_watchers_.erase(iter);
394 }
395 }
396
397 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698