| OLD | NEW |
| (Empty) |
| 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, ©_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 platform_util::OPEN_FOLDER, | |
| 191 platform_util::OpenOperationCallback()); | |
| 192 } | |
| 193 | |
| 194 void MediaGalleriesScanResultController::DidForgetEntry( | |
| 195 MediaGalleryPrefId pref_id) { | |
| 196 media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS_FORGET_GALLERY); | |
| 197 results_to_remove_.insert(pref_id); | |
| 198 scan_results_.erase(pref_id); | |
| 199 dialog_->UpdateGalleries(); | |
| 200 } | |
| 201 | |
| 202 base::string16 MediaGalleriesScanResultController::GetAcceptButtonText() const { | |
| 203 return l10n_util::GetStringUTF16( | |
| 204 IDS_MEDIA_GALLERIES_SCAN_RESULT_DIALOG_CONFIRM); | |
| 205 } | |
| 206 | |
| 207 void MediaGalleriesScanResultController::DialogFinished(bool accepted) { | |
| 208 // No longer interested in preference updates (and the below code generates | |
| 209 // some). | |
| 210 // |preferences_| may be NULL in tests. | |
| 211 if (preferences_) | |
| 212 preferences_->RemoveGalleryChangeObserver(this); | |
| 213 | |
| 214 if (accepted) { | |
| 215 DCHECK(preferences_); | |
| 216 media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS_ACCEPTED); | |
| 217 int granted = 0; | |
| 218 int total = 0; | |
| 219 for (ScanResults::const_iterator it = scan_results_.begin(); | |
| 220 it != scan_results_.end(); | |
| 221 ++it) { | |
| 222 if (it->second.selected) { | |
| 223 bool changed = preferences_->SetGalleryPermissionForExtension( | |
| 224 *extension_, it->first, true); | |
| 225 DCHECK(changed); | |
| 226 granted++; | |
| 227 } | |
| 228 total++; | |
| 229 } | |
| 230 if (total > 0) { | |
| 231 UMA_HISTOGRAM_PERCENTAGE("MediaGalleries.ScanGalleriesGranted", | |
| 232 (granted * 100 / total)); | |
| 233 } | |
| 234 for (MediaGalleryPrefIdSet::const_iterator it = results_to_remove_.begin(); | |
| 235 it != results_to_remove_.end(); | |
| 236 ++it) { | |
| 237 preferences_->ForgetGalleryById(*it); | |
| 238 } | |
| 239 } else { | |
| 240 media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS_CANCELLED); | |
| 241 } | |
| 242 | |
| 243 on_finish_.Run(); | |
| 244 delete this; | |
| 245 } | |
| 246 | |
| 247 ui::MenuModel* MediaGalleriesScanResultController::GetContextMenu( | |
| 248 MediaGalleryPrefId id) { | |
| 249 context_menu_->set_pref_id(id); | |
| 250 return context_menu_.get(); | |
| 251 } | |
| 252 | |
| 253 content::WebContents* MediaGalleriesScanResultController::WebContents() { | |
| 254 return web_contents_; | |
| 255 } | |
| 256 | |
| 257 // static | |
| 258 void MediaGalleriesScanResultController::UpdateScanResultsFromPreferences( | |
| 259 MediaGalleriesPreferences* preferences, | |
| 260 const extensions::Extension* extension, | |
| 261 MediaGalleryPrefIdSet ignore_list, | |
| 262 ScanResults* scan_results) { | |
| 263 DCHECK(preferences->IsInitialized()); | |
| 264 const MediaGalleriesPrefInfoMap& galleries = preferences->known_galleries(); | |
| 265 MediaGalleryPrefIdSet permitted = | |
| 266 preferences->GalleriesForExtension(*extension); | |
| 267 | |
| 268 // Add or update any scan results that the extension doesn't already have | |
| 269 // access to or isn't in |ignore_list|. | |
| 270 for (MediaGalleriesPrefInfoMap::const_iterator it = galleries.begin(); | |
| 271 it != galleries.end(); | |
| 272 ++it) { | |
| 273 const MediaGalleryPrefInfo& gallery = it->second; | |
| 274 if ((gallery.audio_count || gallery.image_count || gallery.video_count) && | |
| 275 !gallery.IsBlackListedType() && | |
| 276 !ContainsKey(permitted, gallery.pref_id) && | |
| 277 !ContainsKey(ignore_list, gallery.pref_id)) { | |
| 278 ScanResults::iterator existing = scan_results->find(gallery.pref_id); | |
| 279 if (existing == scan_results->end()) { | |
| 280 // Default to selected. | |
| 281 (*scan_results)[gallery.pref_id] = Entry(gallery, true); | |
| 282 } else { | |
| 283 // Update pref_info, in case anything has been updated. | |
| 284 existing->second.pref_info = gallery; | |
| 285 } | |
| 286 } | |
| 287 } | |
| 288 | |
| 289 // Remove anything from |scan_results| that's no longer valid or the user | |
| 290 // already has access to. | |
| 291 std::list<ScanResults::iterator> to_remove; | |
| 292 for (ScanResults::iterator it = scan_results->begin(); | |
| 293 it != scan_results->end(); | |
| 294 ++it) { | |
| 295 MediaGalleriesPrefInfoMap::const_iterator pref_gallery = | |
| 296 galleries.find(it->first); | |
| 297 if (pref_gallery == galleries.end() || | |
| 298 pref_gallery->second.IsBlackListedType() || | |
| 299 ContainsKey(permitted, it->first)) { | |
| 300 to_remove.push_back(it); | |
| 301 } | |
| 302 } | |
| 303 while (!to_remove.empty()) { | |
| 304 scan_results->erase(to_remove.front()); | |
| 305 to_remove.pop_front(); | |
| 306 } | |
| 307 } | |
| 308 | |
| 309 void MediaGalleriesScanResultController::OnPreferencesInitialized() { | |
| 310 // These may be NULL in tests. | |
| 311 if (StorageMonitor::GetInstance()) | |
| 312 StorageMonitor::GetInstance()->AddObserver(this); | |
| 313 if (preferences_) { | |
| 314 preferences_->AddGalleryChangeObserver(this); | |
| 315 UpdateScanResultsFromPreferences(preferences_, extension_, | |
| 316 results_to_remove_, &scan_results_); | |
| 317 } | |
| 318 | |
| 319 dialog_.reset(create_dialog_callback_.Run(this)); | |
| 320 } | |
| 321 | |
| 322 void MediaGalleriesScanResultController::OnPreferenceUpdate( | |
| 323 const std::string& extension_id) { | |
| 324 if (extension_id == extension_->id()) { | |
| 325 UpdateScanResultsFromPreferences(preferences_, extension_, | |
| 326 results_to_remove_, &scan_results_); | |
| 327 dialog_->UpdateGalleries(); | |
| 328 } | |
| 329 } | |
| 330 | |
| 331 void MediaGalleriesScanResultController::OnRemovableDeviceUpdate( | |
| 332 const std::string& device_id) { | |
| 333 for (ScanResults::const_iterator it = scan_results_.begin(); | |
| 334 it != scan_results_.end(); | |
| 335 ++it) { | |
| 336 if (it->second.pref_info.device_id == device_id) { | |
| 337 dialog_->UpdateGalleries(); | |
| 338 return; | |
| 339 } | |
| 340 } | |
| 341 } | |
| 342 | |
| 343 Profile* MediaGalleriesScanResultController::GetProfile() const { | |
| 344 return Profile::FromBrowserContext(web_contents_->GetBrowserContext()); | |
| 345 } | |
| 346 | |
| 347 void MediaGalleriesScanResultController::OnRemovableStorageAttached( | |
| 348 const StorageInfo& info) { | |
| 349 OnRemovableDeviceUpdate(info.device_id()); | |
| 350 } | |
| 351 | |
| 352 void MediaGalleriesScanResultController::OnRemovableStorageDetached( | |
| 353 const StorageInfo& info) { | |
| 354 OnRemovableDeviceUpdate(info.device_id()); | |
| 355 } | |
| 356 | |
| 357 void MediaGalleriesScanResultController::OnPermissionAdded( | |
| 358 MediaGalleriesPreferences* /*pref*/, | |
| 359 const std::string& extension_id, | |
| 360 MediaGalleryPrefId /*pref_id*/) { | |
| 361 OnPreferenceUpdate(extension_id); | |
| 362 } | |
| 363 | |
| 364 void MediaGalleriesScanResultController::OnPermissionRemoved( | |
| 365 MediaGalleriesPreferences* /*pref*/, | |
| 366 const std::string& extension_id, | |
| 367 MediaGalleryPrefId /*pref_id*/) { | |
| 368 OnPreferenceUpdate(extension_id); | |
| 369 } | |
| 370 | |
| 371 void MediaGalleriesScanResultController::OnGalleryAdded( | |
| 372 MediaGalleriesPreferences* /*prefs*/, | |
| 373 MediaGalleryPrefId /*pref_id*/) { | |
| 374 OnPreferenceUpdate(extension_->id()); | |
| 375 } | |
| 376 | |
| 377 void MediaGalleriesScanResultController::OnGalleryRemoved( | |
| 378 MediaGalleriesPreferences* /*prefs*/, | |
| 379 MediaGalleryPrefId /*pref_id*/) { | |
| 380 OnPreferenceUpdate(extension_->id()); | |
| 381 } | |
| 382 | |
| 383 void MediaGalleriesScanResultController::OnGalleryInfoUpdated( | |
| 384 MediaGalleriesPreferences* /*prefs*/, | |
| 385 MediaGalleryPrefId /*pref_id*/) { | |
| 386 OnPreferenceUpdate(extension_->id()); | |
| 387 } | |
| OLD | NEW |