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/callback.h"
16 #include "base/lazy_instance.h"
17 #include "base/platform_file.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/values.h"
22 #include "chrome/browser/browser_process.h"
23 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
24 #include "chrome/browser/extensions/blob_reader.h"
25 #include "chrome/browser/extensions/extension_tab_util.h"
26 #include "chrome/browser/media_galleries/fileapi/safe_media_metadata_parser.h"
27 #include "chrome/browser/media_galleries/media_file_system_registry.h"
28 #include "chrome/browser/media_galleries/media_galleries_dialog_controller.h"
29 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
30 #include "chrome/browser/media_galleries/media_galleries_preferences.h"
31 #include "chrome/browser/media_galleries/media_galleries_scan_result_dialog_controller.h"
32 #include "chrome/browser/media_galleries/media_scan_manager.h"
33 #include "chrome/browser/platform_util.h"
34 #include "chrome/browser/profiles/profile.h"
35 #include "chrome/browser/ui/chrome_select_file_policy.h"
36 #include "chrome/common/extensions/api/media_galleries.h"
37 #include "chrome/common/pref_names.h"
38 #include "components/storage_monitor/storage_info.h"
39 #include "components/web_modal/web_contents_modal_dialog_manager.h"
40 #include "content/public/browser/browser_thread.h"
41 #include "content/public/browser/child_process_security_policy.h"
42 #include "content/public/browser/render_process_host.h"
43 #include "content/public/browser/render_view_host.h"
44 #include "content/public/browser/web_contents.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/media_galleries_permission.h"
51 #include "extensions/common/permissions/permissions_data.h"
52 #include "grit/generated_resources.h"
53 #include "net/base/mime_sniffer.h"
54 #include "ui/base/l10n/l10n_util.h"
56 using content::WebContents;
57 using storage_monitor::MediaStorageUtil;
58 using storage_monitor::StorageInfo;
59 using web_modal::WebContentsModalDialogManager;
61 namespace extensions {
63 namespace MediaGalleries = api::media_galleries;
64 namespace DropPermissionForMediaFileSystem =
65 MediaGalleries::DropPermissionForMediaFileSystem;
66 namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems;
70 const char kDisallowedByPolicy[] =
71 "Media Galleries API is disallowed by policy: ";
72 const char kFailedToSetGalleryPermission[] =
73 "Failed to set gallery permission.";
74 const char kInvalidGalleryId[] = "Invalid gallery id.";
75 const char kMissingEventListener[] = "Missing event listener registration.";
76 const char kNonExistentGalleryId[] = "Non-existent gallery id.";
77 const char kNoScanPermission[] = "No permission to scan.";
79 const char kDeviceIdKey[] = "deviceId";
80 const char kGalleryIdKey[] = "galleryId";
81 const char kIsAvailableKey[] = "isAvailable";
82 const char kIsMediaDeviceKey[] = "isMediaDevice";
83 const char kIsRemovableKey[] = "isRemovable";
84 const char kNameKey[] = "name";
86 MediaFileSystemRegistry* media_file_system_registry() {
87 return g_browser_process->media_file_system_registry();
90 MediaScanManager* media_scan_manager() {
91 return media_file_system_registry()->media_scan_manager();
94 // Checks whether the MediaGalleries API is currently accessible (it may be
95 // disallowed even if an extension has the requisite permission). Then
96 // initializes the MediaGalleriesPreferences
97 bool Setup(Profile* profile, std::string* error, base::Closure callback) {
98 if (!ChromeSelectFilePolicy::FileSelectDialogsAllowed()) {
99 *error = std::string(kDisallowedByPolicy) +
100 prefs::kAllowFileSelectionDialogs;
104 MediaGalleriesPreferences* preferences =
105 media_file_system_registry()->GetPreferences(profile);
106 preferences->EnsureInitialized(callback);
110 WebContents* GetWebContents(content::RenderViewHost* rvh,
112 const std::string& app_id) {
113 WebContents* contents = WebContents::FromRenderViewHost(rvh);
114 WebContentsModalDialogManager* web_contents_modal_dialog_manager =
115 WebContentsModalDialogManager::FromWebContents(contents);
116 if (!web_contents_modal_dialog_manager) {
117 // If there is no WebContentsModalDialogManager, then this contents is
118 // probably the background page for an app. Try to find a app window to
120 apps::AppWindow* window = apps::AppWindowRegistry::Get(profile)
121 ->GetCurrentAppWindowForApp(app_id);
122 contents = window ? window->web_contents() : NULL;
127 base::ListValue* ConstructFileSystemList(
128 content::RenderViewHost* rvh,
129 const Extension* extension,
130 const std::vector<MediaFileSystemInfo>& filesystems) {
134 MediaGalleriesPermission::CheckParam read_param(
135 MediaGalleriesPermission::kReadPermission);
136 bool has_read_permission = PermissionsData::CheckAPIPermissionWithParam(
137 extension, APIPermission::kMediaGalleries, &read_param);
138 MediaGalleriesPermission::CheckParam copy_to_param(
139 MediaGalleriesPermission::kCopyToPermission);
140 bool has_copy_to_permission = PermissionsData::CheckAPIPermissionWithParam(
141 extension, APIPermission::kMediaGalleries, ©_to_param);
142 MediaGalleriesPermission::CheckParam delete_param(
143 MediaGalleriesPermission::kDeletePermission);
144 bool has_delete_permission = PermissionsData::CheckAPIPermissionWithParam(
145 extension, APIPermission::kMediaGalleries, &delete_param);
147 const int child_id = rvh->GetProcess()->GetID();
148 scoped_ptr<base::ListValue> list(new base::ListValue());
149 for (size_t i = 0; i < filesystems.size(); ++i) {
150 scoped_ptr<base::DictionaryValue> file_system_dict_value(
151 new base::DictionaryValue());
153 // Send the file system id so the renderer can create a valid FileSystem
155 file_system_dict_value->SetStringWithoutPathExpansion(
156 "fsid", filesystems[i].fsid);
158 file_system_dict_value->SetStringWithoutPathExpansion(
159 kNameKey, filesystems[i].name);
160 file_system_dict_value->SetStringWithoutPathExpansion(
162 base::Uint64ToString(filesystems[i].pref_id));
163 if (!filesystems[i].transient_device_id.empty()) {
164 file_system_dict_value->SetStringWithoutPathExpansion(
165 kDeviceIdKey, filesystems[i].transient_device_id);
167 file_system_dict_value->SetBooleanWithoutPathExpansion(
168 kIsRemovableKey, filesystems[i].removable);
169 file_system_dict_value->SetBooleanWithoutPathExpansion(
170 kIsMediaDeviceKey, filesystems[i].media_device);
171 file_system_dict_value->SetBooleanWithoutPathExpansion(
172 kIsAvailableKey, true);
174 list->Append(file_system_dict_value.release());
176 if (filesystems[i].path.empty())
179 if (has_read_permission) {
180 content::ChildProcessSecurityPolicy* policy =
181 content::ChildProcessSecurityPolicy::GetInstance();
182 policy->GrantReadFile(child_id, filesystems[i].path);
183 if (has_delete_permission) {
184 policy->GrantDeleteFrom(child_id, filesystems[i].path);
185 if (has_copy_to_permission) {
186 policy->GrantCopyInto(child_id, filesystems[i].path);
192 return list.release();
195 bool CheckScanPermission(const extensions::Extension* extension,
196 std::string* error) {
199 MediaGalleriesPermission::CheckParam scan_param(
200 MediaGalleriesPermission::kScanPermission);
201 bool has_scan_permission = PermissionsData::CheckAPIPermissionWithParam(
202 extension, APIPermission::kMediaGalleries, &scan_param);
203 if (!has_scan_permission)
204 *error = kNoScanPermission;
205 return has_scan_permission;
208 class SelectDirectoryDialog : public ui::SelectFileDialog::Listener,
209 public base::RefCounted<SelectDirectoryDialog> {
211 // Selected file path, or an empty path if the user canceled.
212 typedef base::Callback<void(const base::FilePath&)> Callback;
214 SelectDirectoryDialog(WebContents* web_contents, const Callback& callback)
215 : web_contents_(web_contents),
216 callback_(callback) {
217 select_file_dialog_ = ui::SelectFileDialog::Create(
218 this, new ChromeSelectFilePolicy(web_contents));
221 void Show(const base::FilePath& default_path) {
222 AddRef(); // Balanced in the two reachable listener outcomes.
223 select_file_dialog_->SelectFile(
224 ui::SelectFileDialog::SELECT_FOLDER,
225 l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY_TITLE),
229 base::FilePath::StringType(),
230 platform_util::GetTopLevel(web_contents_->GetNativeView()),
234 // ui::SelectFileDialog::Listener implementation.
235 virtual void FileSelected(const base::FilePath& path,
237 void* params) OVERRIDE {
239 Release(); // Balanced in Show().
242 virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
243 void* params) OVERRIDE {
244 NOTREACHED() << "Should not be able to select multiple files";
247 virtual void FileSelectionCanceled(void* params) OVERRIDE {
248 callback_.Run(base::FilePath());
249 Release(); // Balanced in Show().
253 friend class base::RefCounted<SelectDirectoryDialog>;
254 virtual ~SelectDirectoryDialog() {}
256 scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
257 WebContents* web_contents_;
260 DISALLOW_COPY_AND_ASSIGN(SelectDirectoryDialog);
265 MediaGalleriesEventRouter::MediaGalleriesEventRouter(
266 content::BrowserContext* context)
267 : profile_(Profile::FromBrowserContext(context)), weak_ptr_factory_(this) {
268 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
270 media_scan_manager()->AddObserver(profile_, this);
273 MediaGalleriesEventRouter::~MediaGalleriesEventRouter() {
276 void MediaGalleriesEventRouter::Shutdown() {
277 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
278 weak_ptr_factory_.InvalidateWeakPtrs();
279 media_scan_manager()->RemoveObserver(profile_);
280 media_scan_manager()->CancelScansForProfile(profile_);
283 static base::LazyInstance<
284 BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter> > g_factory =
285 LAZY_INSTANCE_INITIALIZER;
288 BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>*
289 MediaGalleriesEventRouter::GetFactoryInstance() {
290 return g_factory.Pointer();
294 MediaGalleriesEventRouter* MediaGalleriesEventRouter::Get(
295 content::BrowserContext* context) {
296 DCHECK(media_file_system_registry()
297 ->GetPreferences(Profile::FromBrowserContext(context))
299 return BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>::Get(context);
302 bool MediaGalleriesEventRouter::ExtensionHasScanProgressListener(
303 const std::string& extension_id) const {
304 return EventRouter::Get(profile_)->ExtensionHasEventListener(
305 extension_id, MediaGalleries::OnScanProgress::kEventName);
308 void MediaGalleriesEventRouter::OnScanStarted(const std::string& extension_id) {
309 MediaGalleries::ScanProgressDetails details;
310 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_START;
311 DispatchEventToExtension(
313 MediaGalleries::OnScanProgress::kEventName,
314 MediaGalleries::OnScanProgress::Create(details).Pass());
317 void MediaGalleriesEventRouter::OnScanCancelled(
318 const std::string& extension_id) {
319 MediaGalleries::ScanProgressDetails details;
320 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_CANCEL;
321 DispatchEventToExtension(
323 MediaGalleries::OnScanProgress::kEventName,
324 MediaGalleries::OnScanProgress::Create(details).Pass());
327 void MediaGalleriesEventRouter::OnScanFinished(
328 const std::string& extension_id, int gallery_count,
329 const MediaGalleryScanResult& file_counts) {
330 media_galleries::UsageCount(media_galleries::SCAN_FINISHED);
331 MediaGalleries::ScanProgressDetails details;
332 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_FINISH;
333 details.gallery_count.reset(new int(gallery_count));
334 details.audio_count.reset(new int(file_counts.audio_count));
335 details.image_count.reset(new int(file_counts.image_count));
336 details.video_count.reset(new int(file_counts.video_count));
337 DispatchEventToExtension(
339 MediaGalleries::OnScanProgress::kEventName,
340 MediaGalleries::OnScanProgress::Create(details).Pass());
343 void MediaGalleriesEventRouter::OnScanError(
344 const std::string& extension_id) {
345 MediaGalleries::ScanProgressDetails details;
346 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_ERROR;
347 DispatchEventToExtension(
349 MediaGalleries::OnScanProgress::kEventName,
350 MediaGalleries::OnScanProgress::Create(details).Pass());
353 void MediaGalleriesEventRouter::DispatchEventToExtension(
354 const std::string& extension_id,
355 const std::string& event_name,
356 scoped_ptr<base::ListValue> event_args) {
357 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
358 EventRouter* router = EventRouter::Get(profile_);
359 if (!router->ExtensionHasEventListener(extension_id, event_name))
362 scoped_ptr<extensions::Event> event(
363 new extensions::Event(event_name, event_args.Pass()));
364 router->DispatchEventToExtension(extension_id, event.Pass());
367 MediaGalleriesGetMediaFileSystemsFunction::
368 ~MediaGalleriesGetMediaFileSystemsFunction() {}
370 bool MediaGalleriesGetMediaFileSystemsFunction::RunAsync() {
371 media_galleries::UsageCount(media_galleries::GET_MEDIA_FILE_SYSTEMS);
372 scoped_ptr<GetMediaFileSystems::Params> params(
373 GetMediaFileSystems::Params::Create(*args_));
374 EXTENSION_FUNCTION_VALIDATE(params.get());
375 MediaGalleries::GetMediaFileSystemsInteractivity interactive =
376 MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO;
377 if (params->details.get() && params->details->interactive != MediaGalleries::
378 GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE) {
379 interactive = params->details->interactive;
382 return Setup(GetProfile(), &error_, base::Bind(
383 &MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit, this,
387 void MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit(
388 MediaGalleries::GetMediaFileSystemsInteractivity interactive) {
389 switch (interactive) {
390 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_YES: {
391 // The MediaFileSystemRegistry only updates preferences for extensions
392 // that it knows are in use. Since this may be the first call to
393 // chrome.getMediaFileSystems for this extension, call
394 // GetMediaFileSystemsForExtension() here solely so that
395 // MediaFileSystemRegistry will send preference changes.
396 GetMediaFileSystemsForExtension(base::Bind(
397 &MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog, this));
400 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_IF_NEEDED: {
401 GetMediaFileSystemsForExtension(base::Bind(
402 &MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries,
406 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO:
407 GetAndReturnGalleries();
409 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE:
415 void MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog(
416 const std::vector<MediaFileSystemInfo>& /*filesystems*/) {
420 void MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries(
421 const std::vector<MediaFileSystemInfo>& filesystems) {
422 if (filesystems.empty())
425 ReturnGalleries(filesystems);
428 void MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries() {
429 GetMediaFileSystemsForExtension(base::Bind(
430 &MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries, this));
433 void MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries(
434 const std::vector<MediaFileSystemInfo>& filesystems) {
435 scoped_ptr<base::ListValue> list(
436 ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
442 // The custom JS binding will use this list to create DOMFileSystem objects.
443 SetResult(list.release());
447 void MediaGalleriesGetMediaFileSystemsFunction::ShowDialog() {
448 media_galleries::UsageCount(media_galleries::SHOW_DIALOG);
449 const Extension* extension = GetExtension();
450 WebContents* contents =
451 GetWebContents(render_view_host(), GetProfile(), extension->id());
457 // Controller will delete itself.
458 base::Closure cb = base::Bind(
459 &MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries, this);
460 new MediaGalleriesDialogController(contents, *extension, cb);
463 void MediaGalleriesGetMediaFileSystemsFunction::GetMediaFileSystemsForExtension(
464 const MediaFileSystemsCallback& cb) {
465 if (!render_view_host()) {
466 cb.Run(std::vector<MediaFileSystemInfo>());
469 MediaFileSystemRegistry* registry = media_file_system_registry();
470 DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
471 registry->GetMediaFileSystemsForExtension(
472 render_view_host(), GetExtension(), cb);
475 MediaGalleriesGetAllMediaFileSystemMetadataFunction::
476 ~MediaGalleriesGetAllMediaFileSystemMetadataFunction() {}
478 bool MediaGalleriesGetAllMediaFileSystemMetadataFunction::RunAsync() {
479 media_galleries::UsageCount(
480 media_galleries::GET_ALL_MEDIA_FILE_SYSTEM_METADATA);
481 return Setup(GetProfile(), &error_, base::Bind(
482 &MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnPreferencesInit,
486 void MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnPreferencesInit() {
487 MediaFileSystemRegistry* registry = media_file_system_registry();
488 MediaGalleriesPreferences* prefs = registry->GetPreferences(GetProfile());
489 DCHECK(prefs->IsInitialized());
490 MediaGalleryPrefIdSet permitted_gallery_ids =
491 prefs->GalleriesForExtension(*GetExtension());
493 MediaStorageUtil::DeviceIdSet* device_ids = new MediaStorageUtil::DeviceIdSet;
494 const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
495 for (MediaGalleryPrefIdSet::const_iterator it = permitted_gallery_ids.begin();
496 it != permitted_gallery_ids.end(); ++it) {
497 MediaGalleriesPrefInfoMap::const_iterator gallery_it = galleries.find(*it);
498 DCHECK(gallery_it != galleries.end());
499 device_ids->insert(gallery_it->second.device_id);
502 MediaStorageUtil::FilterAttachedDevices(
505 &MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries,
507 permitted_gallery_ids,
508 base::Owned(device_ids)));
511 void MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries(
512 const MediaGalleryPrefIdSet& permitted_gallery_ids,
513 const MediaStorageUtil::DeviceIdSet* available_devices) {
514 MediaFileSystemRegistry* registry = media_file_system_registry();
515 MediaGalleriesPreferences* prefs = registry->GetPreferences(GetProfile());
517 base::ListValue* list = new base::ListValue();
518 const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
519 for (MediaGalleryPrefIdSet::const_iterator it = permitted_gallery_ids.begin();
520 it != permitted_gallery_ids.end(); ++it) {
521 MediaGalleriesPrefInfoMap::const_iterator gallery_it = galleries.find(*it);
522 DCHECK(gallery_it != galleries.end());
523 const MediaGalleryPrefInfo& gallery = gallery_it->second;
524 MediaGalleries::MediaFileSystemMetadata metadata;
525 metadata.name = base::UTF16ToUTF8(gallery.GetGalleryDisplayName());
526 metadata.gallery_id = base::Uint64ToString(gallery.pref_id);
527 metadata.is_removable = StorageInfo::IsRemovableDevice(gallery.device_id);
528 metadata.is_media_device = StorageInfo::IsMediaDevice(gallery.device_id);
529 metadata.is_available = ContainsKey(*available_devices, gallery.device_id);
530 list->Append(metadata.ToValue().release());
537 MediaGalleriesAddUserSelectedFolderFunction::
538 ~MediaGalleriesAddUserSelectedFolderFunction() {}
540 bool MediaGalleriesAddUserSelectedFolderFunction::RunAsync() {
541 media_galleries::UsageCount(media_galleries::ADD_USER_SELECTED_FOLDER);
542 return Setup(GetProfile(), &error_, base::Bind(
543 &MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit, this));
546 void MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit() {
547 Profile* profile = GetProfile();
548 const std::string& app_id = GetExtension()->id();
549 WebContents* contents = GetWebContents(render_view_host(), profile, app_id);
551 // When the request originated from a background page, but there is no app
552 // window open, check to see if it originated from a tab and display the
553 // dialog in that tab.
554 bool found_tab = extensions::ExtensionTabUtil::GetTabById(
555 source_tab_id(), profile, profile->IsOffTheRecord(),
556 NULL, NULL, &contents, NULL);
557 if (!found_tab || !contents) {
563 if (!user_gesture()) {
564 OnDirectorySelected(base::FilePath());
568 base::FilePath last_used_path =
569 extensions::file_system_api::GetLastChooseEntryDirectory(
570 extensions::ExtensionPrefs::Get(profile), app_id);
571 SelectDirectoryDialog::Callback callback = base::Bind(
572 &MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected, this);
573 scoped_refptr<SelectDirectoryDialog> select_directory_dialog =
574 new SelectDirectoryDialog(contents, callback);
575 select_directory_dialog->Show(last_used_path);
578 void MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected(
579 const base::FilePath& selected_directory) {
580 if (selected_directory.empty()) {
581 // User cancelled case.
582 GetMediaFileSystemsForExtension(base::Bind(
583 &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
585 kInvalidMediaGalleryPrefId));
589 extensions::file_system_api::SetLastChooseEntryDirectory(
590 extensions::ExtensionPrefs::Get(GetProfile()),
591 GetExtension()->id(),
594 MediaGalleriesPreferences* preferences =
595 media_file_system_registry()->GetPreferences(GetProfile());
596 MediaGalleryPrefId pref_id =
597 preferences->AddGalleryByPath(selected_directory,
598 MediaGalleryPrefInfo::kUserAdded);
599 preferences->SetGalleryPermissionForExtension(*GetExtension(), pref_id, true);
601 GetMediaFileSystemsForExtension(base::Bind(
602 &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
607 void MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId(
608 MediaGalleryPrefId pref_id,
609 const std::vector<MediaFileSystemInfo>& filesystems) {
610 scoped_ptr<base::ListValue> list(
611 ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
618 if (pref_id != kInvalidMediaGalleryPrefId) {
619 for (size_t i = 0; i < filesystems.size(); ++i) {
620 if (filesystems[i].pref_id == pref_id) {
626 base::DictionaryValue* results = new base::DictionaryValue;
627 results->SetWithoutPathExpansion("mediaFileSystems", list.release());
628 results->SetIntegerWithoutPathExpansion("selectedFileSystemIndex", index);
634 MediaGalleriesAddUserSelectedFolderFunction::GetMediaFileSystemsForExtension(
635 const MediaFileSystemsCallback& cb) {
636 if (!render_view_host()) {
637 cb.Run(std::vector<MediaFileSystemInfo>());
640 MediaFileSystemRegistry* registry = media_file_system_registry();
641 DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
642 registry->GetMediaFileSystemsForExtension(
643 render_view_host(), GetExtension(), cb);
646 MediaGalleriesDropPermissionForMediaFileSystemFunction::
647 ~MediaGalleriesDropPermissionForMediaFileSystemFunction() {}
649 bool MediaGalleriesDropPermissionForMediaFileSystemFunction::RunAsync() {
650 media_galleries::UsageCount(
651 media_galleries::DROP_PERMISSION_FOR_MEDIA_FILE_SYSTEM);
653 scoped_ptr<DropPermissionForMediaFileSystem::Params> params(
654 DropPermissionForMediaFileSystem::Params::Create(*args_));
655 EXTENSION_FUNCTION_VALIDATE(params.get());
656 MediaGalleryPrefId pref_id;
657 if (!base::StringToUint64(params->gallery_id, &pref_id)) {
658 error_ = kInvalidGalleryId;
662 base::Closure callback = base::Bind(
663 &MediaGalleriesDropPermissionForMediaFileSystemFunction::
667 return Setup(GetProfile(), &error_, callback);
670 void MediaGalleriesDropPermissionForMediaFileSystemFunction::OnPreferencesInit(
671 MediaGalleryPrefId pref_id) {
672 MediaGalleriesPreferences* preferences =
673 media_file_system_registry()->GetPreferences(GetProfile());
674 if (!ContainsKey(preferences->known_galleries(), pref_id)) {
675 error_ = kNonExistentGalleryId;
680 bool dropped = preferences->SetGalleryPermissionForExtension(
681 *GetExtension(), pref_id, false);
683 SetResult(new base::StringValue(base::Uint64ToString(pref_id)));
685 error_ = kFailedToSetGalleryPermission;
686 SendResponse(dropped);
689 MediaGalleriesStartMediaScanFunction::~MediaGalleriesStartMediaScanFunction() {}
691 bool MediaGalleriesStartMediaScanFunction::RunAsync() {
692 media_galleries::UsageCount(media_galleries::START_MEDIA_SCAN);
693 if (!CheckScanPermission(GetExtension(), &error_)) {
694 MediaGalleriesEventRouter::Get(GetProfile())->OnScanError(
695 GetExtension()->id());
698 return Setup(GetProfile(), &error_, base::Bind(
699 &MediaGalleriesStartMediaScanFunction::OnPreferencesInit, this));
702 void MediaGalleriesStartMediaScanFunction::OnPreferencesInit() {
703 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
704 MediaGalleriesEventRouter* api = MediaGalleriesEventRouter::Get(GetProfile());
705 if (!api->ExtensionHasScanProgressListener(GetExtension()->id())) {
706 error_ = kMissingEventListener;
711 media_scan_manager()->StartScan(GetProfile(), GetExtension(), user_gesture());
715 MediaGalleriesCancelMediaScanFunction::
716 ~MediaGalleriesCancelMediaScanFunction() {
719 bool MediaGalleriesCancelMediaScanFunction::RunAsync() {
720 media_galleries::UsageCount(media_galleries::CANCEL_MEDIA_SCAN);
721 if (!CheckScanPermission(GetExtension(), &error_)) {
722 MediaGalleriesEventRouter::Get(GetProfile())->OnScanError(
723 GetExtension()->id());
726 return Setup(GetProfile(), &error_, base::Bind(
727 &MediaGalleriesCancelMediaScanFunction::OnPreferencesInit, this));
730 void MediaGalleriesCancelMediaScanFunction::OnPreferencesInit() {
731 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
732 media_scan_manager()->CancelScan(GetProfile(), GetExtension());
736 MediaGalleriesAddScanResultsFunction::~MediaGalleriesAddScanResultsFunction() {}
738 bool MediaGalleriesAddScanResultsFunction::RunAsync() {
739 media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS);
740 if (!CheckScanPermission(GetExtension(), &error_)) {
741 // We don't fire a scan progress error here, as it would be unintuitive.
747 return Setup(GetProfile(), &error_, base::Bind(
748 &MediaGalleriesAddScanResultsFunction::OnPreferencesInit, this));
751 MediaGalleriesScanResultDialogController*
752 MediaGalleriesAddScanResultsFunction::MakeDialog(
753 content::WebContents* web_contents,
754 const extensions::Extension& extension,
755 const base::Closure& on_finish) {
756 // Controller will delete itself.
757 return new MediaGalleriesScanResultDialogController(web_contents, extension,
761 void MediaGalleriesAddScanResultsFunction::OnPreferencesInit() {
762 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
763 const Extension* extension = GetExtension();
764 MediaGalleriesPreferences* preferences =
765 media_file_system_registry()->GetPreferences(GetProfile());
766 if (MediaGalleriesScanResultDialogController::ScanResultCountForExtension(
767 preferences, extension) == 0) {
768 GetAndReturnGalleries();
772 WebContents* contents =
773 GetWebContents(render_view_host(), GetProfile(), extension->id());
779 base::Closure cb = base::Bind(
780 &MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries, this);
781 MakeDialog(contents, *extension, cb);
784 void MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries() {
785 if (!render_view_host()) {
786 ReturnGalleries(std::vector<MediaFileSystemInfo>());
789 MediaFileSystemRegistry* registry = media_file_system_registry();
790 DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
791 registry->GetMediaFileSystemsForExtension(
792 render_view_host(), GetExtension(),
793 base::Bind(&MediaGalleriesAddScanResultsFunction::ReturnGalleries,
797 void MediaGalleriesAddScanResultsFunction::ReturnGalleries(
798 const std::vector<MediaFileSystemInfo>& filesystems) {
799 scoped_ptr<base::ListValue> list(
800 ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
806 // The custom JS binding will use this list to create DOMFileSystem objects.
807 SetResult(list.release());
811 MediaGalleriesGetMetadataFunction::~MediaGalleriesGetMetadataFunction() {}
813 bool MediaGalleriesGetMetadataFunction::RunAsync() {
814 std::string blob_uuid;
815 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &blob_uuid));
817 const base::Value* options_value = NULL;
818 if (!args_->Get(1, &options_value))
820 scoped_ptr<MediaGalleries::MediaMetadataOptions> options =
821 MediaGalleries::MediaMetadataOptions::FromValue(*options_value);
825 bool mime_type_only = options->metadata_type ==
826 MediaGalleries::GET_METADATA_TYPE_MIMETYPEONLY;
828 return Setup(GetProfile(), &error_, base::Bind(
829 &MediaGalleriesGetMetadataFunction::OnPreferencesInit, this,
830 mime_type_only, blob_uuid));
833 void MediaGalleriesGetMetadataFunction::OnPreferencesInit(
834 bool mime_type_only, const std::string& blob_uuid) {
835 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
837 // BlobReader is self-deleting.
838 BlobReader* reader = new BlobReader(
841 base::Bind(&MediaGalleriesGetMetadataFunction::SniffMimeType, this,
842 mime_type_only, blob_uuid));
843 reader->SetByteRange(0, net::kMaxBytesToSniff);
847 void MediaGalleriesGetMetadataFunction::SniffMimeType(
848 bool mime_type_only, const std::string& blob_uuid,
849 scoped_ptr<std::string> blob_header, int64 total_blob_length) {
850 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
852 std::string mime_type;
853 bool mime_type_sniffed = net::SniffMimeTypeFromLocalData(
854 blob_header->c_str(), blob_header->size(), &mime_type);
856 if (!mime_type_sniffed) {
861 if (mime_type_only) {
862 MediaGalleries::MediaMetadata metadata;
863 metadata.mime_type = mime_type;
864 SetResult(metadata.ToValue().release());
869 scoped_refptr<metadata::SafeMediaMetadataParser> parser(
870 new metadata::SafeMediaMetadataParser(GetProfile(), blob_uuid,
871 total_blob_length, mime_type));
872 parser->Start(base::Bind(
873 &MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone, this));
876 void MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone(
877 bool parse_success, base::DictionaryValue* metadata_dictionary) {
878 if (!parse_success) {
883 SetResult(metadata_dictionary->DeepCopy());
887 } // namespace extensions