Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / media_galleries / media_galleries_scan_result_controller.cc
1 // Copyright 2014 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 #include "chrome/browser/media_galleries/media_galleries_scan_result_controller.h"
6
7 #include <algorithm>
8 #include <list>
9
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/stl_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/browser_process.h"
16 #include "chrome/browser/media_galleries/media_file_system_registry.h"
17 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
18 #include "chrome/browser/media_galleries/media_gallery_context_menu.h"
19 #include "chrome/browser/platform_util.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/grit/generated_resources.h"
22 #include "components/storage_monitor/storage_info.h"
23 #include "components/storage_monitor/storage_monitor.h"
24 #include "content/public/browser/web_contents.h"
25 #include "extensions/common/extension.h"
26 #include "extensions/common/permissions/media_galleries_permission.h"
27 #include "extensions/common/permissions/permissions_data.h"
28 #include "ui/base/l10n/l10n_util.h"
29
30 using storage_monitor::StorageInfo;
31 using storage_monitor::StorageMonitor;
32
33 namespace {
34
35 // Comparator for sorting Entries -- more files first and then sorts by
36 // absolute path.
37 bool ScanResultsComparator(
38     const MediaGalleriesDialogController::Entry& a,
39     const MediaGalleriesDialogController::Entry& b) {
40   int a_media_count = a.pref_info.audio_count + a.pref_info.image_count +
41                       a.pref_info.video_count;
42   int b_media_count = b.pref_info.audio_count + b.pref_info.image_count +
43                       b.pref_info.video_count;
44   if (a_media_count == b_media_count)
45     return a.pref_info.AbsolutePath() < b.pref_info.AbsolutePath();
46   return a_media_count > b_media_count;
47 }
48
49 }  // namespace
50
51 // static
52 size_t MediaGalleriesScanResultController::ScanResultCountForExtension(
53     MediaGalleriesPreferences* preferences,
54     const extensions::Extension* extension) {
55   ScanResults scan_results;
56   UpdateScanResultsFromPreferences(preferences, extension,
57                                    MediaGalleryPrefIdSet(), &scan_results);
58   return scan_results.size();
59 }
60
61 MediaGalleriesScanResultController::MediaGalleriesScanResultController(
62     content::WebContents* web_contents,
63     const extensions::Extension& extension,
64     const base::Closure& on_finish)
65       : web_contents_(web_contents),
66         extension_(&extension),
67         on_finish_(on_finish),
68         create_dialog_callback_(base::Bind(&MediaGalleriesDialog::Create)) {
69   preferences_ =
70       g_browser_process->media_file_system_registry()->GetPreferences(
71           GetProfile());
72   // Passing unretained pointer is safe, since the dialog controller
73   // is self-deleting, and so won't be deleted until it can be shown
74   // and then closed.
75   preferences_->EnsureInitialized(base::Bind(
76         &MediaGalleriesScanResultController::OnPreferencesInitialized,
77         base::Unretained(this)));
78
79   // Unretained is safe because |this| owns |context_menu_|.
80   context_menu_.reset(new MediaGalleryContextMenu(base::Bind(
81           &MediaGalleriesScanResultController::DidForgetEntry,
82           base::Unretained(this))));
83 }
84
85 MediaGalleriesScanResultController::MediaGalleriesScanResultController(
86     const extensions::Extension& extension,
87     MediaGalleriesPreferences* preferences,
88     const CreateDialogCallback& create_dialog_callback,
89     const base::Closure& on_finish)
90     : web_contents_(NULL),
91       extension_(&extension),
92       on_finish_(on_finish),
93       preferences_(preferences),
94       create_dialog_callback_(create_dialog_callback) {
95   OnPreferencesInitialized();
96 }
97
98 MediaGalleriesScanResultController::~MediaGalleriesScanResultController() {
99   // |preferences_| may be NULL in tests.
100   if (preferences_)
101     preferences_->RemoveGalleryChangeObserver(this);
102   if (StorageMonitor::GetInstance())
103     StorageMonitor::GetInstance()->RemoveObserver(this);
104 }
105
106 base::string16 MediaGalleriesScanResultController::GetHeader() const {
107   return l10n_util::GetStringFUTF16(
108       IDS_MEDIA_GALLERIES_SCAN_RESULT_DIALOG_HEADER,
109       base::UTF8ToUTF16(extension_->name()));
110 }
111
112 base::string16 MediaGalleriesScanResultController::GetSubtext() const {
113   extensions::MediaGalleriesPermission::CheckParam copy_to_param(
114       extensions::MediaGalleriesPermission::kCopyToPermission);
115   extensions::MediaGalleriesPermission::CheckParam delete_param(
116       extensions::MediaGalleriesPermission::kDeletePermission);
117   const extensions::PermissionsData* permissions_data =
118       extension_->permissions_data();
119   bool has_copy_to_permission = permissions_data->CheckAPIPermissionWithParam(
120       extensions::APIPermission::kMediaGalleries, &copy_to_param);
121   bool has_delete_permission = permissions_data->CheckAPIPermissionWithParam(
122       extensions::APIPermission::kMediaGalleries, &delete_param);
123
124   int id;
125   if (has_copy_to_permission)
126     id = IDS_MEDIA_GALLERIES_SCAN_RESULT_DIALOG_SUBTEXT_READ_WRITE;
127   else if (has_delete_permission)
128     id = IDS_MEDIA_GALLERIES_SCAN_RESULT_DIALOG_SUBTEXT_READ_DELETE;
129   else
130     id = IDS_MEDIA_GALLERIES_SCAN_RESULT_DIALOG_SUBTEXT_READ_ONLY;
131
132   return l10n_util::GetStringFUTF16(id, base::UTF8ToUTF16(extension_->name()));
133 }
134
135 bool MediaGalleriesScanResultController::IsAcceptAllowed() const {
136   return true;
137 }
138
139 bool MediaGalleriesScanResultController::ShouldShowFolderViewer(
140     const Entry& entry) const {
141   return entry.pref_info.IsGalleryAvailable();
142 }
143
144 std::vector<base::string16>
145 MediaGalleriesScanResultController::GetSectionHeaders() const {
146   std::vector<base::string16> result;
147   result.push_back(base::string16());
148   return result;
149 }
150
151 MediaGalleriesDialogController::Entries
152 MediaGalleriesScanResultController::GetSectionEntries(
153     size_t index) const {
154   DCHECK_EQ(0U, index);
155   Entries result;
156   result.reserve(scan_results_.size());
157   for (ScanResults::const_iterator it = scan_results_.begin();
158        it != scan_results_.end();
159        ++it) {
160     result.push_back(it->second);
161   }
162   std::sort(result.begin(), result.end(), ScanResultsComparator);
163   return result;
164 }
165
166 base::string16
167 MediaGalleriesScanResultController::GetAuxiliaryButtonText() const {
168   return base::string16();
169 }
170
171 void MediaGalleriesScanResultController::DidClickAuxiliaryButton() {
172   NOTREACHED();
173 }
174
175 void MediaGalleriesScanResultController::DidToggleEntry(
176     MediaGalleryPrefId pref_id, bool selected) {
177   DCHECK(ContainsKey(scan_results_, pref_id));
178   ScanResults::iterator entry = scan_results_.find(pref_id);
179   entry->second.selected = selected;
180 }
181
182 void MediaGalleriesScanResultController::DidClickOpenFolderViewer(
183     MediaGalleryPrefId pref_id) {
184   ScanResults::const_iterator entry = scan_results_.find(pref_id);
185   if (entry == scan_results_.end()) {
186     NOTREACHED();
187     return;
188   }
189   platform_util::OpenItem(GetProfile(), entry->second.pref_info.AbsolutePath());
190 }
191
192 void MediaGalleriesScanResultController::DidForgetEntry(
193     MediaGalleryPrefId pref_id) {
194   media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS_FORGET_GALLERY);
195   results_to_remove_.insert(pref_id);
196   scan_results_.erase(pref_id);
197   dialog_->UpdateGalleries();
198 }
199
200 base::string16 MediaGalleriesScanResultController::GetAcceptButtonText() const {
201   return l10n_util::GetStringUTF16(
202       IDS_MEDIA_GALLERIES_SCAN_RESULT_DIALOG_CONFIRM);
203 }
204
205 void MediaGalleriesScanResultController::DialogFinished(bool accepted) {
206   // No longer interested in preference updates (and the below code generates
207   // some).
208   // |preferences_| may be NULL in tests.
209   if (preferences_)
210     preferences_->RemoveGalleryChangeObserver(this);
211
212   if (accepted) {
213     DCHECK(preferences_);
214     media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS_ACCEPTED);
215     int granted = 0;
216     int total = 0;
217     for (ScanResults::const_iterator it = scan_results_.begin();
218          it != scan_results_.end();
219          ++it) {
220       if (it->second.selected) {
221         bool changed = preferences_->SetGalleryPermissionForExtension(
222               *extension_, it->first, true);
223         DCHECK(changed);
224         granted++;
225       }
226       total++;
227     }
228     if (total > 0) {
229       UMA_HISTOGRAM_PERCENTAGE("MediaGalleries.ScanGalleriesGranted",
230                                (granted * 100 / total));
231     }
232     for (MediaGalleryPrefIdSet::const_iterator it = results_to_remove_.begin();
233         it != results_to_remove_.end();
234         ++it) {
235       preferences_->ForgetGalleryById(*it);
236     }
237   } else {
238     media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS_CANCELLED);
239   }
240
241   on_finish_.Run();
242   delete this;
243 }
244
245 ui::MenuModel* MediaGalleriesScanResultController::GetContextMenu(
246     MediaGalleryPrefId id) {
247   context_menu_->set_pref_id(id);
248   return context_menu_.get();
249 }
250
251 content::WebContents* MediaGalleriesScanResultController::WebContents() {
252   return web_contents_;
253 }
254
255 // static
256 void MediaGalleriesScanResultController::UpdateScanResultsFromPreferences(
257     MediaGalleriesPreferences* preferences,
258     const extensions::Extension* extension,
259     MediaGalleryPrefIdSet ignore_list,
260     ScanResults* scan_results) {
261   DCHECK(preferences->IsInitialized());
262   const MediaGalleriesPrefInfoMap& galleries = preferences->known_galleries();
263   MediaGalleryPrefIdSet permitted =
264       preferences->GalleriesForExtension(*extension);
265
266   // Add or update any scan results that the extension doesn't already have
267   // access to or isn't in |ignore_list|.
268   for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin();
269        it != galleries.end();
270        ++it) {
271     const MediaGalleryPrefInfo& gallery = it->second;
272     if ((gallery.audio_count || gallery.image_count || gallery.video_count) &&
273         !gallery.IsBlackListedType() &&
274         !ContainsKey(permitted, gallery.pref_id) &&
275         !ContainsKey(ignore_list, gallery.pref_id)) {
276       ScanResults::iterator existing = scan_results->find(gallery.pref_id);
277       if (existing == scan_results->end()) {
278         // Default to selected.
279         (*scan_results)[gallery.pref_id] = Entry(gallery, true);
280       } else {
281         // Update pref_info, in case anything has been updated.
282         existing->second.pref_info = gallery;
283       }
284     }
285   }
286
287   // Remove anything from |scan_results| that's no longer valid or the user
288   // already has access to.
289   std::list<ScanResults::iterator> to_remove;
290   for (ScanResults::iterator it = scan_results->begin();
291        it != scan_results->end();
292        ++it) {
293     MediaGalleriesPrefInfoMap::const_iterator pref_gallery =
294         galleries.find(it->first);
295     if (pref_gallery == galleries.end() ||
296         pref_gallery->second.IsBlackListedType() ||
297         ContainsKey(permitted, it->first)) {
298       to_remove.push_back(it);
299     }
300   }
301   while (!to_remove.empty()) {
302     scan_results->erase(to_remove.front());
303     to_remove.pop_front();
304   }
305 }
306
307 void MediaGalleriesScanResultController::OnPreferencesInitialized() {
308   // These may be NULL in tests.
309   if (StorageMonitor::GetInstance())
310     StorageMonitor::GetInstance()->AddObserver(this);
311   if (preferences_) {
312     preferences_->AddGalleryChangeObserver(this);
313     UpdateScanResultsFromPreferences(preferences_, extension_,
314                                      results_to_remove_, &scan_results_);
315   }
316
317   dialog_.reset(create_dialog_callback_.Run(this));
318 }
319
320 void MediaGalleriesScanResultController::OnPreferenceUpdate(
321     const std::string& extension_id) {
322   if (extension_id == extension_->id()) {
323     UpdateScanResultsFromPreferences(preferences_, extension_,
324                                      results_to_remove_, &scan_results_);
325     dialog_->UpdateGalleries();
326   }
327 }
328
329 void MediaGalleriesScanResultController::OnRemovableDeviceUpdate(
330     const std::string device_id) {
331   for (ScanResults::const_iterator it = scan_results_.begin();
332        it != scan_results_.end();
333        ++it) {
334     if (it->second.pref_info.device_id == device_id) {
335       dialog_->UpdateGalleries();
336       return;
337     }
338   }
339 }
340
341 Profile* MediaGalleriesScanResultController::GetProfile() const {
342   return Profile::FromBrowserContext(web_contents_->GetBrowserContext());
343 }
344
345 void MediaGalleriesScanResultController::OnRemovableStorageAttached(
346     const StorageInfo& info) {
347   OnRemovableDeviceUpdate(info.device_id());
348 }
349
350 void MediaGalleriesScanResultController::OnRemovableStorageDetached(
351     const StorageInfo& info) {
352   OnRemovableDeviceUpdate(info.device_id());
353 }
354
355 void MediaGalleriesScanResultController::OnPermissionAdded(
356     MediaGalleriesPreferences* /*pref*/,
357     const std::string& extension_id,
358     MediaGalleryPrefId /*pref_id*/) {
359   OnPreferenceUpdate(extension_id);
360 }
361
362 void MediaGalleriesScanResultController::OnPermissionRemoved(
363     MediaGalleriesPreferences* /*pref*/,
364     const std::string& extension_id,
365     MediaGalleryPrefId /*pref_id*/) {
366   OnPreferenceUpdate(extension_id);
367 }
368
369 void MediaGalleriesScanResultController::OnGalleryAdded(
370     MediaGalleriesPreferences* /*prefs*/,
371     MediaGalleryPrefId /*pref_id*/) {
372   OnPreferenceUpdate(extension_->id());
373 }
374
375 void MediaGalleriesScanResultController::OnGalleryRemoved(
376     MediaGalleriesPreferences* /*prefs*/,
377     MediaGalleryPrefId /*pref_id*/) {
378   OnPreferenceUpdate(extension_->id());
379 }
380
381 void MediaGalleriesScanResultController::OnGalleryInfoUpdated(
382     MediaGalleriesPreferences* /*prefs*/,
383     MediaGalleryPrefId /*pref_id*/) {
384   OnPreferenceUpdate(extension_->id());
385 }