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

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: Disable SetupGalleryWatch browser test on ChromeOS Created 7 years, 11 months 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/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 extension_watch_info_map_[extension_id].watch_count++;
155 AddRef();
156 }
157
158 void GalleryWatchManager::GalleryFilePathWatcher::RemoveExtension(
159 const std::string& extension_id) {
160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
161 ExtensionWatchInfoMap::iterator it =
162 extension_watch_info_map_.find(extension_id);
163 if (it == extension_watch_info_map_.end())
164 return;
165 // If entry found - decrease it's count and remove if necessary
166 it->second.watch_count--;
167 if (0 == it->second.watch_count)
168 extension_watch_info_map_.erase(it);
169 Release();
170 }
171
172 void GalleryWatchManager::GalleryFilePathWatcher::OnExtensionDestroyed(
173 const std::string& extension_id) {
174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
175 RemoveExtensionReferences(extension_id);
176 }
177
178 bool GalleryWatchManager::GalleryFilePathWatcher::SetupWatch() {
179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
180 return file_watcher_.Watch(
181 gallery_path_, true,
182 base::Bind(&GalleryFilePathWatcher::OnFilePathChanged,
183 weak_ptr_factory_.GetWeakPtr()));
184 }
185
186 void GalleryWatchManager::GalleryFilePathWatcher::RemoveAllWatchReferences() {
187 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
188 std::set<std::string> extension_ids;
189 for (ExtensionWatchInfoMap::iterator iter = extension_watch_info_map_.begin();
190 iter != extension_watch_info_map_.end(); ++iter)
191 extension_ids.insert(iter->first);
192
193 for (std::set<std::string>::const_iterator it = extension_ids.begin();
194 it != extension_ids.end(); ++it)
195 RemoveExtensionReferences(*it);
196 }
197
198 GalleryWatchManager::GalleryFilePathWatcher::~GalleryFilePathWatcher() {
199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
200 on_destroyed_callback_.Run();
201 }
202
203 GalleryWatchManager::GalleryFilePathWatcher::ExtensionWatchInfo::
204 ExtensionWatchInfo()
205 : watch_count(0) {
206 }
207
208 void GalleryWatchManager::GalleryFilePathWatcher::OnFilePathChanged(
209 const FilePath& path,
210 bool error) {
211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
212 if (error || (path != gallery_path_))
213 return;
214
215 std::set<std::string> extension_ids;
216 for (ExtensionWatchInfoMap::iterator iter = extension_watch_info_map_.begin();
217 iter != extension_watch_info_map_.end(); ++iter) {
218 if (!iter->second.last_gallery_changed_event.is_null()) {
219 // Ignore gallery change event if it is received too frequently.
220 // For example, when an user copies/deletes 1000 media files from a
221 // gallery, this callback is called 1000 times within a span of 10ms.
222 // GalleryWatchManager should not send 1000 gallery changed events to
223 // the watching extension.
224 const int kMinSecondsToIgnoreGalleryChangedEvent = 3;
225 base::TimeDelta diff =
226 base::Time::Now() - iter->second.last_gallery_changed_event;
227 if (diff.InSeconds() < kMinSecondsToIgnoreGalleryChangedEvent)
228 continue;
229 }
230 iter->second.last_gallery_changed_event = base::Time::Now();
231 extension_ids.insert(iter->first);
232 }
233 if (!extension_ids.empty()) {
234 content::BrowserThread::PostTask(
235 content::BrowserThread::UI, FROM_HERE,
236 base::Bind(SendGalleryChangedEventOnUIThread, event_router_,
237 gallery_id_, extension_ids));
238 }
239 }
240
241 void GalleryWatchManager::GalleryFilePathWatcher::RemoveExtensionReferences(
242 const std::string& extension_id) {
243 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
244 ExtensionWatchInfoMap::iterator it =
245 extension_watch_info_map_.find(extension_id);
246 if (it == extension_watch_info_map_.end())
247 return;
248 int watch_count = it->second.watch_count;
249 extension_watch_info_map_.erase(it);
250 for (int i = 0; i < watch_count; ++i)
251 Release();
252 }
253
254 ///////////////////////////////////////////////////////////////////////////////
255 // GalleryWatchManager //
256 ///////////////////////////////////////////////////////////////////////////////
257
258 // static
259 GalleryWatchManager* GalleryWatchManager::GetForProfile(
260 void* profile_id) {
261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
262 DCHECK(profile_id);
263 bool has_watch_manager = (g_gallery_watch_managers &&
264 GalleryWatchManager::HasForProfile(profile_id));
265 if (!g_gallery_watch_managers)
266 g_gallery_watch_managers = new WatchManagerMap;
267 if (!has_watch_manager)
268 (*g_gallery_watch_managers)[profile_id] = new GalleryWatchManager;
269 return (*g_gallery_watch_managers)[profile_id];
270 }
271
272 // static
273 bool GalleryWatchManager::HasForProfile(void* profile_id) {
274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
275 DCHECK(profile_id);
276 if (!g_gallery_watch_managers)
277 return false;
278 WatchManagerMap::const_iterator it =
279 g_gallery_watch_managers->find(profile_id);
280 return (it != g_gallery_watch_managers->end());
281 }
282
283 // static
284 void GalleryWatchManager::OnProfileShutdown(void* profile_id) {
285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
286 DCHECK(profile_id);
287 if (!g_gallery_watch_managers || g_gallery_watch_managers->empty())
288 return;
289 WatchManagerMap::iterator it = g_gallery_watch_managers->find(profile_id);
290 if (it == g_gallery_watch_managers->end())
291 return;
292 delete it->second;
293 g_gallery_watch_managers->erase(it);
294 if (g_gallery_watch_managers->empty())
295 delete g_gallery_watch_managers;
296 }
297
298 GalleryWatchManager::~GalleryWatchManager() {
299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
300 DeleteAllWatchers();
301 }
302
303 bool GalleryWatchManager::StartGalleryWatch(
304 chrome::MediaGalleryPrefId gallery_id,
305 const FilePath& watch_path,
306 const std::string& extension_id,
307 base::WeakPtr<MediaGalleriesPrivateEventRouter> event_router) {
308 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
309 WatcherMap::const_iterator iter = gallery_watchers_.find(watch_path);
310 if (iter != gallery_watchers_.end()) {
311 // Already watched.
312 iter->second->AddExtension(extension_id);
313 return true;
314 }
315
316 // Need to add a new watcher.
317 scoped_refptr<GalleryFilePathWatcher> watch(
318 new GalleryFilePathWatcher(
319 event_router, gallery_id, watch_path, extension_id,
320 base::Bind(&GalleryWatchManager::RemoveGalleryFilePathWatcherEntry,
321 base::Unretained(this),
322 watch_path)));
323 if (!watch->SetupWatch())
324 return false;
325 gallery_watchers_[watch_path] = watch.get();
326 return true;
327 }
328
329 void GalleryWatchManager::StopGalleryWatch(
330 const FilePath& watch_path,
331 const std::string& extension_id) {
332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
333 WatcherMap::iterator iter = gallery_watchers_.find(watch_path);
334 if (iter == gallery_watchers_.end())
335 return;
336 // Remove the renderer process for this watch.
337 iter->second->RemoveExtension(extension_id);
338 }
339
340 void GalleryWatchManager::OnExtensionDestroyed(
341 const std::string& extension_id) {
342 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
343 std::list<FilePath> watchers_to_notify;
344 for (WatcherMap::iterator iter = gallery_watchers_.begin();
345 iter != gallery_watchers_.end(); ++iter)
346 watchers_to_notify.push_back(iter->first);
347
348 for (std::list<FilePath>::const_iterator path = watchers_to_notify.begin();
349 path != watchers_to_notify.end(); ++path) {
350 WatcherMap::iterator iter = gallery_watchers_.find(*path);
351 if (iter == gallery_watchers_.end())
352 continue;
353 iter->second->OnExtensionDestroyed(extension_id);
354 }
355 }
356
357 GalleryWatchManager::GalleryWatchManager() {
358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
359 }
360
361 void GalleryWatchManager::DeleteAllWatchers() {
362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
363 if (gallery_watchers_.empty())
364 return;
365
366 for (WatcherMap::iterator iter = gallery_watchers_.begin();
367 iter != gallery_watchers_.end(); ++iter)
368 iter->second->RemoveAllWatchReferences();
369 }
370
371 void GalleryWatchManager::RemoveGalleryFilePathWatcherEntry(
372 const FilePath& watch_path) {
373 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
374 gallery_watchers_.erase(watch_path);
375 }
376
377 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698