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/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 | |
OLD | NEW |