OLD | NEW |
---|---|
(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(); | |
Matt Perry
2013/01/03 21:53:41
This is not safe. |profile| could have been delete
kmadhusu
2013/01/04 17:58:11
Fixed. MediaGalleriesPrivateEventRouter supports w
| |
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 // |on_destroyed_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& on_destroyed_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; | |
Matt Perry
2013/01/03 23:20:34
Don't use unsigned here: http://google-styleguide.
kmadhusu
2013/01/04 17:58:11
Changed to int.
| |
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 |this| object is destroyed. | |
131 base::Closure on_destroyed_callback_; | |
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& on_destroyed_callback) | |
150 : profile_(profile), | |
151 gallery_id_(gallery_id), | |
152 on_destroyed_callback_(on_destroyed_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())); | |
Matt Perry
2013/01/03 23:20:34
nit: change ExtensionWatchInfo's constructor to st
kmadhusu
2013/01/04 17:58:11
Done.
| |
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 on_destroyed_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 gallery_watchers_.erase(watch_path); | |
390 } | |
391 | |
392 } // namespace extensions | |
OLD | NEW |