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 // GalleryWatchStateTracker implementation. 6 7 #include "chrome/browser/extensions/api/media_galleries_private/gallery_watch_state_tracker.h" 8 9 #include "base/bind.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/extensions/api/media_galleries_private/gallery_watch_manager.h" 17 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_api.h" 18 #include "chrome/browser/extensions/api/media_galleries_private/media_galleries_private_event_router.h" 19 #include "chrome/browser/extensions/extension_service.h" 20 #include "chrome/browser/extensions/state_store.h" 21 #include "chrome/browser/media_galleries/media_file_system_registry.h" 22 #include "chrome/browser/media_galleries/media_galleries_preferences.h" 23 #include "chrome/browser/profiles/profile.h" 24 #include "content/public/browser/browser_thread.h" 25 #include "extensions/browser/extension_registry.h" 26 #include "extensions/browser/extension_system.h" 27 #include "extensions/common/extension.h" 28 29 namespace extensions { 30 31 namespace { 32 33 // State store key to track the registered gallery watchers for the extensions. 34 const char kRegisteredGalleryWatchers[] = "media_gallery_watchers"; 35 36 // Converts the storage |list| value to WatchedGalleryIds. 37 MediaGalleryPrefIdSet WatchedGalleryIdsFromValue( 38 const base::ListValue* list) { 39 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 40 MediaGalleryPrefIdSet gallery_ids; 41 std::string gallery_id_str; 42 for (size_t i = 0; i < list->GetSize(); ++i) { 43 if (!list->GetString(i, &gallery_id_str) || gallery_id_str.empty()) 44 continue; 45 MediaGalleryPrefId gallery_id; 46 if (base::StringToUint64(gallery_id_str, &gallery_id)) 47 gallery_ids.insert(gallery_id); 48 } 49 return gallery_ids; 50 } 51 52 // Converts WatchedGalleryIds to a storage list value. 53 scoped_ptr<base::ListValue> WatchedGalleryIdsToValue( 54 const MediaGalleryPrefIdSet gallery_ids) { 55 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 56 scoped_ptr<base::ListValue> list(new base::ListValue()); 57 for (MediaGalleryPrefIdSet::const_iterator id_iter = gallery_ids.begin(); 58 id_iter != gallery_ids.end(); ++id_iter) 59 list->AppendString(base::Uint64ToString(*id_iter)); 60 return list.Pass(); 61 } 62 63 // Looks up an extension by ID. Does not include disabled extensions. 64 const Extension* GetExtensionById(Profile* profile, 65 const std::string& extension_id) { 66 ExtensionService* service = profile->GetExtensionService(); 67 if (!service) 68 return NULL; 69 return service->GetExtensionById(extension_id, false); 70 } 71 72 } // namespace 73 74 GalleryWatchStateTracker::GalleryWatchStateTracker(Profile* profile) 75 : profile_(profile), extension_registry_observer_(this) { 76 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 77 DCHECK(profile_); 78 extension_registry_observer_.Add(ExtensionRegistry::Get(profile_)); 79 MediaGalleriesPreferences* preferences = 80 g_browser_process->media_file_system_registry()->GetPreferences(profile); 81 preferences->AddGalleryChangeObserver(this); 82 } 83 84 GalleryWatchStateTracker::~GalleryWatchStateTracker() { 85 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 86 MediaGalleriesPreferences* preferences = 87 g_browser_process->media_file_system_registry()->GetPreferences(profile_); 88 preferences->RemoveGalleryChangeObserver(this); 89 } 90 91 // static 92 GalleryWatchStateTracker* GalleryWatchStateTracker::GetForProfile( 93 Profile* profile) { 94 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 95 DCHECK(profile); 96 MediaGalleriesPrivateAPI* private_api = 97 MediaGalleriesPrivateAPI::Get(profile); 98 // In unit tests, we don't have a MediaGalleriesPrivateAPI. 99 if (private_api) 100 return private_api->GetGalleryWatchStateTracker(); 101 return NULL; 102 } 103 104 void GalleryWatchStateTracker::OnPermissionAdded( 105 MediaGalleriesPreferences* preferences, 106 const std::string& extension_id, 107 MediaGalleryPrefId gallery_id) { 108 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 109 // Granted gallery permission. 110 if (HasGalleryWatchInfo(extension_id, gallery_id, false)) 111 SetupGalleryWatch(extension_id, gallery_id, preferences); 112 } 113 114 void GalleryWatchStateTracker::OnPermissionRemoved( 115 MediaGalleriesPreferences* preferences, 116 const std::string& extension_id, 117 MediaGalleryPrefId gallery_id) { 118 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 119 // Revoked gallery permission. 120 if (HasGalleryWatchInfo(extension_id, gallery_id, true)) 121 RemoveGalleryWatch(extension_id, gallery_id, preferences); 122 } 123 124 void GalleryWatchStateTracker::OnGalleryRemoved(MediaGalleriesPreferences* pref, 125 MediaGalleryPrefId gallery_id) { 126 for (WatchedExtensionsMap::const_iterator it = 127 watched_extensions_map_.begin(); 128 it != watched_extensions_map_.end(); 129 ++it) { 130 if (it->second.find(gallery_id) != it->second.end()) 131 RemoveGalleryWatch(it->first, gallery_id, pref); 132 } 133 } 134 135 MediaGalleryPrefIdSet 136 GalleryWatchStateTracker::GetAllWatchedGalleryIDsForExtension( 137 const std::string& extension_id) const { 138 MediaGalleryPrefIdSet gallery_ids; 139 WatchedExtensionsMap::const_iterator extension_id_iter = 140 watched_extensions_map_.find(extension_id); 141 if (extension_id_iter != watched_extensions_map_.end()) { 142 for (WatchedGalleriesMap::const_iterator gallery_id_iter = 143 extension_id_iter->second.begin(); 144 gallery_id_iter != extension_id_iter->second.end(); 145 ++gallery_id_iter) { 146 gallery_ids.insert(gallery_id_iter->first); 147 } 148 } 149 return gallery_ids; 150 } 151 152 void GalleryWatchStateTracker::RemoveAllGalleryWatchersForExtension( 153 const std::string& extension_id, 154 MediaGalleriesPreferences* preferences) { 155 WatchedExtensionsMap::iterator extension_id_iter = 156 watched_extensions_map_.find(extension_id); 157 if (extension_id_iter == watched_extensions_map_.end()) 158 return; 159 const WatchedGalleriesMap& galleries = extension_id_iter->second; 160 for (WatchedGalleriesMap::const_iterator gallery_id_iter = galleries.begin(); 161 gallery_id_iter != galleries.end(); ++gallery_id_iter) 162 RemoveGalleryWatch(extension_id, gallery_id_iter->second, preferences); 163 watched_extensions_map_.erase(extension_id_iter); 164 WriteToStorage(extension_id); 165 } 166 167 void GalleryWatchStateTracker::OnGalleryWatchAdded( 168 const std::string& extension_id, 169 MediaGalleryPrefId gallery_id) { 170 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 171 bool update_storage = 172 AddWatchedGalleryIdInfoForExtension(extension_id, gallery_id); 173 if (update_storage) 174 WriteToStorage(extension_id); 175 } 176 177 void GalleryWatchStateTracker::OnGalleryWatchRemoved( 178 const std::string& extension_id, 179 MediaGalleryPrefId gallery_id) { 180 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 181 if (!ContainsKey(watched_extensions_map_, extension_id)) 182 return; 183 watched_extensions_map_[extension_id].erase(gallery_id); 184 if (watched_extensions_map_[extension_id].empty()) 185 watched_extensions_map_.erase(extension_id); 186 WriteToStorage(extension_id); 187 } 188 189 void GalleryWatchStateTracker::OnExtensionLoaded( 190 content::BrowserContext* browser_context, 191 const Extension* extension) { 192 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 193 StateStore* storage = ExtensionSystem::Get(profile_)->state_store(); 194 if (!storage) 195 return; 196 storage->GetExtensionValue( 197 extension->id(), 198 kRegisteredGalleryWatchers, 199 base::Bind(&GalleryWatchStateTracker::ReadFromStorage, 200 AsWeakPtr(), 201 extension->id())); 202 } 203 204 void GalleryWatchStateTracker::OnExtensionUnloaded( 205 content::BrowserContext* browser_context, 206 const Extension* extension, 207 UnloadedExtensionInfo::Reason reason) { 208 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 209 if (!ContainsKey(watched_extensions_map_, extension->id())) 210 return; 211 content::BrowserThread::PostTask( 212 content::BrowserThread::FILE, FROM_HERE, 213 base::Bind(&GalleryWatchManager::OnExtensionUnloaded, 214 profile_, 215 extension->id())); 216 for (WatchedGalleriesMap::iterator iter = 217 watched_extensions_map_[extension->id()].begin(); 218 iter != watched_extensions_map_[extension->id()].end(); ++iter) { 219 iter->second = false; 220 } 221 } 222 223 void GalleryWatchStateTracker::WriteToStorage(const std::string& extension_id) { 224 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 225 StateStore* storage = ExtensionSystem::Get(profile_)->state_store(); 226 if (!storage) 227 return; 228 MediaGalleryPrefIdSet gallery_ids = 229 GetAllWatchedGalleryIDsForExtension(extension_id); 230 storage->SetExtensionValue( 231 extension_id, 232 kRegisteredGalleryWatchers, 233 WatchedGalleryIdsToValue(gallery_ids).PassAs<base::Value>()); 234 } 235 236 void GalleryWatchStateTracker::ReadFromStorage( 237 const std::string& extension_id, 238 scoped_ptr<base::Value> value) { 239 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 240 MediaGalleriesPreferences* preferences = 241 g_browser_process->media_file_system_registry()->GetPreferences(profile_); 242 base::ListValue* list = NULL; 243 if (!value.get() || !value->GetAsList(&list)) 244 return; 245 MediaGalleryPrefIdSet gallery_ids = WatchedGalleryIdsFromValue(list); 246 if (gallery_ids.empty()) 247 return; 248 249 for (MediaGalleryPrefIdSet::const_iterator id_iter = gallery_ids.begin(); 250 id_iter != gallery_ids.end(); ++id_iter) { 251 watched_extensions_map_[extension_id][*id_iter] = false; 252 SetupGalleryWatch(extension_id, *id_iter, preferences); 253 } 254 } 255 256 void GalleryWatchStateTracker::SetupGalleryWatch( 257 const std::string& extension_id, 258 MediaGalleryPrefId gallery_id, 259 MediaGalleriesPreferences* preferences) { 260 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 261 const Extension* extension = GetExtensionById(profile_, extension_id); 262 DCHECK(extension); 263 base::FilePath gallery_file_path(preferences->LookUpGalleryPathForExtension( 264 gallery_id, extension, false)); 265 if (gallery_file_path.empty()) 266 return; 267 MediaGalleriesPrivateEventRouter* router = 268 MediaGalleriesPrivateAPI::Get(profile_)->GetEventRouter(); 269 DCHECK(router); 270 content::BrowserThread::PostTaskAndReplyWithResult( 271 content::BrowserThread::FILE, 272 FROM_HERE, 273 base::Bind(&GalleryWatchManager::SetupGalleryWatch, 274 profile_, 275 gallery_id, 276 gallery_file_path, 277 extension_id, 278 router->AsWeakPtr()), 279 base::Bind(&GalleryWatchStateTracker::HandleSetupGalleryWatchResponse, 280 AsWeakPtr(), 281 extension_id, 282 gallery_id)); 283 } 284 285 void GalleryWatchStateTracker::RemoveGalleryWatch( 286 const std::string& extension_id, 287 MediaGalleryPrefId gallery_id, 288 MediaGalleriesPreferences* preferences) { 289 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 290 const Extension* extension = GetExtensionById(profile_, extension_id); 291 DCHECK(extension); 292 base::FilePath gallery_file_path(preferences->LookUpGalleryPathForExtension( 293 gallery_id, extension, true)); 294 if (gallery_file_path.empty()) 295 return; 296 content::BrowserThread::PostTask( 297 content::BrowserThread::FILE, FROM_HERE, 298 base::Bind(&GalleryWatchManager::RemoveGalleryWatch, 299 profile_, 300 gallery_file_path, 301 extension_id)); 302 watched_extensions_map_[extension_id][gallery_id] = false; 303 } 304 305 bool GalleryWatchStateTracker::HasGalleryWatchInfo( 306 const std::string& extension_id, 307 MediaGalleryPrefId gallery_id, 308 bool has_active_watcher) { 309 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 310 return (ContainsKey(watched_extensions_map_, extension_id) && 311 ContainsKey(watched_extensions_map_[extension_id], gallery_id) && 312 watched_extensions_map_[extension_id][gallery_id] == 313 has_active_watcher); 314 } 315 316 void GalleryWatchStateTracker::HandleSetupGalleryWatchResponse( 317 const std::string& extension_id, 318 MediaGalleryPrefId gallery_id, 319 bool success) { 320 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 321 if (!success) 322 return; // Failed to setup the gallery watch for the given extension. 323 AddWatchedGalleryIdInfoForExtension(extension_id, gallery_id); 324 } 325 326 bool GalleryWatchStateTracker::AddWatchedGalleryIdInfoForExtension( 327 const std::string& extension_id, 328 MediaGalleryPrefId gallery_id) { 329 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 330 if (HasGalleryWatchInfo(extension_id, gallery_id, true)) 331 return false; 332 watched_extensions_map_[extension_id][gallery_id] = true; 333 return true; 334 } 335 336 } // namespace extensions 337