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.
5 // GalleryWatchStateTracker implementation.
7 #include "chrome/browser/extensions/api/media_galleries_private/gallery_watch_state_tracker.h"
10 #include "base/files/file_path.h"
11 #include "base/location.h"
12 #include "base/stl_util.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/values.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/extensions/api/media_galleries_private/gallery_watch_manager.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_event_router.h"
20 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/extensions/extension_system.h"
22 #include "chrome/browser/extensions/state_store.h"
23 #include "chrome/browser/media_galleries/media_file_system_registry.h"
24 #include "chrome/browser/media_galleries/media_galleries_preferences.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/common/extensions/extension.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/notification_details.h"
29 #include "content/public/browser/notification_service.h"
31 namespace extensions {
35 // State store key to track the registered gallery watchers for the extensions.
36 const char kRegisteredGalleryWatchers[] = "media_gallery_watchers";
38 // Converts the storage |list| value to WatchedGalleryIds.
39 MediaGalleryPrefIdSet WatchedGalleryIdsFromValue(
40 const base::ListValue* list) {
41 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
42 MediaGalleryPrefIdSet gallery_ids;
43 std::string gallery_id_str;
44 for (size_t i = 0; i < list->GetSize(); ++i) {
45 if (!list->GetString(i, &gallery_id_str) || gallery_id_str.empty())
47 MediaGalleryPrefId gallery_id;
48 if (base::StringToUint64(gallery_id_str, &gallery_id))
49 gallery_ids.insert(gallery_id);
54 // Converts WatchedGalleryIds to a storage list value.
55 scoped_ptr<base::ListValue> WatchedGalleryIdsToValue(
56 const MediaGalleryPrefIdSet gallery_ids) {
57 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
58 scoped_ptr<base::ListValue> list(new base::ListValue());
59 for (MediaGalleryPrefIdSet::const_iterator id_iter = gallery_ids.begin();
60 id_iter != gallery_ids.end(); ++id_iter)
61 list->AppendString(base::Uint64ToString(*id_iter));
65 // Looks up an extension by ID. Does not include disabled extensions.
66 const Extension* GetExtensionById(Profile* profile,
67 const std::string& extension_id) {
68 ExtensionService* service = profile->GetExtensionService();
71 return service->GetExtensionById(extension_id, false);
76 GalleryWatchStateTracker::GalleryWatchStateTracker(Profile* profile)
78 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
80 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
81 content::Source<Profile>(profile_));
82 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
83 content::Source<Profile>(profile_));
84 MediaGalleriesPreferences* preferences =
85 g_browser_process->media_file_system_registry()->GetPreferences(profile);
86 preferences->AddGalleryChangeObserver(this);
89 GalleryWatchStateTracker::~GalleryWatchStateTracker() {
90 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
91 MediaGalleriesPreferences* preferences =
92 g_browser_process->media_file_system_registry()->GetPreferences(profile_);
93 preferences->RemoveGalleryChangeObserver(this);
97 GalleryWatchStateTracker* GalleryWatchStateTracker::GetForProfile(
99 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
101 // Gallery watch operation is supported only on windows.
102 // Please refer to crbug.com/144491 for more details.
104 MediaGalleriesPrivateAPI* private_api =
105 MediaGalleriesPrivateAPI::Get(profile);
107 return NULL; // In unit tests, we don't have a MediaGalleriesPrivateAPI.
108 return private_api->GetGalleryWatchStateTracker();
113 void GalleryWatchStateTracker::OnPermissionAdded(
114 MediaGalleriesPreferences* preferences,
115 const std::string& extension_id,
116 MediaGalleryPrefId gallery_id) {
117 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
118 // Granted gallery permission.
119 if (HasGalleryWatchInfo(extension_id, gallery_id, false))
120 SetupGalleryWatch(extension_id, gallery_id, preferences);
123 void GalleryWatchStateTracker::OnPermissionRemoved(
124 MediaGalleriesPreferences* preferences,
125 const std::string& extension_id,
126 MediaGalleryPrefId gallery_id) {
127 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
128 // Revoked gallery permission.
129 if (HasGalleryWatchInfo(extension_id, gallery_id, true))
130 RemoveGalleryWatch(extension_id, gallery_id, preferences);
133 void GalleryWatchStateTracker::OnGalleryRemoved(MediaGalleriesPreferences* pref,
134 MediaGalleryPrefId gallery_id) {
135 for (WatchedExtensionsMap::const_iterator it =
136 watched_extensions_map_.begin();
137 it != watched_extensions_map_.end();
139 if (it->second.find(gallery_id) != it->second.end())
140 RemoveGalleryWatch(it->first, gallery_id, pref);
144 MediaGalleryPrefIdSet
145 GalleryWatchStateTracker::GetAllWatchedGalleryIDsForExtension(
146 const std::string& extension_id) const {
147 MediaGalleryPrefIdSet gallery_ids;
148 WatchedExtensionsMap::const_iterator extension_id_iter =
149 watched_extensions_map_.find(extension_id);
150 if (extension_id_iter != watched_extensions_map_.end()) {
151 for (WatchedGalleriesMap::const_iterator gallery_id_iter =
152 extension_id_iter->second.begin();
153 gallery_id_iter != extension_id_iter->second.end();
155 gallery_ids.insert(gallery_id_iter->first);
161 void GalleryWatchStateTracker::RemoveAllGalleryWatchersForExtension(
162 const std::string& extension_id,
163 MediaGalleriesPreferences* preferences) {
164 WatchedExtensionsMap::iterator extension_id_iter =
165 watched_extensions_map_.find(extension_id);
166 if (extension_id_iter == watched_extensions_map_.end())
168 const WatchedGalleriesMap& galleries = extension_id_iter->second;
169 for (WatchedGalleriesMap::const_iterator gallery_id_iter = galleries.begin();
170 gallery_id_iter != galleries.end(); ++gallery_id_iter)
171 RemoveGalleryWatch(extension_id, gallery_id_iter->second, preferences);
172 watched_extensions_map_.erase(extension_id_iter);
173 WriteToStorage(extension_id);
176 void GalleryWatchStateTracker::OnGalleryWatchAdded(
177 const std::string& extension_id,
178 MediaGalleryPrefId gallery_id) {
179 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
180 bool update_storage =
181 AddWatchedGalleryIdInfoForExtension(extension_id, gallery_id);
183 WriteToStorage(extension_id);
186 void GalleryWatchStateTracker::OnGalleryWatchRemoved(
187 const std::string& extension_id,
188 MediaGalleryPrefId gallery_id) {
189 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
190 if (!ContainsKey(watched_extensions_map_, extension_id))
192 watched_extensions_map_[extension_id].erase(gallery_id);
193 if (watched_extensions_map_[extension_id].empty())
194 watched_extensions_map_.erase(extension_id);
195 WriteToStorage(extension_id);
198 void GalleryWatchStateTracker::Observe(
200 const content::NotificationSource& source,
201 const content::NotificationDetails& details) {
202 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
204 case chrome::NOTIFICATION_EXTENSION_LOADED: {
205 const Extension* extension =
206 content::Details<const Extension>(details).ptr();
207 StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
210 storage->GetExtensionValue(
212 kRegisteredGalleryWatchers,
213 base::Bind(&GalleryWatchStateTracker::ReadFromStorage,
218 case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
219 Extension* extension = const_cast<Extension*>(
220 content::Details<extensions::UnloadedExtensionInfo>(
221 details)->extension);
223 if (!ContainsKey(watched_extensions_map_, extension->id()))
225 content::BrowserThread::PostTask(
226 content::BrowserThread::FILE, FROM_HERE,
227 base::Bind(&GalleryWatchManager::OnExtensionUnloaded,
230 for (WatchedGalleriesMap::iterator iter =
231 watched_extensions_map_[extension->id()].begin();
232 iter != watched_extensions_map_[extension->id()].end(); ++iter) {
233 iter->second = false;
242 void GalleryWatchStateTracker::WriteToStorage(const std::string& extension_id) {
243 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
244 StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
247 MediaGalleryPrefIdSet gallery_ids =
248 GetAllWatchedGalleryIDsForExtension(extension_id);
249 storage->SetExtensionValue(
251 kRegisteredGalleryWatchers,
252 WatchedGalleryIdsToValue(gallery_ids).PassAs<base::Value>());
255 void GalleryWatchStateTracker::ReadFromStorage(
256 const std::string& extension_id,
257 scoped_ptr<base::Value> value) {
258 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
259 MediaGalleriesPreferences* preferences =
260 g_browser_process->media_file_system_registry()->GetPreferences(profile_);
261 base::ListValue* list = NULL;
262 if (!value.get() || !value->GetAsList(&list))
264 MediaGalleryPrefIdSet gallery_ids = WatchedGalleryIdsFromValue(list);
265 if (gallery_ids.empty())
268 for (MediaGalleryPrefIdSet::const_iterator id_iter = gallery_ids.begin();
269 id_iter != gallery_ids.end(); ++id_iter) {
270 watched_extensions_map_[extension_id][*id_iter] = false;
271 SetupGalleryWatch(extension_id, *id_iter, preferences);
275 void GalleryWatchStateTracker::SetupGalleryWatch(
276 const std::string& extension_id,
277 MediaGalleryPrefId gallery_id,
278 MediaGalleriesPreferences* preferences) {
279 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
280 const Extension* extension = GetExtensionById(profile_, extension_id);
282 base::FilePath gallery_file_path(preferences->LookUpGalleryPathForExtension(
283 gallery_id, extension, false));
284 if (gallery_file_path.empty())
286 MediaGalleriesPrivateEventRouter* router =
287 MediaGalleriesPrivateAPI::Get(profile_)->GetEventRouter();
289 content::BrowserThread::PostTaskAndReplyWithResult(
290 content::BrowserThread::FILE,
292 base::Bind(&GalleryWatchManager::SetupGalleryWatch,
297 router->AsWeakPtr()),
298 base::Bind(&GalleryWatchStateTracker::HandleSetupGalleryWatchResponse,
304 void GalleryWatchStateTracker::RemoveGalleryWatch(
305 const std::string& extension_id,
306 MediaGalleryPrefId gallery_id,
307 MediaGalleriesPreferences* preferences) {
308 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
309 const Extension* extension = GetExtensionById(profile_, extension_id);
311 base::FilePath gallery_file_path(preferences->LookUpGalleryPathForExtension(
312 gallery_id, extension, true));
313 if (gallery_file_path.empty())
315 content::BrowserThread::PostTask(
316 content::BrowserThread::FILE, FROM_HERE,
317 base::Bind(&GalleryWatchManager::RemoveGalleryWatch,
321 watched_extensions_map_[extension_id][gallery_id] = false;
324 bool GalleryWatchStateTracker::HasGalleryWatchInfo(
325 const std::string& extension_id,
326 MediaGalleryPrefId gallery_id,
327 bool has_active_watcher) {
328 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
329 return (ContainsKey(watched_extensions_map_, extension_id) &&
330 ContainsKey(watched_extensions_map_[extension_id], gallery_id) &&
331 watched_extensions_map_[extension_id][gallery_id] ==
335 void GalleryWatchStateTracker::HandleSetupGalleryWatchResponse(
336 const std::string& extension_id,
337 MediaGalleryPrefId gallery_id,
339 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
341 return; // Failed to setup the gallery watch for the given extension.
342 AddWatchedGalleryIdInfoForExtension(extension_id, gallery_id);
345 bool GalleryWatchStateTracker::AddWatchedGalleryIdInfoForExtension(
346 const std::string& extension_id,
347 MediaGalleryPrefId gallery_id) {
348 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
349 if (HasGalleryWatchInfo(extension_id, gallery_id, true))
351 watched_extensions_map_[extension_id][gallery_id] = true;
355 } // namespace extensions