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