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 // Implements the Chrome Extensions Media Galleries API.
7 #include "chrome/browser/extensions/api/media_galleries/media_galleries_api.h"
13 #include "apps/app_window.h"
14 #include "apps/app_window_registry.h"
15 #include "base/lazy_instance.h"
16 #include "base/platform_file.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/values.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
23 #include "chrome/browser/extensions/blob_reader.h"
24 #include "chrome/browser/extensions/extension_tab_util.h"
25 #include "chrome/browser/media_galleries/media_file_system_registry.h"
26 #include "chrome/browser/media_galleries/media_galleries_dialog_controller.h"
27 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
28 #include "chrome/browser/media_galleries/media_galleries_preferences.h"
29 #include "chrome/browser/media_galleries/media_galleries_scan_result_dialog_controller.h"
30 #include "chrome/browser/media_galleries/media_scan_manager.h"
31 #include "chrome/browser/platform_util.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/ui/chrome_select_file_policy.h"
34 #include "chrome/common/extensions/api/media_galleries.h"
35 #include "chrome/common/extensions/permissions/media_galleries_permission.h"
36 #include "chrome/common/pref_names.h"
37 #include "components/storage_monitor/storage_info.h"
38 #include "components/web_modal/web_contents_modal_dialog_manager.h"
39 #include "content/public/browser/browser_thread.h"
40 #include "content/public/browser/child_process_security_policy.h"
41 #include "content/public/browser/render_process_host.h"
42 #include "content/public/browser/render_view_host.h"
43 #include "content/public/browser/web_contents.h"
44 #include "content/public/browser/web_contents_view.h"
45 #include "extensions/browser/event_router.h"
46 #include "extensions/browser/extension_prefs.h"
47 #include "extensions/browser/extension_system.h"
48 #include "extensions/common/extension.h"
49 #include "extensions/common/permissions/api_permission.h"
50 #include "extensions/common/permissions/permissions_data.h"
51 #include "grit/generated_resources.h"
52 #include "net/base/mime_sniffer.h"
53 #include "ui/base/l10n/l10n_util.h"
55 using content::WebContents;
56 using web_modal::WebContentsModalDialogManager;
58 namespace extensions {
60 namespace MediaGalleries = api::media_galleries;
61 namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems;
65 const char kDisallowedByPolicy[] =
66 "Media Galleries API is disallowed by policy: ";
67 const char kMissingEventListener[] =
68 "Missing event listener registration.";
69 const char kNoScanPermission[] =
70 "No permission to scan.";
72 const char kDeviceIdKey[] = "deviceId";
73 const char kGalleryIdKey[] = "galleryId";
74 const char kIsAvailableKey[] = "isAvailable";
75 const char kIsMediaDeviceKey[] = "isMediaDevice";
76 const char kIsRemovableKey[] = "isRemovable";
77 const char kNameKey[] = "name";
79 MediaFileSystemRegistry* media_file_system_registry() {
80 return g_browser_process->media_file_system_registry();
83 MediaScanManager* media_scan_manager() {
84 return media_file_system_registry()->media_scan_manager();
87 // Checks whether the MediaGalleries API is currently accessible (it may be
88 // disallowed even if an extension has the requisite permission). Then
89 // initializes the MediaGalleriesPreferences
90 bool Setup(Profile* profile, std::string* error, base::Closure callback) {
91 if (!ChromeSelectFilePolicy::FileSelectDialogsAllowed()) {
92 *error = std::string(kDisallowedByPolicy) +
93 prefs::kAllowFileSelectionDialogs;
97 MediaGalleriesPreferences* preferences =
98 media_file_system_registry()->GetPreferences(profile);
99 preferences->EnsureInitialized(callback);
103 WebContents* GetWebContents(content::RenderViewHost* rvh,
105 const std::string& app_id) {
106 WebContents* contents = WebContents::FromRenderViewHost(rvh);
107 WebContentsModalDialogManager* web_contents_modal_dialog_manager =
108 WebContentsModalDialogManager::FromWebContents(contents);
109 if (!web_contents_modal_dialog_manager) {
110 // If there is no WebContentsModalDialogManager, then this contents is
111 // probably the background page for an app. Try to find a app window to
113 apps::AppWindow* window = apps::AppWindowRegistry::Get(profile)
114 ->GetCurrentAppWindowForApp(app_id);
115 contents = window ? window->web_contents() : NULL;
120 base::ListValue* ConstructFileSystemList(
121 content::RenderViewHost* rvh,
122 const Extension* extension,
123 const std::vector<MediaFileSystemInfo>& filesystems) {
127 MediaGalleriesPermission::CheckParam read_param(
128 MediaGalleriesPermission::kReadPermission);
129 bool has_read_permission = PermissionsData::CheckAPIPermissionWithParam(
130 extension, APIPermission::kMediaGalleries, &read_param);
131 MediaGalleriesPermission::CheckParam copy_to_param(
132 MediaGalleriesPermission::kCopyToPermission);
133 bool has_copy_to_permission = PermissionsData::CheckAPIPermissionWithParam(
134 extension, APIPermission::kMediaGalleries, ©_to_param);
135 MediaGalleriesPermission::CheckParam delete_param(
136 MediaGalleriesPermission::kDeletePermission);
137 bool has_delete_permission = PermissionsData::CheckAPIPermissionWithParam(
138 extension, APIPermission::kMediaGalleries, &delete_param);
140 const int child_id = rvh->GetProcess()->GetID();
141 scoped_ptr<base::ListValue> list(new base::ListValue());
142 for (size_t i = 0; i < filesystems.size(); ++i) {
143 scoped_ptr<base::DictionaryValue> file_system_dict_value(
144 new base::DictionaryValue());
146 // Send the file system id so the renderer can create a valid FileSystem
148 file_system_dict_value->SetStringWithoutPathExpansion(
149 "fsid", filesystems[i].fsid);
151 file_system_dict_value->SetStringWithoutPathExpansion(
152 kNameKey, filesystems[i].name);
153 file_system_dict_value->SetStringWithoutPathExpansion(
155 base::Uint64ToString(filesystems[i].pref_id));
156 if (!filesystems[i].transient_device_id.empty()) {
157 file_system_dict_value->SetStringWithoutPathExpansion(
158 kDeviceIdKey, filesystems[i].transient_device_id);
160 file_system_dict_value->SetBooleanWithoutPathExpansion(
161 kIsRemovableKey, filesystems[i].removable);
162 file_system_dict_value->SetBooleanWithoutPathExpansion(
163 kIsMediaDeviceKey, filesystems[i].media_device);
164 file_system_dict_value->SetBooleanWithoutPathExpansion(
165 kIsAvailableKey, true);
167 list->Append(file_system_dict_value.release());
169 if (filesystems[i].path.empty())
172 if (has_read_permission) {
173 content::ChildProcessSecurityPolicy* policy =
174 content::ChildProcessSecurityPolicy::GetInstance();
175 policy->GrantReadFileSystem(child_id, filesystems[i].fsid);
176 if (has_delete_permission) {
177 policy->GrantDeleteFromFileSystem(child_id, filesystems[i].fsid);
178 if (has_copy_to_permission) {
179 policy->GrantCopyIntoFileSystem(child_id, filesystems[i].fsid);
185 return list.release();
188 bool CheckScanPermission(const extensions::Extension* extension,
189 std::string* error) {
192 MediaGalleriesPermission::CheckParam scan_param(
193 MediaGalleriesPermission::kScanPermission);
194 bool has_scan_permission = PermissionsData::CheckAPIPermissionWithParam(
195 extension, APIPermission::kMediaGalleries, &scan_param);
196 if (!has_scan_permission)
197 *error = kNoScanPermission;
198 return has_scan_permission;
201 class SelectDirectoryDialog : public ui::SelectFileDialog::Listener,
202 public base::RefCounted<SelectDirectoryDialog> {
204 // Selected file path, or an empty path if the user canceled.
205 typedef base::Callback<void(const base::FilePath&)> Callback;
207 SelectDirectoryDialog(WebContents* web_contents, const Callback& callback)
208 : web_contents_(web_contents),
209 callback_(callback) {
210 select_file_dialog_ = ui::SelectFileDialog::Create(
211 this, new ChromeSelectFilePolicy(web_contents));
214 void Show(const base::FilePath& default_path) {
215 AddRef(); // Balanced in the two reachable listener outcomes.
216 select_file_dialog_->SelectFile(
217 ui::SelectFileDialog::SELECT_FOLDER,
218 l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY_TITLE),
222 base::FilePath::StringType(),
223 platform_util::GetTopLevel(web_contents_->GetView()->GetNativeView()),
227 // ui::SelectFileDialog::Listener implementation.
228 virtual void FileSelected(const base::FilePath& path,
230 void* params) OVERRIDE {
232 Release(); // Balanced in Show().
235 virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
236 void* params) OVERRIDE {
237 NOTREACHED() << "Should not be able to select multiple files";
240 virtual void FileSelectionCanceled(void* params) OVERRIDE {
241 callback_.Run(base::FilePath());
242 Release(); // Balanced in Show().
246 friend class base::RefCounted<SelectDirectoryDialog>;
247 virtual ~SelectDirectoryDialog() {}
249 scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
250 WebContents* web_contents_;
253 DISALLOW_COPY_AND_ASSIGN(SelectDirectoryDialog);
258 MediaGalleriesEventRouter::MediaGalleriesEventRouter(Profile* profile)
260 weak_ptr_factory_(this) {
261 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
263 media_scan_manager()->AddObserver(profile_, this);
266 MediaGalleriesEventRouter::~MediaGalleriesEventRouter() {
269 void MediaGalleriesEventRouter::Shutdown() {
270 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
271 weak_ptr_factory_.InvalidateWeakPtrs();
272 media_scan_manager()->RemoveObserver(profile_);
273 media_scan_manager()->CancelScansForProfile(profile_);
276 static base::LazyInstance<ProfileKeyedAPIFactory<MediaGalleriesEventRouter> >
277 g_factory = LAZY_INSTANCE_INITIALIZER;
280 ProfileKeyedAPIFactory<MediaGalleriesEventRouter>*
281 MediaGalleriesEventRouter::GetFactoryInstance() {
282 return g_factory.Pointer();
286 MediaGalleriesEventRouter* MediaGalleriesEventRouter::Get(Profile* profile) {
287 DCHECK(media_file_system_registry()->GetPreferences(profile)->
289 return ProfileKeyedAPIFactory<MediaGalleriesEventRouter>::GetForProfile(
293 bool MediaGalleriesEventRouter::ExtensionHasScanProgressListener(
294 const std::string& extension_id) const {
295 EventRouter* router = ExtensionSystem::Get(profile_)->event_router();
296 return router->ExtensionHasEventListener(
298 MediaGalleries::OnScanProgress::kEventName);
301 void MediaGalleriesEventRouter::OnScanStarted(const std::string& extension_id) {
302 MediaGalleries::ScanProgressDetails details;
303 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_START;
304 DispatchEventToExtension(
306 MediaGalleries::OnScanProgress::kEventName,
307 MediaGalleries::OnScanProgress::Create(details).Pass());
310 void MediaGalleriesEventRouter::OnScanCancelled(
311 const std::string& extension_id) {
312 MediaGalleries::ScanProgressDetails details;
313 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_CANCEL;
314 DispatchEventToExtension(
316 MediaGalleries::OnScanProgress::kEventName,
317 MediaGalleries::OnScanProgress::Create(details).Pass());
320 void MediaGalleriesEventRouter::OnScanFinished(
321 const std::string& extension_id, int gallery_count,
322 const MediaGalleryScanResult& file_counts) {
323 MediaGalleries::ScanProgressDetails details;
324 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_FINISH;
325 details.gallery_count.reset(new int(gallery_count));
326 details.audio_count.reset(new int(file_counts.audio_count));
327 details.image_count.reset(new int(file_counts.image_count));
328 details.video_count.reset(new int(file_counts.video_count));
329 DispatchEventToExtension(
331 MediaGalleries::OnScanProgress::kEventName,
332 MediaGalleries::OnScanProgress::Create(details).Pass());
335 void MediaGalleriesEventRouter::OnScanError(
336 const std::string& extension_id) {
337 MediaGalleries::ScanProgressDetails details;
338 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_ERROR;
339 DispatchEventToExtension(
341 MediaGalleries::OnScanProgress::kEventName,
342 MediaGalleries::OnScanProgress::Create(details).Pass());
345 void MediaGalleriesEventRouter::DispatchEventToExtension(
346 const std::string& extension_id,
347 const std::string& event_name,
348 scoped_ptr<base::ListValue> event_args) {
349 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
350 EventRouter* router =
351 extensions::ExtensionSystem::Get(profile_)->event_router();
352 if (!router->ExtensionHasEventListener(extension_id, event_name))
355 scoped_ptr<extensions::Event> event(
356 new extensions::Event(event_name, event_args.Pass()));
357 router->DispatchEventToExtension(extension_id, event.Pass());
360 MediaGalleriesGetMediaFileSystemsFunction::
361 ~MediaGalleriesGetMediaFileSystemsFunction() {}
363 bool MediaGalleriesGetMediaFileSystemsFunction::RunImpl() {
364 media_galleries::UsageCount(media_galleries::GET_MEDIA_FILE_SYSTEMS);
365 scoped_ptr<GetMediaFileSystems::Params> params(
366 GetMediaFileSystems::Params::Create(*args_));
367 EXTENSION_FUNCTION_VALIDATE(params.get());
368 MediaGalleries::GetMediaFileSystemsInteractivity interactive =
369 MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO;
370 if (params->details.get() && params->details->interactive != MediaGalleries::
371 GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE) {
372 interactive = params->details->interactive;
375 return Setup(GetProfile(), &error_, base::Bind(
376 &MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit, this,
380 void MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit(
381 MediaGalleries::GetMediaFileSystemsInteractivity interactive) {
382 switch (interactive) {
383 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_YES: {
384 // The MediaFileSystemRegistry only updates preferences for extensions
385 // that it knows are in use. Since this may be the first call to
386 // chrome.getMediaFileSystems for this extension, call
387 // GetMediaFileSystemsForExtension() here solely so that
388 // MediaFileSystemRegistry will send preference changes.
389 GetMediaFileSystemsForExtension(base::Bind(
390 &MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog, this));
393 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_IF_NEEDED: {
394 GetMediaFileSystemsForExtension(base::Bind(
395 &MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries,
399 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO:
400 GetAndReturnGalleries();
402 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE:
408 void MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog(
409 const std::vector<MediaFileSystemInfo>& /*filesystems*/) {
413 void MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries(
414 const std::vector<MediaFileSystemInfo>& filesystems) {
415 if (filesystems.empty())
418 ReturnGalleries(filesystems);
421 void MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries() {
422 GetMediaFileSystemsForExtension(base::Bind(
423 &MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries, this));
426 void MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries(
427 const std::vector<MediaFileSystemInfo>& filesystems) {
428 scoped_ptr<base::ListValue> list(
429 ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
435 // The custom JS binding will use this list to create DOMFileSystem objects.
436 SetResult(list.release());
440 void MediaGalleriesGetMediaFileSystemsFunction::ShowDialog() {
441 media_galleries::UsageCount(media_galleries::SHOW_DIALOG);
442 const Extension* extension = GetExtension();
443 WebContents* contents =
444 GetWebContents(render_view_host(), GetProfile(), extension->id());
450 // Controller will delete itself.
451 base::Closure cb = base::Bind(
452 &MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries, this);
453 new MediaGalleriesDialogController(contents, *extension, cb);
456 void MediaGalleriesGetMediaFileSystemsFunction::GetMediaFileSystemsForExtension(
457 const MediaFileSystemsCallback& cb) {
458 if (!render_view_host()) {
459 cb.Run(std::vector<MediaFileSystemInfo>());
462 MediaFileSystemRegistry* registry = media_file_system_registry();
463 DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
464 registry->GetMediaFileSystemsForExtension(
465 render_view_host(), GetExtension(), cb);
468 MediaGalleriesGetAllMediaFileSystemMetadataFunction::
469 ~MediaGalleriesGetAllMediaFileSystemMetadataFunction() {}
471 bool MediaGalleriesGetAllMediaFileSystemMetadataFunction::RunImpl() {
472 media_galleries::UsageCount(
473 media_galleries::GET_ALL_MEDIA_FILE_SYSTEM_METADATA);
474 return Setup(GetProfile(), &error_, base::Bind(
475 &MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnPreferencesInit,
479 void MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnPreferencesInit() {
480 MediaFileSystemRegistry* registry = media_file_system_registry();
481 MediaGalleriesPreferences* prefs = registry->GetPreferences(GetProfile());
482 DCHECK(prefs->IsInitialized());
483 MediaGalleryPrefIdSet permitted_gallery_ids =
484 prefs->GalleriesForExtension(*GetExtension());
486 MediaStorageUtil::DeviceIdSet* device_ids = new MediaStorageUtil::DeviceIdSet;
487 const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
488 for (MediaGalleryPrefIdSet::const_iterator it = permitted_gallery_ids.begin();
489 it != permitted_gallery_ids.end(); ++it) {
490 MediaGalleriesPrefInfoMap::const_iterator gallery_it = galleries.find(*it);
491 DCHECK(gallery_it != galleries.end());
492 device_ids->insert(gallery_it->second.device_id);
495 MediaStorageUtil::FilterAttachedDevices(
498 &MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries,
500 permitted_gallery_ids,
501 base::Owned(device_ids)));
504 void MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries(
505 const MediaGalleryPrefIdSet& permitted_gallery_ids,
506 const MediaStorageUtil::DeviceIdSet* available_devices) {
507 MediaFileSystemRegistry* registry = media_file_system_registry();
508 MediaGalleriesPreferences* prefs = registry->GetPreferences(GetProfile());
510 base::ListValue* list = new base::ListValue();
511 const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
512 for (MediaGalleryPrefIdSet::const_iterator it = permitted_gallery_ids.begin();
513 it != permitted_gallery_ids.end(); ++it) {
514 MediaGalleriesPrefInfoMap::const_iterator gallery_it = galleries.find(*it);
515 DCHECK(gallery_it != galleries.end());
516 const MediaGalleryPrefInfo& gallery = gallery_it->second;
517 MediaGalleries::MediaFileSystemMetadata metadata;
518 metadata.name = base::UTF16ToUTF8(gallery.GetGalleryDisplayName());
519 metadata.gallery_id = base::Uint64ToString(gallery.pref_id);
520 metadata.is_removable = StorageInfo::IsRemovableDevice(gallery.device_id);
521 metadata.is_media_device = StorageInfo::IsMediaDevice(gallery.device_id);
522 metadata.is_available = ContainsKey(*available_devices, gallery.device_id);
523 list->Append(metadata.ToValue().release());
530 MediaGalleriesAddUserSelectedFolderFunction::
531 ~MediaGalleriesAddUserSelectedFolderFunction() {}
533 bool MediaGalleriesAddUserSelectedFolderFunction::RunImpl() {
534 media_galleries::UsageCount(media_galleries::ADD_USER_SELECTED_FOLDER);
535 return Setup(GetProfile(), &error_, base::Bind(
536 &MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit, this));
539 void MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit() {
540 Profile* profile = GetProfile();
541 const std::string& app_id = GetExtension()->id();
542 WebContents* contents = GetWebContents(render_view_host(), profile, app_id);
544 // When the request originated from a background page, but there is no app
545 // window open, check to see if it originated from a tab and display the
546 // dialog in that tab.
547 bool found_tab = extensions::ExtensionTabUtil::GetTabById(
548 source_tab_id(), profile, profile->IsOffTheRecord(),
549 NULL, NULL, &contents, NULL);
550 if (!found_tab || !contents) {
556 if (!user_gesture()) {
557 OnDirectorySelected(base::FilePath());
561 base::FilePath last_used_path =
562 extensions::file_system_api::GetLastChooseEntryDirectory(
563 extensions::ExtensionPrefs::Get(profile), app_id);
564 SelectDirectoryDialog::Callback callback = base::Bind(
565 &MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected, this);
566 scoped_refptr<SelectDirectoryDialog> select_directory_dialog =
567 new SelectDirectoryDialog(contents, callback);
568 select_directory_dialog->Show(last_used_path);
571 void MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected(
572 const base::FilePath& selected_directory) {
573 if (selected_directory.empty()) {
574 // User cancelled case.
575 GetMediaFileSystemsForExtension(base::Bind(
576 &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
578 kInvalidMediaGalleryPrefId));
582 extensions::file_system_api::SetLastChooseEntryDirectory(
583 extensions::ExtensionPrefs::Get(GetProfile()),
584 GetExtension()->id(),
587 MediaGalleriesPreferences* preferences =
588 media_file_system_registry()->GetPreferences(GetProfile());
589 MediaGalleryPrefId pref_id =
590 preferences->AddGalleryByPath(selected_directory,
591 MediaGalleryPrefInfo::kUserAdded);
592 preferences->SetGalleryPermissionForExtension(*GetExtension(), pref_id, true);
594 GetMediaFileSystemsForExtension(base::Bind(
595 &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
600 void MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId(
601 MediaGalleryPrefId pref_id,
602 const std::vector<MediaFileSystemInfo>& filesystems) {
603 scoped_ptr<base::ListValue> list(
604 ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
611 if (pref_id != kInvalidMediaGalleryPrefId) {
612 for (size_t i = 0; i < filesystems.size(); ++i) {
613 if (filesystems[i].pref_id == pref_id) {
619 base::DictionaryValue* results = new base::DictionaryValue;
620 results->SetWithoutPathExpansion("mediaFileSystems", list.release());
621 results->SetIntegerWithoutPathExpansion("selectedFileSystemIndex", index);
627 MediaGalleriesAddUserSelectedFolderFunction::GetMediaFileSystemsForExtension(
628 const MediaFileSystemsCallback& cb) {
629 if (!render_view_host()) {
630 cb.Run(std::vector<MediaFileSystemInfo>());
633 MediaFileSystemRegistry* registry = media_file_system_registry();
634 DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
635 registry->GetMediaFileSystemsForExtension(
636 render_view_host(), GetExtension(), cb);
639 MediaGalleriesStartMediaScanFunction::~MediaGalleriesStartMediaScanFunction() {}
641 bool MediaGalleriesStartMediaScanFunction::RunImpl() {
642 media_galleries::UsageCount(media_galleries::START_MEDIA_SCAN);
643 if (!CheckScanPermission(GetExtension(), &error_)) {
644 MediaGalleriesEventRouter::Get(GetProfile())->OnScanError(
645 GetExtension()->id());
648 return Setup(GetProfile(), &error_, base::Bind(
649 &MediaGalleriesStartMediaScanFunction::OnPreferencesInit, this));
652 void MediaGalleriesStartMediaScanFunction::OnPreferencesInit() {
653 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
654 MediaGalleriesEventRouter* api = MediaGalleriesEventRouter::Get(GetProfile());
655 if (!api->ExtensionHasScanProgressListener(GetExtension()->id())) {
656 error_ = kMissingEventListener;
661 media_scan_manager()->StartScan(GetProfile(), GetExtension(), user_gesture());
665 MediaGalleriesCancelMediaScanFunction::
666 ~MediaGalleriesCancelMediaScanFunction() {
669 bool MediaGalleriesCancelMediaScanFunction::RunImpl() {
670 media_galleries::UsageCount(media_galleries::CANCEL_MEDIA_SCAN);
671 if (!CheckScanPermission(GetExtension(), &error_)) {
672 MediaGalleriesEventRouter::Get(GetProfile())->OnScanError(
673 GetExtension()->id());
676 return Setup(GetProfile(), &error_, base::Bind(
677 &MediaGalleriesCancelMediaScanFunction::OnPreferencesInit, this));
680 void MediaGalleriesCancelMediaScanFunction::OnPreferencesInit() {
681 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
682 media_scan_manager()->CancelScan(GetProfile(), GetExtension());
686 MediaGalleriesAddScanResultsFunction::~MediaGalleriesAddScanResultsFunction() {}
688 bool MediaGalleriesAddScanResultsFunction::RunImpl() {
689 media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS);
690 if (!CheckScanPermission(GetExtension(), &error_)) {
691 // We don't fire a scan progress error here, as it would be unintuitive.
697 return Setup(GetProfile(), &error_, base::Bind(
698 &MediaGalleriesAddScanResultsFunction::OnPreferencesInit, this));
701 void MediaGalleriesAddScanResultsFunction::OnPreferencesInit() {
702 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
703 const Extension* extension = GetExtension();
704 MediaGalleriesPreferences* preferences =
705 media_file_system_registry()->GetPreferences(GetProfile());
706 if (MediaGalleriesScanResultDialogController::ScanResultCountForExtension(
707 preferences, extension) == 0) {
708 GetAndReturnGalleries();
712 WebContents* contents =
713 GetWebContents(render_view_host(), GetProfile(), extension->id());
719 // Controller will delete itself.
720 base::Closure cb = base::Bind(
721 &MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries, this);
722 new MediaGalleriesScanResultDialogController(contents, *extension, cb);
725 void MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries() {
726 if (!render_view_host()) {
727 ReturnGalleries(std::vector<MediaFileSystemInfo>());
730 MediaFileSystemRegistry* registry = media_file_system_registry();
731 DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
732 registry->GetMediaFileSystemsForExtension(
733 render_view_host(), GetExtension(),
734 base::Bind(&MediaGalleriesAddScanResultsFunction::ReturnGalleries,
738 void MediaGalleriesAddScanResultsFunction::ReturnGalleries(
739 const std::vector<MediaFileSystemInfo>& filesystems) {
740 scoped_ptr<base::ListValue> list(
741 ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
747 // The custom JS binding will use this list to create DOMFileSystem objects.
748 SetResult(list.release());
752 MediaGalleriesGetMetadataFunction::~MediaGalleriesGetMetadataFunction() {}
754 bool MediaGalleriesGetMetadataFunction::RunImpl() {
755 std::string blob_uuid;
756 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &blob_uuid));
758 const base::Value* options_value = NULL;
759 if (!args_->Get(1, &options_value))
761 scoped_ptr<MediaGalleries::MediaMetadataOptions> options =
762 MediaGalleries::MediaMetadataOptions::FromValue(*options_value);
766 bool mime_type_only = options->metadata_type ==
767 MediaGalleries::GET_METADATA_TYPE_MIMETYPEONLY;
769 return Setup(GetProfile(), &error_, base::Bind(
770 &MediaGalleriesGetMetadataFunction::OnPreferencesInit, this,
771 mime_type_only, blob_uuid));
774 void MediaGalleriesGetMetadataFunction::OnPreferencesInit(
775 bool mime_type_only, const std::string& blob_uuid) {
776 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
778 // BlobReader is self-deleting.
779 BlobReader* reader = new BlobReader(
782 base::Bind(&MediaGalleriesGetMetadataFunction::SniffMimeType, this,
784 reader->SetByteRange(0, net::kMaxBytesToSniff);
788 void MediaGalleriesGetMetadataFunction::SniffMimeType(
789 bool mime_type_only, scoped_ptr<std::string> blob_header,
790 int64 /* total_blob_length */) {
791 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
793 MediaGalleries::MediaMetadata metadata;
795 std::string mime_type;
796 bool mime_type_sniffed = net::SniffMimeTypeFromLocalData(
797 blob_header->c_str(), blob_header->size(), &mime_type);
798 if (mime_type_sniffed)
799 metadata.mime_type = mime_type;
801 // TODO(tommycli): Kick off SafeMediaMetadataParser if |mime_type_only| false.
803 SetResult(metadata.ToValue().release());
807 } // namespace extensions