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

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

Powered by Google App Engine
This is Rietveld 408576698