Home | History | Annotate | Download | only in media_galleries_private
      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