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 "content/public/browser/web_contents_view.h"
46 #include "extensions/browser/event_router.h"
47 #include "extensions/browser/extension_prefs.h"
48 #include "extensions/browser/extension_system.h"
49 #include "extensions/common/extension.h"
50 #include "extensions/common/permissions/api_permission.h"
51 #include "extensions/common/permissions/media_galleries_permission.h"
52 #include "extensions/common/permissions/permissions_data.h"
53 #include "grit/generated_resources.h"
54 #include "net/base/mime_sniffer.h"
55 #include "ui/base/l10n/l10n_util.h"
57 using content::WebContents;
58 using storage_monitor::MediaStorageUtil;
59 using storage_monitor::StorageInfo;
60 using web_modal::WebContentsModalDialogManager;
62 namespace extensions {
64 namespace MediaGalleries = api::media_galleries;
65 namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems;
69 const char kDisallowedByPolicy[] =
70 "Media Galleries API is disallowed by policy: ";
71 const char kMissingEventListener[] =
72 "Missing event listener registration.";
73 const char kNoScanPermission[] =
74 "No permission to scan.";
76 const char kDeviceIdKey[] = "deviceId";
77 const char kGalleryIdKey[] = "galleryId";
78 const char kIsAvailableKey[] = "isAvailable";
79 const char kIsMediaDeviceKey[] = "isMediaDevice";
80 const char kIsRemovableKey[] = "isRemovable";
81 const char kNameKey[] = "name";
83 MediaFileSystemRegistry* media_file_system_registry() {
84 return g_browser_process->media_file_system_registry();
87 MediaScanManager* media_scan_manager() {
88 return media_file_system_registry()->media_scan_manager();
91 // Checks whether the MediaGalleries API is currently accessible (it may be
92 // disallowed even if an extension has the requisite permission). Then
93 // initializes the MediaGalleriesPreferences
94 bool Setup(Profile* profile, std::string* error, base::Closure callback) {
95 if (!ChromeSelectFilePolicy::FileSelectDialogsAllowed()) {
96 *error = std::string(kDisallowedByPolicy) +
97 prefs::kAllowFileSelectionDialogs;
101 MediaGalleriesPreferences* preferences =
102 media_file_system_registry()->GetPreferences(profile);
103 preferences->EnsureInitialized(callback);
107 WebContents* GetWebContents(content::RenderViewHost* rvh,
109 const std::string& app_id) {
110 WebContents* contents = WebContents::FromRenderViewHost(rvh);
111 WebContentsModalDialogManager* web_contents_modal_dialog_manager =
112 WebContentsModalDialogManager::FromWebContents(contents);
113 if (!web_contents_modal_dialog_manager) {
114 // If there is no WebContentsModalDialogManager, then this contents is
115 // probably the background page for an app. Try to find a app window to
117 apps::AppWindow* window = apps::AppWindowRegistry::Get(profile)
118 ->GetCurrentAppWindowForApp(app_id);
119 contents = window ? window->web_contents() : NULL;
124 base::ListValue* ConstructFileSystemList(
125 content::RenderViewHost* rvh,
126 const Extension* extension,
127 const std::vector<MediaFileSystemInfo>& filesystems) {
131 MediaGalleriesPermission::CheckParam read_param(
132 MediaGalleriesPermission::kReadPermission);
133 bool has_read_permission = PermissionsData::CheckAPIPermissionWithParam(
134 extension, APIPermission::kMediaGalleries, &read_param);
135 MediaGalleriesPermission::CheckParam copy_to_param(
136 MediaGalleriesPermission::kCopyToPermission);
137 bool has_copy_to_permission = PermissionsData::CheckAPIPermissionWithParam(
138 extension, APIPermission::kMediaGalleries, ©_to_param);
139 MediaGalleriesPermission::CheckParam delete_param(
140 MediaGalleriesPermission::kDeletePermission);
141 bool has_delete_permission = PermissionsData::CheckAPIPermissionWithParam(
142 extension, APIPermission::kMediaGalleries, &delete_param);
144 const int child_id = rvh->GetProcess()->GetID();
145 scoped_ptr<base::ListValue> list(new base::ListValue());
146 for (size_t i = 0; i < filesystems.size(); ++i) {
147 scoped_ptr<base::DictionaryValue> file_system_dict_value(
148 new base::DictionaryValue());
150 // Send the file system id so the renderer can create a valid FileSystem
152 file_system_dict_value->SetStringWithoutPathExpansion(
153 "fsid", filesystems[i].fsid);
155 file_system_dict_value->SetStringWithoutPathExpansion(
156 kNameKey, filesystems[i].name);
157 file_system_dict_value->SetStringWithoutPathExpansion(
159 base::Uint64ToString(filesystems[i].pref_id));
160 if (!filesystems[i].transient_device_id.empty()) {
161 file_system_dict_value->SetStringWithoutPathExpansion(
162 kDeviceIdKey, filesystems[i].transient_device_id);
164 file_system_dict_value->SetBooleanWithoutPathExpansion(
165 kIsRemovableKey, filesystems[i].removable);
166 file_system_dict_value->SetBooleanWithoutPathExpansion(
167 kIsMediaDeviceKey, filesystems[i].media_device);
168 file_system_dict_value->SetBooleanWithoutPathExpansion(
169 kIsAvailableKey, true);
171 list->Append(file_system_dict_value.release());
173 if (filesystems[i].path.empty())
176 if (has_read_permission) {
177 content::ChildProcessSecurityPolicy* policy =
178 content::ChildProcessSecurityPolicy::GetInstance();
179 policy->GrantReadFileSystem(child_id, filesystems[i].fsid);
180 if (has_delete_permission) {
181 policy->GrantDeleteFromFileSystem(child_id, filesystems[i].fsid);
182 if (has_copy_to_permission) {
183 policy->GrantCopyIntoFileSystem(child_id, filesystems[i].fsid);
189 return list.release();
192 bool CheckScanPermission(const extensions::Extension* extension,
193 std::string* error) {
196 MediaGalleriesPermission::CheckParam scan_param(
197 MediaGalleriesPermission::kScanPermission);
198 bool has_scan_permission = PermissionsData::CheckAPIPermissionWithParam(
199 extension, APIPermission::kMediaGalleries, &scan_param);
200 if (!has_scan_permission)
201 *error = kNoScanPermission;
202 return has_scan_permission;
205 class SelectDirectoryDialog : public ui::SelectFileDialog::Listener,
206 public base::RefCounted<SelectDirectoryDialog> {
208 // Selected file path, or an empty path if the user canceled.
209 typedef base::Callback<void(const base::FilePath&)> Callback;
211 SelectDirectoryDialog(WebContents* web_contents, const Callback& callback)
212 : web_contents_(web_contents),
213 callback_(callback) {
214 select_file_dialog_ = ui::SelectFileDialog::Create(
215 this, new ChromeSelectFilePolicy(web_contents));
218 void Show(const base::FilePath& default_path) {
219 AddRef(); // Balanced in the two reachable listener outcomes.
220 select_file_dialog_->SelectFile(
221 ui::SelectFileDialog::SELECT_FOLDER,
222 l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY_TITLE),
226 base::FilePath::StringType(),
227 platform_util::GetTopLevel(web_contents_->GetView()->GetNativeView()),
231 // ui::SelectFileDialog::Listener implementation.
232 virtual void FileSelected(const base::FilePath& path,
234 void* params) OVERRIDE {
236 Release(); // Balanced in Show().
239 virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
240 void* params) OVERRIDE {
241 NOTREACHED() << "Should not be able to select multiple files";
244 virtual void FileSelectionCanceled(void* params) OVERRIDE {
245 callback_.Run(base::FilePath());
246 Release(); // Balanced in Show().
250 friend class base::RefCounted<SelectDirectoryDialog>;
251 virtual ~SelectDirectoryDialog() {}
253 scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
254 WebContents* web_contents_;
257 DISALLOW_COPY_AND_ASSIGN(SelectDirectoryDialog);
262 MediaGalleriesEventRouter::MediaGalleriesEventRouter(
263 content::BrowserContext* context)
264 : profile_(Profile::FromBrowserContext(context)), weak_ptr_factory_(this) {
265 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
267 media_scan_manager()->AddObserver(profile_, this);
270 MediaGalleriesEventRouter::~MediaGalleriesEventRouter() {
273 void MediaGalleriesEventRouter::Shutdown() {
274 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
275 weak_ptr_factory_.InvalidateWeakPtrs();
276 media_scan_manager()->RemoveObserver(profile_);
277 media_scan_manager()->CancelScansForProfile(profile_);
280 static base::LazyInstance<
281 BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter> > g_factory =
282 LAZY_INSTANCE_INITIALIZER;
285 BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>*
286 MediaGalleriesEventRouter::GetFactoryInstance() {
287 return g_factory.Pointer();
291 MediaGalleriesEventRouter* MediaGalleriesEventRouter::Get(
292 content::BrowserContext* context) {
293 DCHECK(media_file_system_registry()
294 ->GetPreferences(Profile::FromBrowserContext(context))
296 return BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>::Get(context);
299 bool MediaGalleriesEventRouter::ExtensionHasScanProgressListener(
300 const std::string& extension_id) const {
301 EventRouter* router = ExtensionSystem::Get(profile_)->event_router();
302 return router->ExtensionHasEventListener(
304 MediaGalleries::OnScanProgress::kEventName);
307 void MediaGalleriesEventRouter::OnScanStarted(const std::string& extension_id) {
308 MediaGalleries::ScanProgressDetails details;
309 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_START;
310 DispatchEventToExtension(
312 MediaGalleries::OnScanProgress::kEventName,
313 MediaGalleries::OnScanProgress::Create(details).Pass());
316 void MediaGalleriesEventRouter::OnScanCancelled(
317 const std::string& extension_id) {
318 MediaGalleries::ScanProgressDetails details;
319 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_CANCEL;
320 DispatchEventToExtension(
322 MediaGalleries::OnScanProgress::kEventName,
323 MediaGalleries::OnScanProgress::Create(details).Pass());
326 void MediaGalleriesEventRouter::OnScanFinished(
327 const std::string& extension_id, int gallery_count,
328 const MediaGalleryScanResult& file_counts) {
329 media_galleries::UsageCount(media_galleries::SCAN_FINISHED);
330 MediaGalleries::ScanProgressDetails details;
331 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_FINISH;
332 details.gallery_count.reset(new int(gallery_count));
333 details.audio_count.reset(new int(file_counts.audio_count));
334 details.image_count.reset(new int(file_counts.image_count));
335 details.video_count.reset(new int(file_counts.video_count));
336 DispatchEventToExtension(
338 MediaGalleries::OnScanProgress::kEventName,
339 MediaGalleries::OnScanProgress::Create(details).Pass());
342 void MediaGalleriesEventRouter::OnScanError(
343 const std::string& extension_id) {
344 MediaGalleries::ScanProgressDetails details;
345 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_ERROR;
346 DispatchEventToExtension(
348 MediaGalleries::OnScanProgress::kEventName,
349 MediaGalleries::OnScanProgress::Create(details).Pass());
352 void MediaGalleriesEventRouter::DispatchEventToExtension(
353 const std::string& extension_id,
354 const std::string& event_name,
355 scoped_ptr<base::ListValue> event_args) {
356 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
357 EventRouter* router =
358 extensions::ExtensionSystem::Get(profile_)->event_router();
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::RunImpl() {
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::RunImpl() {
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::RunImpl() {
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 MediaGalleriesStartMediaScanFunction::~MediaGalleriesStartMediaScanFunction() {}
648 bool MediaGalleriesStartMediaScanFunction::RunImpl() {
649 media_galleries::UsageCount(media_galleries::START_MEDIA_SCAN);
650 if (!CheckScanPermission(GetExtension(), &error_)) {
651 MediaGalleriesEventRouter::Get(GetProfile())->OnScanError(
652 GetExtension()->id());
655 return Setup(GetProfile(), &error_, base::Bind(
656 &MediaGalleriesStartMediaScanFunction::OnPreferencesInit, this));
659 void MediaGalleriesStartMediaScanFunction::OnPreferencesInit() {
660 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
661 MediaGalleriesEventRouter* api = MediaGalleriesEventRouter::Get(GetProfile());
662 if (!api->ExtensionHasScanProgressListener(GetExtension()->id())) {
663 error_ = kMissingEventListener;
668 media_scan_manager()->StartScan(GetProfile(), GetExtension(), user_gesture());
672 MediaGalleriesCancelMediaScanFunction::
673 ~MediaGalleriesCancelMediaScanFunction() {
676 bool MediaGalleriesCancelMediaScanFunction::RunImpl() {
677 media_galleries::UsageCount(media_galleries::CANCEL_MEDIA_SCAN);
678 if (!CheckScanPermission(GetExtension(), &error_)) {
679 MediaGalleriesEventRouter::Get(GetProfile())->OnScanError(
680 GetExtension()->id());
683 return Setup(GetProfile(), &error_, base::Bind(
684 &MediaGalleriesCancelMediaScanFunction::OnPreferencesInit, this));
687 void MediaGalleriesCancelMediaScanFunction::OnPreferencesInit() {
688 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
689 media_scan_manager()->CancelScan(GetProfile(), GetExtension());
693 MediaGalleriesAddScanResultsFunction::~MediaGalleriesAddScanResultsFunction() {}
695 bool MediaGalleriesAddScanResultsFunction::RunImpl() {
696 media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS);
697 if (!CheckScanPermission(GetExtension(), &error_)) {
698 // We don't fire a scan progress error here, as it would be unintuitive.
704 return Setup(GetProfile(), &error_, base::Bind(
705 &MediaGalleriesAddScanResultsFunction::OnPreferencesInit, this));
708 MediaGalleriesScanResultDialogController*
709 MediaGalleriesAddScanResultsFunction::MakeDialog(
710 content::WebContents* web_contents,
711 const extensions::Extension& extension,
712 const base::Closure& on_finish) {
713 // Controller will delete itself.
714 return new MediaGalleriesScanResultDialogController(web_contents, extension,
718 void MediaGalleriesAddScanResultsFunction::OnPreferencesInit() {
719 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
720 const Extension* extension = GetExtension();
721 MediaGalleriesPreferences* preferences =
722 media_file_system_registry()->GetPreferences(GetProfile());
723 if (MediaGalleriesScanResultDialogController::ScanResultCountForExtension(
724 preferences, extension) == 0) {
725 GetAndReturnGalleries();
729 WebContents* contents =
730 GetWebContents(render_view_host(), GetProfile(), extension->id());
736 base::Closure cb = base::Bind(
737 &MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries, this);
738 MakeDialog(contents, *extension, cb);
741 void MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries() {
742 if (!render_view_host()) {
743 ReturnGalleries(std::vector<MediaFileSystemInfo>());
746 MediaFileSystemRegistry* registry = media_file_system_registry();
747 DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
748 registry->GetMediaFileSystemsForExtension(
749 render_view_host(), GetExtension(),
750 base::Bind(&MediaGalleriesAddScanResultsFunction::ReturnGalleries,
754 void MediaGalleriesAddScanResultsFunction::ReturnGalleries(
755 const std::vector<MediaFileSystemInfo>& filesystems) {
756 scoped_ptr<base::ListValue> list(
757 ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
763 // The custom JS binding will use this list to create DOMFileSystem objects.
764 SetResult(list.release());
768 MediaGalleriesGetMetadataFunction::~MediaGalleriesGetMetadataFunction() {}
770 bool MediaGalleriesGetMetadataFunction::RunImpl() {
771 std::string blob_uuid;
772 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &blob_uuid));
774 const base::Value* options_value = NULL;
775 if (!args_->Get(1, &options_value))
777 scoped_ptr<MediaGalleries::MediaMetadataOptions> options =
778 MediaGalleries::MediaMetadataOptions::FromValue(*options_value);
782 bool mime_type_only = options->metadata_type ==
783 MediaGalleries::GET_METADATA_TYPE_MIMETYPEONLY;
785 return Setup(GetProfile(), &error_, base::Bind(
786 &MediaGalleriesGetMetadataFunction::OnPreferencesInit, this,
787 mime_type_only, blob_uuid));
790 void MediaGalleriesGetMetadataFunction::OnPreferencesInit(
791 bool mime_type_only, const std::string& blob_uuid) {
792 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
794 // BlobReader is self-deleting.
795 BlobReader* reader = new BlobReader(
798 base::Bind(&MediaGalleriesGetMetadataFunction::SniffMimeType, this,
799 mime_type_only, blob_uuid));
800 reader->SetByteRange(0, net::kMaxBytesToSniff);
804 void MediaGalleriesGetMetadataFunction::SniffMimeType(
805 bool mime_type_only, const std::string& blob_uuid,
806 scoped_ptr<std::string> blob_header, int64 total_blob_length) {
807 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
809 std::string mime_type;
810 bool mime_type_sniffed = net::SniffMimeTypeFromLocalData(
811 blob_header->c_str(), blob_header->size(), &mime_type);
813 if (!mime_type_sniffed) {
818 if (mime_type_only) {
819 MediaGalleries::MediaMetadata metadata;
820 metadata.mime_type = mime_type;
821 SetResult(metadata.ToValue().release());
826 scoped_refptr<metadata::SafeMediaMetadataParser> parser(
827 new metadata::SafeMediaMetadataParser(GetProfile(), blob_uuid,
828 total_blob_length, mime_type));
829 parser->Start(base::Bind(
830 &MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone, this));
833 void MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone(
834 bool parse_success, base::DictionaryValue* metadata_dictionary) {
835 if (!parse_success) {
840 SetResult(metadata_dictionary->DeepCopy());
844 } // namespace extensions