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