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/numerics/safe_conversions.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/gallery_watch_manager.h"
28 #include "chrome/browser/media_galleries/media_file_system_registry.h"
29 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
30 #include "chrome/browser/media_galleries/media_galleries_permission_controller.h"
31 #include "chrome/browser/media_galleries/media_galleries_preferences.h"
32 #include "chrome/browser/media_galleries/media_galleries_scan_result_controller.h"
33 #include "chrome/browser/media_galleries/media_scan_manager.h"
34 #include "chrome/browser/platform_util.h"
35 #include "chrome/browser/profiles/profile.h"
36 #include "chrome/browser/ui/chrome_select_file_policy.h"
37 #include "chrome/common/extensions/api/media_galleries.h"
38 #include "chrome/common/pref_names.h"
39 #include "components/storage_monitor/storage_info.h"
40 #include "components/web_modal/web_contents_modal_dialog_manager.h"
41 #include "content/public/browser/blob_handle.h"
42 #include "content/public/browser/browser_context.h"
43 #include "content/public/browser/browser_thread.h"
44 #include "content/public/browser/child_process_security_policy.h"
45 #include "content/public/browser/render_process_host.h"
46 #include "content/public/browser/render_view_host.h"
47 #include "content/public/browser/web_contents.h"
48 #include "extensions/browser/blob_holder.h"
49 #include "extensions/browser/extension_prefs.h"
50 #include "extensions/browser/extension_system.h"
51 #include "extensions/common/extension.h"
52 #include "extensions/common/permissions/api_permission.h"
53 #include "extensions/common/permissions/media_galleries_permission.h"
54 #include "extensions/common/permissions/permissions_data.h"
55 #include "grit/generated_resources.h"
56 #include "net/base/mime_sniffer.h"
57 #include "ui/base/l10n/l10n_util.h"
58 #include "webkit/browser/blob/blob_data_handle.h"
60 using content::WebContents;
61 using storage_monitor::MediaStorageUtil;
62 using storage_monitor::StorageInfo;
63 using web_modal::WebContentsModalDialogManager;
65 namespace extensions {
67 namespace MediaGalleries = api::media_galleries;
68 namespace DropPermissionForMediaFileSystem =
69 MediaGalleries::DropPermissionForMediaFileSystem;
70 namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems;
74 const char kDisallowedByPolicy[] =
75 "Media Galleries API is disallowed by policy: ";
76 const char kFailedToSetGalleryPermission[] =
77 "Failed to set gallery permission.";
78 const char kInvalidGalleryId[] = "Invalid gallery id.";
79 const char kMissingEventListener[] = "Missing event listener registration.";
80 const char kNonExistentGalleryId[] = "Non-existent gallery id.";
81 const char kNoScanPermission[] = "No permission to scan.";
83 const char kDeviceIdKey[] = "deviceId";
84 const char kGalleryIdKey[] = "galleryId";
85 const char kIsAvailableKey[] = "isAvailable";
86 const char kIsMediaDeviceKey[] = "isMediaDevice";
87 const char kIsRemovableKey[] = "isRemovable";
88 const char kNameKey[] = "name";
90 const char kMetadataKey[] = "metadata";
91 const char kAttachedImagesBlobInfoKey[] = "attachedImagesBlobInfo";
92 const char kBlobUUIDKey[] = "blobUUID";
93 const char kTypeKey[] = "type";
94 const char kSizeKey[] = "size";
96 MediaFileSystemRegistry* media_file_system_registry() {
97 return g_browser_process->media_file_system_registry();
100 GalleryWatchManager* gallery_watch_manager() {
101 return media_file_system_registry()->gallery_watch_manager();
104 MediaScanManager* media_scan_manager() {
105 return media_file_system_registry()->media_scan_manager();
108 // Checks whether the MediaGalleries API is currently accessible (it may be
109 // disallowed even if an extension has the requisite permission). Then
110 // initializes the MediaGalleriesPreferences
111 bool Setup(Profile* profile, std::string* error, base::Closure callback) {
112 if (!ChromeSelectFilePolicy::FileSelectDialogsAllowed()) {
113 *error = std::string(kDisallowedByPolicy) +
114 prefs::kAllowFileSelectionDialogs;
118 MediaGalleriesPreferences* preferences =
119 media_file_system_registry()->GetPreferences(profile);
120 preferences->EnsureInitialized(callback);
124 WebContents* GetWebContents(content::RenderViewHost* rvh,
126 const std::string& app_id) {
127 WebContents* contents = WebContents::FromRenderViewHost(rvh);
128 WebContentsModalDialogManager* web_contents_modal_dialog_manager =
129 WebContentsModalDialogManager::FromWebContents(contents);
130 if (!web_contents_modal_dialog_manager) {
131 // If there is no WebContentsModalDialogManager, then this contents is
132 // probably the background page for an app. Try to find a app window to
134 apps::AppWindow* window = apps::AppWindowRegistry::Get(profile)
135 ->GetCurrentAppWindowForApp(app_id);
136 contents = window ? window->web_contents() : NULL;
141 base::ListValue* ConstructFileSystemList(
142 content::RenderViewHost* rvh,
143 const Extension* extension,
144 const std::vector<MediaFileSystemInfo>& filesystems) {
148 MediaGalleriesPermission::CheckParam read_param(
149 MediaGalleriesPermission::kReadPermission);
150 const PermissionsData* permissions_data = extension->permissions_data();
151 bool has_read_permission = permissions_data->CheckAPIPermissionWithParam(
152 APIPermission::kMediaGalleries, &read_param);
153 MediaGalleriesPermission::CheckParam copy_to_param(
154 MediaGalleriesPermission::kCopyToPermission);
155 bool has_copy_to_permission = permissions_data->CheckAPIPermissionWithParam(
156 APIPermission::kMediaGalleries, ©_to_param);
157 MediaGalleriesPermission::CheckParam delete_param(
158 MediaGalleriesPermission::kDeletePermission);
159 bool has_delete_permission = permissions_data->CheckAPIPermissionWithParam(
160 APIPermission::kMediaGalleries, &delete_param);
162 const int child_id = rvh->GetProcess()->GetID();
163 scoped_ptr<base::ListValue> list(new base::ListValue());
164 for (size_t i = 0; i < filesystems.size(); ++i) {
165 scoped_ptr<base::DictionaryValue> file_system_dict_value(
166 new base::DictionaryValue());
168 // Send the file system id so the renderer can create a valid FileSystem
170 file_system_dict_value->SetStringWithoutPathExpansion(
171 "fsid", filesystems[i].fsid);
173 file_system_dict_value->SetStringWithoutPathExpansion(
174 kNameKey, filesystems[i].name);
175 file_system_dict_value->SetStringWithoutPathExpansion(
177 base::Uint64ToString(filesystems[i].pref_id));
178 if (!filesystems[i].transient_device_id.empty()) {
179 file_system_dict_value->SetStringWithoutPathExpansion(
180 kDeviceIdKey, filesystems[i].transient_device_id);
182 file_system_dict_value->SetBooleanWithoutPathExpansion(
183 kIsRemovableKey, filesystems[i].removable);
184 file_system_dict_value->SetBooleanWithoutPathExpansion(
185 kIsMediaDeviceKey, filesystems[i].media_device);
186 file_system_dict_value->SetBooleanWithoutPathExpansion(
187 kIsAvailableKey, true);
189 list->Append(file_system_dict_value.release());
191 if (filesystems[i].path.empty())
194 if (has_read_permission) {
195 content::ChildProcessSecurityPolicy* policy =
196 content::ChildProcessSecurityPolicy::GetInstance();
197 policy->GrantReadFile(child_id, filesystems[i].path);
198 if (has_delete_permission) {
199 policy->GrantDeleteFrom(child_id, filesystems[i].path);
200 if (has_copy_to_permission) {
201 policy->GrantCopyInto(child_id, filesystems[i].path);
207 return list.release();
210 bool CheckScanPermission(const extensions::Extension* extension,
211 std::string* error) {
214 MediaGalleriesPermission::CheckParam scan_param(
215 MediaGalleriesPermission::kScanPermission);
216 bool has_scan_permission =
217 extension->permissions_data()->CheckAPIPermissionWithParam(
218 APIPermission::kMediaGalleries, &scan_param);
219 if (!has_scan_permission)
220 *error = kNoScanPermission;
221 return has_scan_permission;
224 class SelectDirectoryDialog : public ui::SelectFileDialog::Listener,
225 public base::RefCounted<SelectDirectoryDialog> {
227 // Selected file path, or an empty path if the user canceled.
228 typedef base::Callback<void(const base::FilePath&)> Callback;
230 SelectDirectoryDialog(WebContents* web_contents, const Callback& callback)
231 : web_contents_(web_contents),
232 callback_(callback) {
233 select_file_dialog_ = ui::SelectFileDialog::Create(
234 this, new ChromeSelectFilePolicy(web_contents));
237 void Show(const base::FilePath& default_path) {
238 AddRef(); // Balanced in the two reachable listener outcomes.
239 select_file_dialog_->SelectFile(
240 ui::SelectFileDialog::SELECT_FOLDER,
241 l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY_TITLE),
245 base::FilePath::StringType(),
246 platform_util::GetTopLevel(web_contents_->GetNativeView()),
250 // ui::SelectFileDialog::Listener implementation.
251 virtual void FileSelected(const base::FilePath& path,
253 void* params) OVERRIDE {
255 Release(); // Balanced in Show().
258 virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
259 void* params) OVERRIDE {
260 NOTREACHED() << "Should not be able to select multiple files";
263 virtual void FileSelectionCanceled(void* params) OVERRIDE {
264 callback_.Run(base::FilePath());
265 Release(); // Balanced in Show().
269 friend class base::RefCounted<SelectDirectoryDialog>;
270 virtual ~SelectDirectoryDialog() {}
272 scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
273 WebContents* web_contents_;
276 DISALLOW_COPY_AND_ASSIGN(SelectDirectoryDialog);
281 MediaGalleriesEventRouter::MediaGalleriesEventRouter(
282 content::BrowserContext* context)
283 : profile_(Profile::FromBrowserContext(context)), weak_ptr_factory_(this) {
284 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
287 EventRouter::Get(profile_)->RegisterObserver(
288 this, MediaGalleries::OnGalleryChanged::kEventName);
290 gallery_watch_manager()->AddObserver(profile_, this);
291 media_scan_manager()->AddObserver(profile_, this);
294 MediaGalleriesEventRouter::~MediaGalleriesEventRouter() {
297 void MediaGalleriesEventRouter::Shutdown() {
298 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
299 weak_ptr_factory_.InvalidateWeakPtrs();
301 EventRouter::Get(profile_)->UnregisterObserver(this);
303 gallery_watch_manager()->RemoveObserver(profile_);
304 media_scan_manager()->RemoveObserver(profile_);
305 media_scan_manager()->CancelScansForProfile(profile_);
308 static base::LazyInstance<
309 BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter> > g_factory =
310 LAZY_INSTANCE_INITIALIZER;
313 BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>*
314 MediaGalleriesEventRouter::GetFactoryInstance() {
315 return g_factory.Pointer();
319 MediaGalleriesEventRouter* MediaGalleriesEventRouter::Get(
320 content::BrowserContext* context) {
321 DCHECK(media_file_system_registry()
322 ->GetPreferences(Profile::FromBrowserContext(context))
324 return BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>::Get(context);
327 bool MediaGalleriesEventRouter::ExtensionHasGalleryChangeListener(
328 const std::string& extension_id) const {
329 return EventRouter::Get(profile_)->ExtensionHasEventListener(
330 extension_id, MediaGalleries::OnGalleryChanged::kEventName);
333 bool MediaGalleriesEventRouter::ExtensionHasScanProgressListener(
334 const std::string& extension_id) const {
335 return EventRouter::Get(profile_)->ExtensionHasEventListener(
336 extension_id, MediaGalleries::OnScanProgress::kEventName);
339 void MediaGalleriesEventRouter::OnScanStarted(const std::string& extension_id) {
340 MediaGalleries::ScanProgressDetails details;
341 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_START;
342 DispatchEventToExtension(
344 MediaGalleries::OnScanProgress::kEventName,
345 MediaGalleries::OnScanProgress::Create(details).Pass());
348 void MediaGalleriesEventRouter::OnScanCancelled(
349 const std::string& extension_id) {
350 MediaGalleries::ScanProgressDetails details;
351 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_CANCEL;
352 DispatchEventToExtension(
354 MediaGalleries::OnScanProgress::kEventName,
355 MediaGalleries::OnScanProgress::Create(details).Pass());
358 void MediaGalleriesEventRouter::OnScanFinished(
359 const std::string& extension_id, int gallery_count,
360 const MediaGalleryScanResult& file_counts) {
361 media_galleries::UsageCount(media_galleries::SCAN_FINISHED);
362 MediaGalleries::ScanProgressDetails details;
363 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_FINISH;
364 details.gallery_count.reset(new int(gallery_count));
365 details.audio_count.reset(new int(file_counts.audio_count));
366 details.image_count.reset(new int(file_counts.image_count));
367 details.video_count.reset(new int(file_counts.video_count));
368 DispatchEventToExtension(
370 MediaGalleries::OnScanProgress::kEventName,
371 MediaGalleries::OnScanProgress::Create(details).Pass());
374 void MediaGalleriesEventRouter::OnScanError(
375 const std::string& extension_id) {
376 MediaGalleries::ScanProgressDetails details;
377 details.type = MediaGalleries::SCAN_PROGRESS_TYPE_ERROR;
378 DispatchEventToExtension(
380 MediaGalleries::OnScanProgress::kEventName,
381 MediaGalleries::OnScanProgress::Create(details).Pass());
384 void MediaGalleriesEventRouter::DispatchEventToExtension(
385 const std::string& extension_id,
386 const std::string& event_name,
387 scoped_ptr<base::ListValue> event_args) {
388 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
389 EventRouter* router = EventRouter::Get(profile_);
390 if (!router->ExtensionHasEventListener(extension_id, event_name))
393 scoped_ptr<extensions::Event> event(
394 new extensions::Event(event_name, event_args.Pass()));
395 router->DispatchEventToExtension(extension_id, event.Pass());
398 void MediaGalleriesEventRouter::OnGalleryChanged(
399 const std::string& extension_id, MediaGalleryPrefId gallery_id) {
400 MediaGalleries::GalleryChangeDetails details;
401 details.type = MediaGalleries::GALLERY_CHANGE_TYPE_CONTENTS_CHANGED;
402 details.gallery_id = gallery_id;
403 DispatchEventToExtension(
405 MediaGalleries::OnGalleryChanged::kEventName,
406 MediaGalleries::OnGalleryChanged::Create(details).Pass());
409 void MediaGalleriesEventRouter::OnGalleryWatchDropped(
410 const std::string& extension_id, MediaGalleryPrefId gallery_id) {
411 MediaGalleries::GalleryChangeDetails details;
412 details.type = MediaGalleries::GALLERY_CHANGE_TYPE_WATCH_DROPPED;
413 details.gallery_id = gallery_id;
414 DispatchEventToExtension(
416 MediaGalleries::OnGalleryChanged::kEventName,
417 MediaGalleries::OnGalleryChanged::Create(details).Pass());
420 void MediaGalleriesEventRouter::OnListenerRemoved(
421 const EventListenerInfo& details) {
422 if (details.event_name == MediaGalleries::OnGalleryChanged::kEventName &&
423 !ExtensionHasGalleryChangeListener(details.extension_id)) {
424 gallery_watch_manager()->RemoveAllWatches(profile_, details.extension_id);
428 MediaGalleriesGetMediaFileSystemsFunction::
429 ~MediaGalleriesGetMediaFileSystemsFunction() {}
431 bool MediaGalleriesGetMediaFileSystemsFunction::RunAsync() {
432 media_galleries::UsageCount(media_galleries::GET_MEDIA_FILE_SYSTEMS);
433 scoped_ptr<GetMediaFileSystems::Params> params(
434 GetMediaFileSystems::Params::Create(*args_));
435 EXTENSION_FUNCTION_VALIDATE(params.get());
436 MediaGalleries::GetMediaFileSystemsInteractivity interactive =
437 MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO;
438 if (params->details.get() && params->details->interactive != MediaGalleries::
439 GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE) {
440 interactive = params->details->interactive;
443 return Setup(GetProfile(), &error_, base::Bind(
444 &MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit, this,
448 void MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit(
449 MediaGalleries::GetMediaFileSystemsInteractivity interactive) {
450 switch (interactive) {
451 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_YES: {
452 // The MediaFileSystemRegistry only updates preferences for extensions
453 // that it knows are in use. Since this may be the first call to
454 // chrome.getMediaFileSystems for this extension, call
455 // GetMediaFileSystemsForExtension() here solely so that
456 // MediaFileSystemRegistry will send preference changes.
457 GetMediaFileSystemsForExtension(base::Bind(
458 &MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog, this));
461 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_IF_NEEDED: {
462 GetMediaFileSystemsForExtension(base::Bind(
463 &MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries,
467 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO:
468 GetAndReturnGalleries();
470 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE:
476 void MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog(
477 const std::vector<MediaFileSystemInfo>& /*filesystems*/) {
481 void MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries(
482 const std::vector<MediaFileSystemInfo>& filesystems) {
483 if (filesystems.empty())
486 ReturnGalleries(filesystems);
489 void MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries() {
490 GetMediaFileSystemsForExtension(base::Bind(
491 &MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries, this));
494 void MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries(
495 const std::vector<MediaFileSystemInfo>& filesystems) {
496 scoped_ptr<base::ListValue> list(
497 ConstructFileSystemList(render_view_host(), extension(), filesystems));
503 // The custom JS binding will use this list to create DOMFileSystem objects.
504 SetResult(list.release());
508 void MediaGalleriesGetMediaFileSystemsFunction::ShowDialog() {
509 media_galleries::UsageCount(media_galleries::SHOW_DIALOG);
510 WebContents* contents =
511 GetWebContents(render_view_host(), GetProfile(), extension()->id());
517 // Controller will delete itself.
518 base::Closure cb = base::Bind(
519 &MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries, this);
520 new MediaGalleriesPermissionController(contents, *extension(), cb);
523 void MediaGalleriesGetMediaFileSystemsFunction::GetMediaFileSystemsForExtension(
524 const MediaFileSystemsCallback& cb) {
525 if (!render_view_host()) {
526 cb.Run(std::vector<MediaFileSystemInfo>());
529 MediaFileSystemRegistry* registry = media_file_system_registry();
530 DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
531 registry->GetMediaFileSystemsForExtension(
532 render_view_host(), extension(), cb);
535 MediaGalleriesGetAllMediaFileSystemMetadataFunction::
536 ~MediaGalleriesGetAllMediaFileSystemMetadataFunction() {}
538 bool MediaGalleriesGetAllMediaFileSystemMetadataFunction::RunAsync() {
539 media_galleries::UsageCount(
540 media_galleries::GET_ALL_MEDIA_FILE_SYSTEM_METADATA);
541 return Setup(GetProfile(), &error_, base::Bind(
542 &MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnPreferencesInit,
546 void MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnPreferencesInit() {
547 MediaFileSystemRegistry* registry = media_file_system_registry();
548 MediaGalleriesPreferences* prefs = registry->GetPreferences(GetProfile());
549 DCHECK(prefs->IsInitialized());
550 MediaGalleryPrefIdSet permitted_gallery_ids =
551 prefs->GalleriesForExtension(*extension());
553 MediaStorageUtil::DeviceIdSet* device_ids = new MediaStorageUtil::DeviceIdSet;
554 const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
555 for (MediaGalleryPrefIdSet::const_iterator it = permitted_gallery_ids.begin();
556 it != permitted_gallery_ids.end(); ++it) {
557 MediaGalleriesPrefInfoMap::const_iterator gallery_it = galleries.find(*it);
558 DCHECK(gallery_it != galleries.end());
559 device_ids->insert(gallery_it->second.device_id);
562 MediaStorageUtil::FilterAttachedDevices(
565 &MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries,
567 permitted_gallery_ids,
568 base::Owned(device_ids)));
571 void MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries(
572 const MediaGalleryPrefIdSet& permitted_gallery_ids,
573 const MediaStorageUtil::DeviceIdSet* available_devices) {
574 MediaFileSystemRegistry* registry = media_file_system_registry();
575 MediaGalleriesPreferences* prefs = registry->GetPreferences(GetProfile());
577 base::ListValue* list = new base::ListValue();
578 const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
579 for (MediaGalleryPrefIdSet::const_iterator it = permitted_gallery_ids.begin();
580 it != permitted_gallery_ids.end(); ++it) {
581 MediaGalleriesPrefInfoMap::const_iterator gallery_it = galleries.find(*it);
582 DCHECK(gallery_it != galleries.end());
583 const MediaGalleryPrefInfo& gallery = gallery_it->second;
584 MediaGalleries::MediaFileSystemMetadata metadata;
585 metadata.name = base::UTF16ToUTF8(gallery.GetGalleryDisplayName());
586 metadata.gallery_id = base::Uint64ToString(gallery.pref_id);
587 metadata.is_removable = StorageInfo::IsRemovableDevice(gallery.device_id);
588 metadata.is_media_device = StorageInfo::IsMediaDevice(gallery.device_id);
589 metadata.is_available = ContainsKey(*available_devices, gallery.device_id);
590 list->Append(metadata.ToValue().release());
597 MediaGalleriesAddUserSelectedFolderFunction::
598 ~MediaGalleriesAddUserSelectedFolderFunction() {}
600 bool MediaGalleriesAddUserSelectedFolderFunction::RunAsync() {
601 media_galleries::UsageCount(media_galleries::ADD_USER_SELECTED_FOLDER);
602 return Setup(GetProfile(), &error_, base::Bind(
603 &MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit, this));
606 void MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit() {
607 Profile* profile = GetProfile();
608 const std::string& app_id = extension()->id();
609 WebContents* contents = GetWebContents(render_view_host(), profile, app_id);
611 // When the request originated from a background page, but there is no app
612 // window open, check to see if it originated from a tab and display the
613 // dialog in that tab.
614 bool found_tab = extensions::ExtensionTabUtil::GetTabById(
615 source_tab_id(), profile, profile->IsOffTheRecord(),
616 NULL, NULL, &contents, NULL);
617 if (!found_tab || !contents) {
623 if (!user_gesture()) {
624 OnDirectorySelected(base::FilePath());
628 base::FilePath last_used_path =
629 extensions::file_system_api::GetLastChooseEntryDirectory(
630 extensions::ExtensionPrefs::Get(profile), app_id);
631 SelectDirectoryDialog::Callback callback = base::Bind(
632 &MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected, this);
633 scoped_refptr<SelectDirectoryDialog> select_directory_dialog =
634 new SelectDirectoryDialog(contents, callback);
635 select_directory_dialog->Show(last_used_path);
638 void MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected(
639 const base::FilePath& selected_directory) {
640 if (selected_directory.empty()) {
641 // User cancelled case.
642 GetMediaFileSystemsForExtension(base::Bind(
643 &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
645 kInvalidMediaGalleryPrefId));
649 extensions::file_system_api::SetLastChooseEntryDirectory(
650 extensions::ExtensionPrefs::Get(GetProfile()),
654 MediaGalleriesPreferences* preferences =
655 media_file_system_registry()->GetPreferences(GetProfile());
656 MediaGalleryPrefId pref_id =
657 preferences->AddGalleryByPath(selected_directory,
658 MediaGalleryPrefInfo::kUserAdded);
659 preferences->SetGalleryPermissionForExtension(*extension(), pref_id, true);
661 GetMediaFileSystemsForExtension(base::Bind(
662 &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
667 void MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId(
668 MediaGalleryPrefId pref_id,
669 const std::vector<MediaFileSystemInfo>& filesystems) {
670 scoped_ptr<base::ListValue> list(
671 ConstructFileSystemList(render_view_host(), extension(), filesystems));
678 if (pref_id != kInvalidMediaGalleryPrefId) {
679 for (size_t i = 0; i < filesystems.size(); ++i) {
680 if (filesystems[i].pref_id == pref_id) {
686 base::DictionaryValue* results = new base::DictionaryValue;
687 results->SetWithoutPathExpansion("mediaFileSystems", list.release());
688 results->SetIntegerWithoutPathExpansion("selectedFileSystemIndex", index);
694 MediaGalleriesAddUserSelectedFolderFunction::GetMediaFileSystemsForExtension(
695 const MediaFileSystemsCallback& cb) {
696 if (!render_view_host()) {
697 cb.Run(std::vector<MediaFileSystemInfo>());
700 MediaFileSystemRegistry* registry = media_file_system_registry();
701 DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
702 registry->GetMediaFileSystemsForExtension(
703 render_view_host(), extension(), cb);
706 MediaGalleriesDropPermissionForMediaFileSystemFunction::
707 ~MediaGalleriesDropPermissionForMediaFileSystemFunction() {}
709 bool MediaGalleriesDropPermissionForMediaFileSystemFunction::RunAsync() {
710 media_galleries::UsageCount(
711 media_galleries::DROP_PERMISSION_FOR_MEDIA_FILE_SYSTEM);
713 scoped_ptr<DropPermissionForMediaFileSystem::Params> params(
714 DropPermissionForMediaFileSystem::Params::Create(*args_));
715 EXTENSION_FUNCTION_VALIDATE(params.get());
716 MediaGalleryPrefId pref_id;
717 if (!base::StringToUint64(params->gallery_id, &pref_id)) {
718 error_ = kInvalidGalleryId;
722 base::Closure callback = base::Bind(
723 &MediaGalleriesDropPermissionForMediaFileSystemFunction::
727 return Setup(GetProfile(), &error_, callback);
730 void MediaGalleriesDropPermissionForMediaFileSystemFunction::OnPreferencesInit(
731 MediaGalleryPrefId pref_id) {
732 MediaGalleriesPreferences* preferences =
733 media_file_system_registry()->GetPreferences(GetProfile());
734 if (!ContainsKey(preferences->known_galleries(), pref_id)) {
735 error_ = kNonExistentGalleryId;
740 bool dropped = preferences->SetGalleryPermissionForExtension(
741 *extension(), pref_id, false);
743 SetResult(new base::StringValue(base::Uint64ToString(pref_id)));
745 error_ = kFailedToSetGalleryPermission;
746 SendResponse(dropped);
749 MediaGalleriesStartMediaScanFunction::~MediaGalleriesStartMediaScanFunction() {}
751 bool MediaGalleriesStartMediaScanFunction::RunAsync() {
752 media_galleries::UsageCount(media_galleries::START_MEDIA_SCAN);
753 if (!CheckScanPermission(extension(), &error_)) {
754 MediaGalleriesEventRouter::Get(GetProfile())
755 ->OnScanError(extension()->id());
758 return Setup(GetProfile(), &error_, base::Bind(
759 &MediaGalleriesStartMediaScanFunction::OnPreferencesInit, this));
762 void MediaGalleriesStartMediaScanFunction::OnPreferencesInit() {
763 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
764 MediaGalleriesEventRouter* api = MediaGalleriesEventRouter::Get(GetProfile());
765 if (!api->ExtensionHasScanProgressListener(extension()->id())) {
766 error_ = kMissingEventListener;
771 media_scan_manager()->StartScan(GetProfile(), extension(), user_gesture());
775 MediaGalleriesCancelMediaScanFunction::
776 ~MediaGalleriesCancelMediaScanFunction() {
779 bool MediaGalleriesCancelMediaScanFunction::RunAsync() {
780 media_galleries::UsageCount(media_galleries::CANCEL_MEDIA_SCAN);
781 if (!CheckScanPermission(extension(), &error_)) {
782 MediaGalleriesEventRouter::Get(GetProfile())
783 ->OnScanError(extension()->id());
786 return Setup(GetProfile(), &error_, base::Bind(
787 &MediaGalleriesCancelMediaScanFunction::OnPreferencesInit, this));
790 void MediaGalleriesCancelMediaScanFunction::OnPreferencesInit() {
791 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
792 media_scan_manager()->CancelScan(GetProfile(), extension());
796 MediaGalleriesAddScanResultsFunction::~MediaGalleriesAddScanResultsFunction() {}
798 bool MediaGalleriesAddScanResultsFunction::RunAsync() {
799 media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS);
800 if (!CheckScanPermission(extension(), &error_)) {
801 // We don't fire a scan progress error here, as it would be unintuitive.
807 return Setup(GetProfile(), &error_, base::Bind(
808 &MediaGalleriesAddScanResultsFunction::OnPreferencesInit, this));
811 MediaGalleriesScanResultController*
812 MediaGalleriesAddScanResultsFunction::MakeDialog(
813 content::WebContents* web_contents,
814 const extensions::Extension& extension,
815 const base::Closure& on_finish) {
816 // Controller will delete itself.
817 return new MediaGalleriesScanResultController(web_contents, extension,
821 void MediaGalleriesAddScanResultsFunction::OnPreferencesInit() {
822 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
823 MediaGalleriesPreferences* preferences =
824 media_file_system_registry()->GetPreferences(GetProfile());
825 if (MediaGalleriesScanResultController::ScanResultCountForExtension(
826 preferences, extension()) == 0) {
827 GetAndReturnGalleries();
831 WebContents* contents =
832 GetWebContents(render_view_host(), GetProfile(), extension()->id());
838 base::Closure cb = base::Bind(
839 &MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries, this);
840 MakeDialog(contents, *extension(), cb);
843 void MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries() {
844 if (!render_view_host()) {
845 ReturnGalleries(std::vector<MediaFileSystemInfo>());
848 MediaFileSystemRegistry* registry = media_file_system_registry();
849 DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
850 registry->GetMediaFileSystemsForExtension(
853 base::Bind(&MediaGalleriesAddScanResultsFunction::ReturnGalleries, this));
856 void MediaGalleriesAddScanResultsFunction::ReturnGalleries(
857 const std::vector<MediaFileSystemInfo>& filesystems) {
858 scoped_ptr<base::ListValue> list(
859 ConstructFileSystemList(render_view_host(), extension(), filesystems));
865 // The custom JS binding will use this list to create DOMFileSystem objects.
866 SetResult(list.release());
870 MediaGalleriesGetMetadataFunction::~MediaGalleriesGetMetadataFunction() {}
872 bool MediaGalleriesGetMetadataFunction::RunAsync() {
873 std::string blob_uuid;
874 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &blob_uuid));
876 const base::Value* options_value = NULL;
877 if (!args_->Get(1, &options_value))
879 scoped_ptr<MediaGalleries::MediaMetadataOptions> options =
880 MediaGalleries::MediaMetadataOptions::FromValue(*options_value);
884 return Setup(GetProfile(), &error_, base::Bind(
885 &MediaGalleriesGetMetadataFunction::OnPreferencesInit, this,
886 options->metadata_type, blob_uuid));
889 void MediaGalleriesGetMetadataFunction::OnPreferencesInit(
890 MediaGalleries::GetMetadataType metadata_type,
891 const std::string& blob_uuid) {
892 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
894 // BlobReader is self-deleting.
895 BlobReader* reader = new BlobReader(
898 base::Bind(&MediaGalleriesGetMetadataFunction::GetMetadata, this,
899 metadata_type, blob_uuid));
900 reader->SetByteRange(0, net::kMaxBytesToSniff);
904 void MediaGalleriesGetMetadataFunction::GetMetadata(
905 MediaGalleries::GetMetadataType metadata_type, const std::string& blob_uuid,
906 scoped_ptr<std::string> blob_header, int64 total_blob_length) {
907 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
909 std::string mime_type;
910 bool mime_type_sniffed = net::SniffMimeTypeFromLocalData(
911 blob_header->c_str(), blob_header->size(), &mime_type);
913 if (!mime_type_sniffed) {
918 if (metadata_type == MediaGalleries::GET_METADATA_TYPE_MIMETYPEONLY) {
919 MediaGalleries::MediaMetadata metadata;
920 metadata.mime_type = mime_type;
922 base::DictionaryValue* result_dictionary = new base::DictionaryValue;
923 result_dictionary->Set(kMetadataKey, metadata.ToValue().release());
924 SetResult(result_dictionary);
929 // We get attached images by default. GET_METADATA_TYPE_NONE is the default
930 // value if the caller doesn't specify the metadata type.
931 bool get_attached_images =
932 metadata_type == MediaGalleries::GET_METADATA_TYPE_ALL ||
933 metadata_type == MediaGalleries::GET_METADATA_TYPE_NONE;
935 scoped_refptr<metadata::SafeMediaMetadataParser> parser(
936 new metadata::SafeMediaMetadataParser(GetProfile(), blob_uuid,
937 total_blob_length, mime_type,
938 get_attached_images));
939 parser->Start(base::Bind(
940 &MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone, this));
943 void MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone(
944 bool parse_success, scoped_ptr<base::DictionaryValue> metadata_dictionary,
945 scoped_ptr<std::vector<metadata::AttachedImage> > attached_images) {
946 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
948 if (!parse_success) {
953 DCHECK(metadata_dictionary.get());
954 DCHECK(attached_images.get());
956 scoped_ptr<base::DictionaryValue> result_dictionary(
957 new base::DictionaryValue);
958 result_dictionary->Set(kMetadataKey, metadata_dictionary.release());
960 if (attached_images->empty()) {
961 SetResult(result_dictionary.release());
966 result_dictionary->Set(kAttachedImagesBlobInfoKey, new base::ListValue);
967 metadata::AttachedImage* first_image = &attached_images->front();
968 content::BrowserContext::CreateMemoryBackedBlob(
970 first_image->data.c_str(),
971 first_image->data.size(),
972 base::Bind(&MediaGalleriesGetMetadataFunction::ConstructNextBlob,
973 this, base::Passed(&result_dictionary),
974 base::Passed(&attached_images),
975 base::Passed(make_scoped_ptr(new std::vector<std::string>))));
978 void MediaGalleriesGetMetadataFunction::ConstructNextBlob(
979 scoped_ptr<base::DictionaryValue> result_dictionary,
980 scoped_ptr<std::vector<metadata::AttachedImage> > attached_images,
981 scoped_ptr<std::vector<std::string> > blob_uuids,
982 scoped_ptr<content::BlobHandle> current_blob) {
983 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
985 DCHECK(result_dictionary.get());
986 DCHECK(attached_images.get());
987 DCHECK(blob_uuids.get());
988 DCHECK(current_blob.get());
990 DCHECK(!attached_images->empty());
991 DCHECK_LT(blob_uuids->size(), attached_images->size());
993 // For the newly constructed Blob, store its image's metadata and Blob UUID.
994 base::ListValue* attached_images_list = NULL;
995 result_dictionary->GetList(kAttachedImagesBlobInfoKey, &attached_images_list);
996 DCHECK(attached_images_list);
997 DCHECK_LT(attached_images_list->GetSize(), attached_images->size());
999 metadata::AttachedImage* current_image =
1000 &(*attached_images)[blob_uuids->size()];
1001 base::DictionaryValue* attached_image = new base::DictionaryValue;
1002 attached_image->Set(kBlobUUIDKey, new base::StringValue(
1003 current_blob->GetUUID()));
1004 attached_image->Set(kTypeKey, new base::StringValue(
1005 current_image->type));
1006 attached_image->Set(kSizeKey, new base::FundamentalValue(
1007 base::checked_cast<int>(current_image->data.size())));
1008 attached_images_list->Append(attached_image);
1010 blob_uuids->push_back(current_blob->GetUUID());
1011 WebContents* contents = WebContents::FromRenderViewHost(render_view_host());
1012 extensions::BlobHolder* holder =
1013 extensions::BlobHolder::FromRenderProcessHost(
1014 contents->GetRenderProcessHost());
1015 holder->HoldBlobReference(current_blob.Pass());
1017 // Construct the next Blob if necessary.
1018 if (blob_uuids->size() < attached_images->size()) {
1019 metadata::AttachedImage* next_image =
1020 &(*attached_images)[blob_uuids->size()];
1021 content::BrowserContext::CreateMemoryBackedBlob(
1023 next_image->data.c_str(),
1024 next_image->data.size(),
1025 base::Bind(&MediaGalleriesGetMetadataFunction::ConstructNextBlob,
1026 this, base::Passed(&result_dictionary),
1027 base::Passed(&attached_images), base::Passed(&blob_uuids)));
1031 // All Blobs have been constructed. The renderer will take ownership.
1032 SetResult(result_dictionary.release());
1033 SetTransferredBlobUUIDs(*blob_uuids);
1037 } // namespace extensions