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.
5 #include "chrome/browser/media_galleries/media_galleries_dialog_controller.h"
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"
30 using extensions::APIPermission
;
31 using extensions::Extension
;
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
)
42 if (!a
.allowed
&& b
.allowed
)
45 return a
.pref_info
.AbsolutePath() < b
.pref_info
.AbsolutePath();
50 class GalleryContextMenuModel
: public ui::SimpleMenuModel::Delegate
{
52 explicit GalleryContextMenuModel(MediaGalleriesDialogController
* controller
)
53 : controller_(controller
), id_(kInvalidMediaGalleryPrefId
) {}
54 virtual ~GalleryContextMenuModel() {}
56 void set_media_gallery_pref_id(MediaGalleryPrefId id
) {
60 virtual bool IsCommandIdChecked(int command_id
) const OVERRIDE
{
63 virtual bool IsCommandIdEnabled(int command_id
) const OVERRIDE
{
66 virtual bool IsCommandIdVisible(int command_id
) const OVERRIDE
{
70 virtual bool GetAcceleratorForCommandId(
71 int command_id
, ui::Accelerator
* accelerator
) OVERRIDE
{
75 virtual void ExecuteCommand(int command_id
, int event_flags
) OVERRIDE
{
76 controller_
->DidForgetGallery(id_
);
80 MediaGalleriesDialogController
* controller_
;
81 MediaGalleryPrefId id_
;
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
) {
92 g_browser_process
->media_file_system_registry()->GetPreferences(
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
97 preferences_
->EnsureInitialized(
98 base::Bind(&MediaGalleriesDialogController::OnPreferencesInitialized
,
99 base::Unretained(this)));
101 gallery_menu_model_
.reset(new GalleryContextMenuModel(this));
102 ui::SimpleMenuModel
* menu_model
=
103 new ui::SimpleMenuModel(gallery_menu_model_
.get());
105 1, l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_DIALOG_DELETE
));
106 context_menu_model_
.reset(menu_model
);
109 void MediaGalleriesDialogController::OnPreferencesInitialized() {
110 InitializePermissions();
112 dialog_
.reset(MediaGalleriesDialog::Create(this));
114 StorageMonitor::GetInstance()->AddObserver(this);
116 preferences_
->AddGalleryChangeObserver(this);
119 MediaGalleriesDialogController::MediaGalleriesDialogController(
120 const extensions::Extension
& extension
)
121 : web_contents_(NULL
),
122 extension_(&extension
),
123 preferences_(NULL
) {}
125 MediaGalleriesDialogController::~MediaGalleriesDialogController() {
126 if (StorageMonitor::GetInstance())
127 StorageMonitor::GetInstance()->RemoveObserver(this);
129 if (select_folder_dialog_
.get())
130 select_folder_dialog_
->ListenerDestroyed();
133 base::string16
MediaGalleriesDialogController::GetHeader() const {
134 return l10n_util::GetStringFUTF16(IDS_MEDIA_GALLERIES_DIALOG_HEADER
,
135 base::UTF8ToUTF16(extension_
->name()));
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
, ©_to_param
);
146 bool has_delete_permission
=
147 extensions::PermissionsData::CheckAPIPermissionWithParam(
148 extension_
, APIPermission::kMediaGalleries
, &delete_param
);
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
;
156 id
= IDS_MEDIA_GALLERIES_DIALOG_SUBTEXT_READ_ONLY
;
158 return l10n_util::GetStringFUTF16(id
, base::UTF8ToUTF16(extension_
->name()));
161 base::string16
MediaGalleriesDialogController::GetUnattachedLocationsHeader()
163 return l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_UNATTACHED_LOCATIONS
);
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
)
175 // Do this? Views did.
176 if (new_galleries_
.size() > 0)
182 // Note: sorts by display criterion: GalleriesVectorComparator.
183 void MediaGalleriesDialogController::FillPermissions(
185 MediaGalleriesDialogController::GalleryPermissionsVector
* permissions
)
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
);
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
);
198 std::sort(permissions
->begin(), permissions
->end(),
199 GalleriesVectorComparator
);
202 MediaGalleriesDialogController::GalleryPermissionsVector
203 MediaGalleriesDialogController::AttachedPermissions() const {
204 GalleryPermissionsVector attached
;
205 FillPermissions(true, &attached
);
209 MediaGalleriesDialogController::GalleryPermissionsVector
210 MediaGalleriesDialogController::UnattachedPermissions() const {
211 GalleryPermissionsVector unattached
;
212 FillPermissions(false, &unattached
);
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
),
230 base::FilePath::StringType(),
231 web_contents_
->GetView()->GetTopLevelNativeWindow(),
235 void MediaGalleriesDialogController::DidToggleGalleryId(
236 MediaGalleryPrefId gallery_id
,
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
)
245 iter
->second
.allowed
= enabled
;
246 if (ContainsKey(toggled_galleries_
, gallery_id
))
247 toggled_galleries_
.erase(gallery_id
);
249 toggled_galleries_
.insert(gallery_id
);
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
258 void MediaGalleriesDialogController::DidToggleNewGallery(
259 const MediaGalleryPrefInfo
& gallery
,
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
;
271 void MediaGalleriesDialogController::DidForgetGallery(
272 MediaGalleryPrefId pref_id
) {
273 DCHECK(preferences_
);
274 preferences_
->ForgetGalleryById(pref_id
);
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);
291 content::WebContents
* MediaGalleriesDialogController::web_contents() {
292 return web_contents_
;
295 void MediaGalleriesDialogController::FileSelected(const base::FilePath
& path
,
298 extensions::file_system_api::SetLastChooseEntryDirectory(
299 extensions::ExtensionPrefs::Get(GetProfile()),
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();
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();
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();
332 void MediaGalleriesDialogController::OnRemovableStorageAttached(
333 const StorageInfo
& info
) {
334 UpdateGalleriesOnDeviceEvent(info
.device_id());
337 void MediaGalleriesDialogController::OnRemovableStorageDetached(
338 const StorageInfo
& info
) {
339 UpdateGalleriesOnDeviceEvent(info
.device_id());
342 void MediaGalleriesDialogController::OnPermissionAdded(
343 MediaGalleriesPreferences
* /* prefs */,
344 const std::string
& extension_id
,
345 MediaGalleryPrefId
/* pref_id */) {
346 if (extension_id
!= extension_
->id())
348 UpdateGalleriesOnPreferencesEvent();
351 void MediaGalleriesDialogController::OnPermissionRemoved(
352 MediaGalleriesPreferences
* /* prefs */,
353 const std::string
& extension_id
,
354 MediaGalleryPrefId
/* pref_id */) {
355 if (extension_id
!= extension_
->id())
357 UpdateGalleriesOnPreferencesEvent();
360 void MediaGalleriesDialogController::OnGalleryAdded(
361 MediaGalleriesPreferences
* /* prefs */,
362 MediaGalleryPrefId
/* pref_id */) {
363 UpdateGalleriesOnPreferencesEvent();
366 void MediaGalleriesDialogController::OnGalleryRemoved(
367 MediaGalleriesPreferences
* /* prefs */,
368 MediaGalleryPrefId
/* pref_id */) {
369 UpdateGalleriesOnPreferencesEvent();
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())
381 const MediaGalleryPrefInfo
& gallery_info
= pref_it
->second
;
382 UpdateGalleriesOnDeviceEvent(gallery_info
.device_id
);
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();
391 const MediaGalleryPrefInfo
& gallery
= iter
->second
;
392 if (gallery
.type
== MediaGalleryPrefInfo::kBlackListed
) {
396 known_galleries_
[iter
->first
] = GalleryPermission(gallery
, false);
399 MediaGalleryPrefIdSet permitted
=
400 preferences_
->GalleriesForExtension(*extension_
);
402 for (MediaGalleryPrefIdSet::iterator iter
= permitted
.begin();
403 iter
!= permitted
.end(); ++iter
) {
404 if (ContainsKey(toggled_galleries_
, *iter
))
406 DCHECK(ContainsKey(known_galleries_
, *iter
));
407 known_galleries_
[*iter
].allowed
= true;
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
);
418 if (iter
->second
.allowed
)
419 media_galleries::UsageCount(media_galleries::DIALOG_PERMISSION_ADDED
);
421 media_galleries::UsageCount(media_galleries::DIALOG_PERMISSION_REMOVED
);
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.
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
, MediaGalleryPrefInfo::kUserAdded
,
436 gallery
.volume_label
, gallery
.vendor_name
, gallery
.model_name
,
437 gallery
.total_size_in_bytes
, gallery
.last_attach_time
, 0, 0, 0);
438 preferences_
->SetGalleryPermissionForExtension(*extension_
, id
, true);
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();
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();
453 GalleryPermission
& gallery
= it
->second
;
454 for (GalleryPermissionsVector::iterator new_it
= new_galleries_
.begin();
455 new_it
!= new_galleries_
.end();
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
461 gallery
.allowed
= new_it
->allowed
;
462 new_galleries_
.erase(new_it
);
468 dialog_
->UpdateGalleries();
471 void MediaGalleriesDialogController::UpdateGalleriesOnDeviceEvent(
472 const std::string
& device_id
) {
473 dialog_
->UpdateGalleries();
476 ui::MenuModel
* MediaGalleriesDialogController::GetContextMenuModel(
477 MediaGalleryPrefId id
) {
478 gallery_menu_model_
->set_media_gallery_pref_id(id
);
479 return context_menu_model_
.get();
482 Profile
* MediaGalleriesDialogController::GetProfile() {
483 return Profile::FromBrowserContext(web_contents_
->GetBrowserContext());
486 // MediaGalleries dialog -------------------------------------------------------
488 MediaGalleriesDialog::~MediaGalleriesDialog() {}