• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/extensions/extension_prefs.h"
14 #include "chrome/browser/media_galleries/media_file_system_registry.h"
15 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/storage_monitor/storage_info.h"
18 #include "chrome/browser/storage_monitor/storage_monitor.h"
19 #include "chrome/browser/ui/chrome_select_file_policy.h"
20 #include "chrome/common/extensions/permissions/media_galleries_permission.h"
21 #include "content/public/browser/web_contents.h"
22 #include "content/public/browser/web_contents_view.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.
GalleriesVectorComparator(const MediaGalleriesDialogController::GalleryPermission & a,const MediaGalleriesDialogController::GalleryPermission & b)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:
GalleryContextMenuModel(MediaGalleriesDialogController * controller)52   explicit GalleryContextMenuModel(MediaGalleriesDialogController* controller)
53       : controller_(controller), id_(kInvalidMediaGalleryPrefId) {}
~GalleryContextMenuModel()54   virtual ~GalleryContextMenuModel() {}
55 
set_media_gallery_pref_id(MediaGalleryPrefId id)56   void set_media_gallery_pref_id(MediaGalleryPrefId id) {
57     id_ = id;
58   }
59 
IsCommandIdChecked(int command_id) const60   virtual bool IsCommandIdChecked(int command_id) const OVERRIDE {
61     return false;
62   }
IsCommandIdEnabled(int command_id) const63   virtual bool IsCommandIdEnabled(int command_id) const OVERRIDE {
64     return true;
65   }
IsCommandIdVisible(int command_id) const66   virtual bool IsCommandIdVisible(int command_id) const OVERRIDE {
67     return true;
68   }
69 
GetAcceleratorForCommandId(int command_id,ui::Accelerator * accelerator)70   virtual bool GetAcceleratorForCommandId(
71       int command_id, ui::Accelerator* accelerator) OVERRIDE {
72     return false;
73   }
74 
ExecuteCommand(int command_id,int event_flags)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 
MediaGalleriesDialogController(content::WebContents * web_contents,const Extension & extension,const base::Closure & on_finish)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 
OnPreferencesInitialized()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 
MediaGalleriesDialogController(const extensions::Extension & extension)119 MediaGalleriesDialogController::MediaGalleriesDialogController(
120     const extensions::Extension& extension)
121     : web_contents_(NULL),
122       extension_(&extension),
123       preferences_(NULL) {}
124 
~MediaGalleriesDialogController()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 
GetHeader() const133 base::string16 MediaGalleriesDialogController::GetHeader() const {
134   return l10n_util::GetStringFUTF16(IDS_MEDIA_GALLERIES_DIALOG_HEADER,
135                                     UTF8ToUTF16(extension_->name()));
136 }
137 
GetSubtext() const138 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, UTF8ToUTF16(extension_->name()));
159 }
160 
GetUnattachedLocationsHeader() const161 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.
HasPermittedGalleries() const168 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.
FillPermissions(bool attached,MediaGalleriesDialogController::GalleryPermissionsVector * permissions) const183 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
AttachedPermissions() const203 MediaGalleriesDialogController::AttachedPermissions() const {
204   GalleryPermissionsVector attached;
205   FillPermissions(true, &attached);
206   return attached;
207 }
208 
209 MediaGalleriesDialogController::GalleryPermissionsVector
UnattachedPermissions() const210 MediaGalleriesDialogController::UnattachedPermissions() const {
211   GalleryPermissionsVector unattached;
212   FillPermissions(false, &unattached);
213   return unattached;
214 }
215 
OnAddFolderClicked()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 
DidToggleGalleryId(MediaGalleryPrefId gallery_id,bool enabled)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 
DidToggleNewGallery(const MediaGalleryPrefInfo & gallery,bool enabled)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 
DidForgetGallery(MediaGalleryPrefId pref_id)271 void MediaGalleriesDialogController::DidForgetGallery(
272     MediaGalleryPrefId pref_id) {
273   DCHECK(preferences_);
274   preferences_->ForgetGalleryById(pref_id);
275 }
276 
DialogFinished(bool accepted)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 
web_contents()291 content::WebContents* MediaGalleriesDialogController::web_contents() {
292   return web_contents_;
293 }
294 
FileSelected(const base::FilePath & path,int,void *)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.type != MediaGalleryPrefInfo::kBlackListed) {
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 
OnRemovableStorageAttached(const StorageInfo & info)332 void MediaGalleriesDialogController::OnRemovableStorageAttached(
333     const StorageInfo& info) {
334   UpdateGalleriesOnDeviceEvent(info.device_id());
335 }
336 
OnRemovableStorageDetached(const StorageInfo & info)337 void MediaGalleriesDialogController::OnRemovableStorageDetached(
338     const StorageInfo& info) {
339   UpdateGalleriesOnDeviceEvent(info.device_id());
340 }
341 
OnPermissionAdded(MediaGalleriesPreferences *,const std::string & extension_id,MediaGalleryPrefId)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 
OnPermissionRemoved(MediaGalleriesPreferences *,const std::string & extension_id,MediaGalleryPrefId)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 
OnGalleryAdded(MediaGalleriesPreferences *,MediaGalleryPrefId)360 void MediaGalleriesDialogController::OnGalleryAdded(
361     MediaGalleriesPreferences* /* prefs */,
362     MediaGalleryPrefId /* pref_id */) {
363   UpdateGalleriesOnPreferencesEvent();
364 }
365 
OnGalleryRemoved(MediaGalleriesPreferences *,MediaGalleryPrefId)366 void MediaGalleriesDialogController::OnGalleryRemoved(
367     MediaGalleriesPreferences* /* prefs */,
368     MediaGalleryPrefId /* pref_id */) {
369   UpdateGalleriesOnPreferencesEvent();
370 }
371 
OnGalleryInfoUpdated(MediaGalleriesPreferences * prefs,MediaGalleryPrefId pref_id)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 
InitializePermissions()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.type == MediaGalleryPrefInfo::kBlackListed) {
393       continue;
394     }
395 
396     known_galleries_[iter->first] = GalleryPermission(gallery, false);
397   }
398 
399   MediaGalleryPrefIdSet permitted =
400       preferences_->GalleriesForExtension(*extension_);
401 
402   for (MediaGalleryPrefIdSet::iterator iter = permitted.begin();
403        iter != permitted.end(); ++iter) {
404     if (ContainsKey(toggled_galleries_, *iter))
405       continue;
406     DCHECK(ContainsKey(known_galleries_, *iter));
407     known_galleries_[*iter].allowed = true;
408   }
409 }
410 
SavePermissions()411 void MediaGalleriesDialogController::SavePermissions() {
412   media_galleries::UsageCount(media_galleries::SAVE_DIALOG);
413   for (KnownGalleryPermissions::const_iterator iter = known_galleries_.begin();
414        iter != known_galleries_.end(); ++iter) {
415     bool changed = preferences_->SetGalleryPermissionForExtension(
416         *extension_, iter->first, iter->second.allowed);
417     if (changed) {
418       if (iter->second.allowed)
419         media_galleries::UsageCount(media_galleries::DIALOG_PERMISSION_ADDED);
420       else
421         media_galleries::UsageCount(media_galleries::DIALOG_PERMISSION_REMOVED);
422     }
423   }
424 
425   for (GalleryPermissionsVector::const_iterator iter = new_galleries_.begin();
426        iter != new_galleries_.end(); ++iter) {
427     media_galleries::UsageCount(media_galleries::DIALOG_GALLERY_ADDED);
428     // If the user added a gallery then unchecked it, forget about it.
429     if (!iter->allowed)
430       continue;
431 
432     // TODO(gbillock): Should be adding volume metadata during FileSelected.
433     const MediaGalleryPrefInfo& gallery = iter->pref_info;
434     MediaGalleryPrefId id = preferences_->AddGallery(
435         gallery.device_id, gallery.path, true,
436         gallery.volume_label, gallery.vendor_name, gallery.model_name,
437         gallery.total_size_in_bytes, gallery.last_attach_time);
438     preferences_->SetGalleryPermissionForExtension(*extension_, id, true);
439   }
440 }
441 
UpdateGalleriesOnPreferencesEvent()442 void MediaGalleriesDialogController::UpdateGalleriesOnPreferencesEvent() {
443   // Merge in the permissions from |preferences_|. Afterwards,
444   // |known_galleries_| may contain galleries that no longer belong there,
445   // but the code below will put |known_galleries_| back in a consistent state.
446   InitializePermissions();
447 
448   // Look for duplicate entries in |new_galleries_| in case one was added
449   // in another dialog.
450   for (KnownGalleryPermissions::iterator it = known_galleries_.begin();
451        it != known_galleries_.end();
452        ++it) {
453     GalleryPermission& gallery = it->second;
454     for (GalleryPermissionsVector::iterator new_it = new_galleries_.begin();
455          new_it != new_galleries_.end();
456          ++new_it) {
457       if (new_it->pref_info.path == gallery.pref_info.path &&
458           new_it->pref_info.device_id == gallery.pref_info.device_id) {
459         // Found duplicate entry. Get the existing permission from it and then
460         // remove it.
461         gallery.allowed = new_it->allowed;
462         new_galleries_.erase(new_it);
463         break;
464       }
465     }
466   }
467 
468   dialog_->UpdateGalleries();
469 }
470 
UpdateGalleriesOnDeviceEvent(const std::string & device_id)471 void MediaGalleriesDialogController::UpdateGalleriesOnDeviceEvent(
472     const std::string& device_id) {
473   dialog_->UpdateGalleries();
474 }
475 
GetContextMenuModel(MediaGalleryPrefId id)476 ui::MenuModel* MediaGalleriesDialogController::GetContextMenuModel(
477     MediaGalleryPrefId id) {
478   gallery_menu_model_->set_media_gallery_pref_id(id);
479   return context_menu_model_.get();
480 }
481 
GetProfile()482 Profile* MediaGalleriesDialogController::GetProfile() {
483   return Profile::FromBrowserContext(web_contents_->GetBrowserContext());
484 }
485 
486 // MediaGalleries dialog -------------------------------------------------------
487 
~MediaGalleriesDialog()488 MediaGalleriesDialog::~MediaGalleriesDialog() {}
489