Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / media_galleries / media_galleries_dialog_controller.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 #include "chrome/browser/media_galleries/media_galleries_dialog_controller.h"
6
7 #include "base/base_paths.h"
8 #include "base/path_service.h"
9 #include "base/stl_util.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
13 #include "chrome/browser/media_galleries/media_file_system_registry.h"
14 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
15 #include "chrome/browser/media_galleries/media_gallery_context_menu.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/chrome_select_file_policy.h"
18 #include "chrome/common/extensions/permissions/media_galleries_permission.h"
19 #include "components/storage_monitor/storage_info.h"
20 #include "components/storage_monitor/storage_monitor.h"
21 #include "content/public/browser/web_contents.h"
22 #include "content/public/browser/web_contents_view.h"
23 #include "extensions/browser/extension_prefs.h"
24 #include "extensions/common/extension.h"
25 #include "extensions/common/permissions/permissions_data.h"
26 #include "grit/generated_resources.h"
27 #include "ui/base/l10n/l10n_util.h"
28 #include "ui/base/models/simple_menu_model.h"
29 #include "ui/base/text/bytes_formatting.h"
30
31 using extensions::APIPermission;
32 using extensions::Extension;
33
34 namespace {
35
36 // Comparator for sorting GalleryPermissionsVector -- sorts
37 // allowed galleries low, and then sorts by absolute path.
38 bool GalleriesVectorComparator(
39     const MediaGalleriesDialogController::GalleryPermission& a,
40     const MediaGalleriesDialogController::GalleryPermission& b) {
41   if (a.allowed && !b.allowed)
42     return true;
43   if (!a.allowed && b.allowed)
44     return false;
45
46   return a.pref_info.AbsolutePath() < b.pref_info.AbsolutePath();
47 }
48
49 }  // namespace
50
51 MediaGalleriesDialogController::MediaGalleriesDialogController(
52     content::WebContents* web_contents,
53     const Extension& extension,
54     const base::Closure& on_finish)
55       : web_contents_(web_contents),
56         extension_(&extension),
57         on_finish_(on_finish),
58         preferences_(
59             g_browser_process->media_file_system_registry()->GetPreferences(
60                 GetProfile())),
61         create_dialog_callback_(base::Bind(&MediaGalleriesDialog::Create)) {
62   // Passing unretained pointer is safe, since the dialog controller
63   // is self-deleting, and so won't be deleted until it can be shown
64   // and then closed.
65   preferences_->EnsureInitialized(
66       base::Bind(&MediaGalleriesDialogController::OnPreferencesInitialized,
67                  base::Unretained(this)));
68
69   // Unretained is safe because |this| owns |context_menu_|.
70   context_menu_.reset(
71       new MediaGalleryContextMenu(
72           base::Bind(&MediaGalleriesDialogController::DidForgetGallery,
73                      base::Unretained(this))));
74 }
75
76 void MediaGalleriesDialogController::OnPreferencesInitialized() {
77   if (StorageMonitor::GetInstance())
78     StorageMonitor::GetInstance()->AddObserver(this);
79
80   // |preferences_| may be NULL in tests.
81   if (preferences_) {
82     preferences_->AddGalleryChangeObserver(this);
83     InitializePermissions();
84   }
85
86   dialog_.reset(create_dialog_callback_.Run(this));
87 }
88
89 MediaGalleriesDialogController::MediaGalleriesDialogController(
90     const extensions::Extension& extension,
91     MediaGalleriesPreferences* preferences,
92     const CreateDialogCallback& create_dialog_callback,
93     const base::Closure& on_finish)
94     : web_contents_(NULL),
95       extension_(&extension),
96       on_finish_(on_finish),
97       preferences_(preferences),
98       create_dialog_callback_(create_dialog_callback) {
99   OnPreferencesInitialized();
100 }
101
102 MediaGalleriesDialogController::~MediaGalleriesDialogController() {
103   if (StorageMonitor::GetInstance())
104     StorageMonitor::GetInstance()->RemoveObserver(this);
105
106   // |preferences_| may be NULL in tests.
107   if (preferences_)
108     preferences_->RemoveGalleryChangeObserver(this);
109
110   if (select_folder_dialog_.get())
111     select_folder_dialog_->ListenerDestroyed();
112 }
113
114 base::string16 MediaGalleriesDialogController::GetHeader() const {
115   return l10n_util::GetStringFUTF16(IDS_MEDIA_GALLERIES_DIALOG_HEADER,
116                                     base::UTF8ToUTF16(extension_->name()));
117 }
118
119 base::string16 MediaGalleriesDialogController::GetSubtext() const {
120   extensions::MediaGalleriesPermission::CheckParam copy_to_param(
121       extensions::MediaGalleriesPermission::kCopyToPermission);
122   extensions::MediaGalleriesPermission::CheckParam delete_param(
123       extensions::MediaGalleriesPermission::kDeletePermission);
124   bool has_copy_to_permission =
125       extensions::PermissionsData::CheckAPIPermissionWithParam(
126           extension_, APIPermission::kMediaGalleries, &copy_to_param);
127   bool has_delete_permission =
128       extensions::PermissionsData::CheckAPIPermissionWithParam(
129           extension_, APIPermission::kMediaGalleries, &delete_param);
130
131   int id;
132   if (has_copy_to_permission)
133     id = IDS_MEDIA_GALLERIES_DIALOG_SUBTEXT_READ_WRITE;
134   else if (has_delete_permission)
135     id = IDS_MEDIA_GALLERIES_DIALOG_SUBTEXT_READ_DELETE;
136   else
137     id = IDS_MEDIA_GALLERIES_DIALOG_SUBTEXT_READ_ONLY;
138
139   return l10n_util::GetStringFUTF16(id, base::UTF8ToUTF16(extension_->name()));
140 }
141
142 base::string16 MediaGalleriesDialogController::GetUnattachedLocationsHeader()
143     const {
144   return l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_UNATTACHED_LOCATIONS);
145 }
146
147 // TODO(gbillock): Call this something a bit more connected to the
148 // messaging in the dialog.
149 bool MediaGalleriesDialogController::HasPermittedGalleries() const {
150   for (KnownGalleryPermissions::const_iterator iter = known_galleries_.begin();
151        iter != known_galleries_.end(); ++iter) {
152     if (iter->second.allowed)
153       return true;
154   }
155
156   // Do this? Views did.
157   if (new_galleries_.size() > 0)
158     return true;
159
160   return false;
161 }
162
163 // Note: sorts by display criterion: GalleriesVectorComparator.
164 void MediaGalleriesDialogController::FillPermissions(
165     bool attached,
166     MediaGalleriesDialogController::GalleryPermissionsVector* permissions)
167     const {
168   for (KnownGalleryPermissions::const_iterator iter = known_galleries_.begin();
169        iter != known_galleries_.end(); ++iter) {
170     if (!ContainsKey(forgotten_gallery_ids_, iter->first) &&
171         attached == iter->second.pref_info.IsGalleryAvailable()) {
172       permissions->push_back(iter->second);
173     }
174   }
175   for (GalleryPermissionsVector::const_iterator iter = new_galleries_.begin();
176        iter != new_galleries_.end(); ++iter) {
177     if (attached == iter->pref_info.IsGalleryAvailable()) {
178       permissions->push_back(*iter);
179     }
180   }
181
182   std::sort(permissions->begin(), permissions->end(),
183             GalleriesVectorComparator);
184 }
185
186 MediaGalleriesDialogController::GalleryPermissionsVector
187 MediaGalleriesDialogController::AttachedPermissions() const {
188   GalleryPermissionsVector attached;
189   FillPermissions(true, &attached);
190   return attached;
191 }
192
193 MediaGalleriesDialogController::GalleryPermissionsVector
194 MediaGalleriesDialogController::UnattachedPermissions() const {
195   GalleryPermissionsVector unattached;
196   FillPermissions(false, &unattached);
197   return unattached;
198 }
199
200 void MediaGalleriesDialogController::OnAddFolderClicked() {
201   base::FilePath default_path =
202       extensions::file_system_api::GetLastChooseEntryDirectory(
203           extensions::ExtensionPrefs::Get(GetProfile()), extension_->id());
204   if (default_path.empty())
205     PathService::Get(base::DIR_USER_DESKTOP, &default_path);
206   select_folder_dialog_ =
207       ui::SelectFileDialog::Create(this, new ChromeSelectFilePolicy(NULL));
208   select_folder_dialog_->SelectFile(
209       ui::SelectFileDialog::SELECT_FOLDER,
210       l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY_TITLE),
211       default_path,
212       NULL,
213       0,
214       base::FilePath::StringType(),
215       web_contents_->GetView()->GetTopLevelNativeWindow(),
216       NULL);
217 }
218
219 void MediaGalleriesDialogController::DidToggleGalleryId(
220     MediaGalleryPrefId gallery_id,
221     bool enabled) {
222   // Check known galleries.
223   KnownGalleryPermissions::iterator iter =
224       known_galleries_.find(gallery_id);
225   if (iter != known_galleries_.end()) {
226     if (iter->second.allowed == enabled)
227       return;
228
229     iter->second.allowed = enabled;
230     if (ContainsKey(toggled_galleries_, gallery_id))
231       toggled_galleries_.erase(gallery_id);
232     else
233       toggled_galleries_.insert(gallery_id);
234     return;
235   }
236
237   // Don't sort -- the dialog is open, and we don't want to adjust any
238   // positions for future updates to the dialog contents until they are
239   // redrawn.
240 }
241
242 void MediaGalleriesDialogController::DidToggleNewGallery(
243     const MediaGalleryPrefInfo& gallery,
244     bool enabled) {
245   for (GalleryPermissionsVector::iterator iter = new_galleries_.begin();
246        iter != new_galleries_.end(); ++iter) {
247     if (iter->pref_info.path == gallery.path &&
248         iter->pref_info.device_id == gallery.device_id) {
249       iter->allowed = enabled;
250       return;
251     }
252   }
253 }
254
255 void MediaGalleriesDialogController::DidForgetGallery(
256     MediaGalleryPrefId pref_id) {
257   // TODO(scr): remove from new_galleries_ if it's in there.  Should
258   // new_galleries be a set? Why don't new_galleries allow context clicking?
259   DCHECK(ContainsKey(known_galleries_, pref_id));
260   forgotten_gallery_ids_.insert(pref_id);
261   dialog_->UpdateGalleries();
262 }
263
264 void MediaGalleriesDialogController::DialogFinished(bool accepted) {
265   // The dialog has finished, so there is no need to watch for more updates
266   // from |preferences_|.
267   // |preferences_| may be NULL in tests.
268   if (preferences_)
269     preferences_->RemoveGalleryChangeObserver(this);
270
271   if (accepted)
272     SavePermissions();
273
274   on_finish_.Run();
275
276   delete this;
277 }
278
279 content::WebContents* MediaGalleriesDialogController::web_contents() {
280   return web_contents_;
281 }
282
283 void MediaGalleriesDialogController::FileSelected(const base::FilePath& path,
284                                                   int /*index*/,
285                                                   void* /*params*/) {
286   extensions::file_system_api::SetLastChooseEntryDirectory(
287       extensions::ExtensionPrefs::Get(GetProfile()),
288       extension_->id(),
289       path);
290
291   // Try to find it in the prefs.
292   MediaGalleryPrefInfo gallery;
293   DCHECK(preferences_);
294   bool gallery_exists = preferences_->LookUpGalleryByPath(path, &gallery);
295   if (gallery_exists && !gallery.IsBlackListedType()) {
296     // The prefs are in sync with |known_galleries_|, so it should exist in
297     // |known_galleries_| as well. User selecting a known gallery effectively
298     // just sets the gallery to permitted.
299     DCHECK(ContainsKey(known_galleries_, gallery.pref_id));
300     forgotten_gallery_ids_.erase(gallery.pref_id);
301     dialog_->UpdateGalleries();
302     return;
303   }
304
305   // Try to find it in |new_galleries_| (user added same folder twice).
306   for (GalleryPermissionsVector::iterator iter = new_galleries_.begin();
307        iter != new_galleries_.end(); ++iter) {
308     if (iter->pref_info.path == gallery.path &&
309         iter->pref_info.device_id == gallery.device_id) {
310       iter->allowed = true;
311       dialog_->UpdateGalleries();
312       return;
313     }
314   }
315
316   // Lastly, if not found, add a new gallery to |new_galleries_|.
317   // Note that it will have prefId = kInvalidMediaGalleryPrefId.
318   new_galleries_.push_back(GalleryPermission(gallery, true));
319   dialog_->UpdateGalleries();
320 }
321
322 void MediaGalleriesDialogController::OnRemovableStorageAttached(
323     const StorageInfo& info) {
324   UpdateGalleriesOnDeviceEvent(info.device_id());
325 }
326
327 void MediaGalleriesDialogController::OnRemovableStorageDetached(
328     const StorageInfo& info) {
329   UpdateGalleriesOnDeviceEvent(info.device_id());
330 }
331
332 void MediaGalleriesDialogController::OnPermissionAdded(
333     MediaGalleriesPreferences* /* prefs */,
334     const std::string& extension_id,
335     MediaGalleryPrefId /* pref_id */) {
336   if (extension_id != extension_->id())
337     return;
338   UpdateGalleriesOnPreferencesEvent();
339 }
340
341 void MediaGalleriesDialogController::OnPermissionRemoved(
342     MediaGalleriesPreferences* /* prefs */,
343     const std::string& extension_id,
344     MediaGalleryPrefId /* pref_id */) {
345   if (extension_id != extension_->id())
346     return;
347   UpdateGalleriesOnPreferencesEvent();
348 }
349
350 void MediaGalleriesDialogController::OnGalleryAdded(
351     MediaGalleriesPreferences* /* prefs */,
352     MediaGalleryPrefId /* pref_id */) {
353   UpdateGalleriesOnPreferencesEvent();
354 }
355
356 void MediaGalleriesDialogController::OnGalleryRemoved(
357     MediaGalleriesPreferences* /* prefs */,
358     MediaGalleryPrefId /* pref_id */) {
359   UpdateGalleriesOnPreferencesEvent();
360 }
361
362 void MediaGalleriesDialogController::OnGalleryInfoUpdated(
363     MediaGalleriesPreferences* prefs,
364     MediaGalleryPrefId pref_id) {
365   DCHECK(preferences_);
366   const MediaGalleriesPrefInfoMap& pref_galleries =
367       preferences_->known_galleries();
368   MediaGalleriesPrefInfoMap::const_iterator pref_it =
369       pref_galleries.find(pref_id);
370   if (pref_it == pref_galleries.end())
371     return;
372   const MediaGalleryPrefInfo& gallery_info = pref_it->second;
373   UpdateGalleriesOnDeviceEvent(gallery_info.device_id);
374 }
375
376 void MediaGalleriesDialogController::InitializePermissions() {
377   known_galleries_.clear();
378   DCHECK(preferences_);
379   const MediaGalleriesPrefInfoMap& galleries = preferences_->known_galleries();
380   for (MediaGalleriesPrefInfoMap::const_iterator iter = galleries.begin();
381        iter != galleries.end();
382        ++iter) {
383     const MediaGalleryPrefInfo& gallery = iter->second;
384     if (gallery.IsBlackListedType())
385       continue;
386
387     known_galleries_[iter->first] = GalleryPermission(gallery, false);
388   }
389
390   MediaGalleryPrefIdSet permitted =
391       preferences_->GalleriesForExtension(*extension_);
392
393   for (MediaGalleryPrefIdSet::iterator iter = permitted.begin();
394        iter != permitted.end(); ++iter) {
395     if (ContainsKey(toggled_galleries_, *iter))
396       continue;
397     DCHECK(ContainsKey(known_galleries_, *iter));
398     known_galleries_[*iter].allowed = true;
399   }
400 }
401
402 void MediaGalleriesDialogController::SavePermissions() {
403   DCHECK(preferences_);
404   media_galleries::UsageCount(media_galleries::SAVE_DIALOG);
405   for (KnownGalleryPermissions::const_iterator iter = known_galleries_.begin();
406        iter != known_galleries_.end(); ++iter) {
407     if (ContainsKey(forgotten_gallery_ids_, iter->first)) {
408       preferences_->ForgetGalleryById(iter->first);
409     } else {
410       bool changed = preferences_->SetGalleryPermissionForExtension(
411           *extension_, iter->first, iter->second.allowed);
412       if (changed) {
413         if (iter->second.allowed) {
414           media_galleries::UsageCount(
415               media_galleries::DIALOG_PERMISSION_ADDED);
416         } else {
417           media_galleries::UsageCount(
418               media_galleries::DIALOG_PERMISSION_REMOVED);
419         }
420       }
421     }
422   }
423
424   for (GalleryPermissionsVector::const_iterator iter = new_galleries_.begin();
425        iter != new_galleries_.end(); ++iter) {
426     media_galleries::UsageCount(media_galleries::DIALOG_GALLERY_ADDED);
427     // If the user added a gallery then unchecked it, forget about it.
428     if (!iter->allowed)
429       continue;
430
431     // TODO(gbillock): Should be adding volume metadata during FileSelected.
432     const MediaGalleryPrefInfo& gallery = iter->pref_info;
433     MediaGalleryPrefId id = preferences_->AddGallery(
434         gallery.device_id, gallery.path, MediaGalleryPrefInfo::kUserAdded,
435         gallery.volume_label, gallery.vendor_name, gallery.model_name,
436         gallery.total_size_in_bytes, gallery.last_attach_time, 0, 0, 0);
437     preferences_->SetGalleryPermissionForExtension(*extension_, id, true);
438   }
439 }
440
441 void MediaGalleriesDialogController::UpdateGalleriesOnPreferencesEvent() {
442   // Merge in the permissions from |preferences_|. Afterwards,
443   // |known_galleries_| may contain galleries that no longer belong there,
444   // but the code below will put |known_galleries_| back in a consistent state.
445   InitializePermissions();
446
447   // Look for duplicate entries in |new_galleries_| in case one was added
448   // in another dialog.
449   for (KnownGalleryPermissions::iterator it = known_galleries_.begin();
450        it != known_galleries_.end();
451        ++it) {
452     GalleryPermission& gallery = it->second;
453     for (GalleryPermissionsVector::iterator new_it = new_galleries_.begin();
454          new_it != new_galleries_.end();
455          ++new_it) {
456       if (new_it->pref_info.path == gallery.pref_info.path &&
457           new_it->pref_info.device_id == gallery.pref_info.device_id) {
458         // Found duplicate entry. Get the existing permission from it and then
459         // remove it.
460         gallery.allowed = new_it->allowed;
461         new_galleries_.erase(new_it);
462         break;
463       }
464     }
465   }
466
467   dialog_->UpdateGalleries();
468 }
469
470 void MediaGalleriesDialogController::UpdateGalleriesOnDeviceEvent(
471     const std::string& device_id) {
472   dialog_->UpdateGalleries();
473 }
474
475 ui::MenuModel* MediaGalleriesDialogController::GetContextMenu(
476     MediaGalleryPrefId id) {
477   context_menu_->set_pref_id(id);
478   return context_menu_.get();
479 }
480
481 Profile* MediaGalleriesDialogController::GetProfile() {
482   return Profile::FromBrowserContext(web_contents_->GetBrowserContext());
483 }
484
485 // MediaGalleries dialog -------------------------------------------------------
486
487 MediaGalleriesDialog::~MediaGalleriesDialog() {}