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