- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / media_galleries_private / gallery_watch_state_tracker.cc
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/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"
30
31 namespace extensions {
32
33 namespace {
34
35 // State store key to track the registered gallery watchers for the extensions.
36 const char kRegisteredGalleryWatchers[] = "media_gallery_watchers";
37
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())
46       continue;
47     MediaGalleryPrefId gallery_id;
48     if (base::StringToUint64(gallery_id_str, &gallery_id))
49       gallery_ids.insert(gallery_id);
50   }
51   return gallery_ids;
52 }
53
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));
62   return list.Pass();
63 }
64
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();
69   if (!service)
70     return NULL;
71   return service->GetExtensionById(extension_id, false);
72 }
73
74 }  // namespace
75
76 GalleryWatchStateTracker::GalleryWatchStateTracker(Profile* profile)
77     : profile_(profile) {
78   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
79   DCHECK(profile_);
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);
87 }
88
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);
94 }
95
96 // static
97 GalleryWatchStateTracker* GalleryWatchStateTracker::GetForProfile(
98     Profile* profile) {
99   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
100 #if defined(OS_WIN)
101   // Gallery watch operation is supported only on windows.
102   // Please refer to crbug.com/144491 for more details.
103   DCHECK(profile);
104   MediaGalleriesPrivateAPI* private_api =
105       MediaGalleriesPrivateAPI::Get(profile);
106   if (!private_api)
107     return NULL;  // In unit tests, we don't have a MediaGalleriesPrivateAPI.
108   return private_api->GetGalleryWatchStateTracker();
109 #endif
110   return NULL;
111 }
112
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);
121 }
122
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);
131 }
132
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();
138        ++it) {
139     if (it->second.find(gallery_id) != it->second.end())
140       RemoveGalleryWatch(it->first, gallery_id, pref);
141   }
142 }
143
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();
154          ++gallery_id_iter) {
155       gallery_ids.insert(gallery_id_iter->first);
156     }
157   }
158   return gallery_ids;
159 }
160
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())
167     return;
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);
174 }
175
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);
182   if (update_storage)
183     WriteToStorage(extension_id);
184 }
185
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))
191     return;
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);
196 }
197
198 void GalleryWatchStateTracker::Observe(
199     int type,
200     const content::NotificationSource& source,
201     const content::NotificationDetails& details) {
202   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
203   switch (type) {
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();
208       if (!storage)
209         return;
210       storage->GetExtensionValue(
211           extension->id(),
212           kRegisteredGalleryWatchers,
213           base::Bind(&GalleryWatchStateTracker::ReadFromStorage,
214                      AsWeakPtr(),
215                      extension->id()));
216       break;
217     }
218     case chrome::NOTIFICATION_EXTENSION_UNLOADED: {
219       Extension* extension = const_cast<Extension*>(
220           content::Details<extensions::UnloadedExtensionInfo>(
221               details)->extension);
222       DCHECK(extension);
223       if (!ContainsKey(watched_extensions_map_, extension->id()))
224         return;
225       content::BrowserThread::PostTask(
226           content::BrowserThread::FILE, FROM_HERE,
227           base::Bind(&GalleryWatchManager::OnExtensionUnloaded,
228                      profile_,
229                      extension->id()));
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;
234       }
235       break;
236     }
237     default:
238       NOTREACHED();
239   }
240 }
241
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();
245   if (!storage)
246     return;
247   MediaGalleryPrefIdSet gallery_ids =
248       GetAllWatchedGalleryIDsForExtension(extension_id);
249   storage->SetExtensionValue(
250       extension_id,
251       kRegisteredGalleryWatchers,
252       WatchedGalleryIdsToValue(gallery_ids).PassAs<base::Value>());
253 }
254
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))
263     return;
264   MediaGalleryPrefIdSet gallery_ids = WatchedGalleryIdsFromValue(list);
265   if (gallery_ids.empty())
266     return;
267
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);
272   }
273 }
274
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);
281   DCHECK(extension);
282   base::FilePath gallery_file_path(preferences->LookUpGalleryPathForExtension(
283       gallery_id, extension, false));
284   if (gallery_file_path.empty())
285     return;
286   MediaGalleriesPrivateEventRouter* router =
287       MediaGalleriesPrivateAPI::Get(profile_)->GetEventRouter();
288   DCHECK(router);
289   content::BrowserThread::PostTaskAndReplyWithResult(
290       content::BrowserThread::FILE,
291       FROM_HERE,
292       base::Bind(&GalleryWatchManager::SetupGalleryWatch,
293                  profile_,
294                  gallery_id,
295                  gallery_file_path,
296                  extension_id,
297                  router->AsWeakPtr()),
298       base::Bind(&GalleryWatchStateTracker::HandleSetupGalleryWatchResponse,
299                  AsWeakPtr(),
300                  extension_id,
301                  gallery_id));
302 }
303
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);
310   DCHECK(extension);
311   base::FilePath gallery_file_path(preferences->LookUpGalleryPathForExtension(
312       gallery_id, extension, true));
313   if (gallery_file_path.empty())
314     return;
315   content::BrowserThread::PostTask(
316       content::BrowserThread::FILE, FROM_HERE,
317       base::Bind(&GalleryWatchManager::RemoveGalleryWatch,
318                  profile_,
319                  gallery_file_path,
320                  extension_id));
321   watched_extensions_map_[extension_id][gallery_id] = false;
322 }
323
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] ==
332               has_active_watcher);
333 }
334
335 void GalleryWatchStateTracker::HandleSetupGalleryWatchResponse(
336     const std::string& extension_id,
337     MediaGalleryPrefId gallery_id,
338     bool success) {
339   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
340   if (!success)
341     return;  // Failed to setup the gallery watch for the given extension.
342   AddWatchedGalleryIdInfoForExtension(extension_id, gallery_id);
343 }
344
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))
350     return false;
351   watched_extensions_map_[extension_id][gallery_id] = true;
352   return true;
353 }
354
355 }  // namespace extensions