• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 // Implements the Chrome Extensions Media Galleries API.
6 
7 #include "chrome/browser/extensions/api/media_galleries/media_galleries_api.h"
8 
9 #include <set>
10 #include <string>
11 #include <vector>
12 
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/media_file_system_registry.h"
28 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
29 #include "chrome/browser/media_galleries/media_galleries_permission_controller.h"
30 #include "chrome/browser/media_galleries/media_galleries_preferences.h"
31 #include "chrome/browser/media_galleries/media_galleries_scan_result_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/blob_handle.h"
41 #include "content/public/browser/browser_context.h"
42 #include "content/public/browser/browser_thread.h"
43 #include "content/public/browser/child_process_security_policy.h"
44 #include "content/public/browser/render_process_host.h"
45 #include "content/public/browser/render_view_host.h"
46 #include "content/public/browser/web_contents.h"
47 #include "extensions/browser/blob_holder.h"
48 #include "extensions/browser/event_router.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"
59 
60 using content::WebContents;
61 using storage_monitor::MediaStorageUtil;
62 using storage_monitor::StorageInfo;
63 using web_modal::WebContentsModalDialogManager;
64 
65 namespace extensions {
66 
67 namespace MediaGalleries = api::media_galleries;
68 namespace DropPermissionForMediaFileSystem =
69     MediaGalleries::DropPermissionForMediaFileSystem;
70 namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems;
71 
72 namespace {
73 
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.";
82 
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";
89 
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";
95 
media_file_system_registry()96 MediaFileSystemRegistry* media_file_system_registry() {
97   return g_browser_process->media_file_system_registry();
98 }
99 
media_scan_manager()100 MediaScanManager* media_scan_manager() {
101   return media_file_system_registry()->media_scan_manager();
102 }
103 
104 // Checks whether the MediaGalleries API is currently accessible (it may be
105 // disallowed even if an extension has the requisite permission). Then
106 // initializes the MediaGalleriesPreferences
Setup(Profile * profile,std::string * error,base::Closure callback)107 bool Setup(Profile* profile, std::string* error, base::Closure callback) {
108   if (!ChromeSelectFilePolicy::FileSelectDialogsAllowed()) {
109     *error = std::string(kDisallowedByPolicy) +
110         prefs::kAllowFileSelectionDialogs;
111     return false;
112   }
113 
114   MediaGalleriesPreferences* preferences =
115       media_file_system_registry()->GetPreferences(profile);
116   preferences->EnsureInitialized(callback);
117   return true;
118 }
119 
GetWebContents(content::RenderViewHost * rvh,Profile * profile,const std::string & app_id)120 WebContents* GetWebContents(content::RenderViewHost* rvh,
121                             Profile* profile,
122                             const std::string& app_id) {
123   WebContents* contents = WebContents::FromRenderViewHost(rvh);
124   WebContentsModalDialogManager* web_contents_modal_dialog_manager =
125       WebContentsModalDialogManager::FromWebContents(contents);
126   if (!web_contents_modal_dialog_manager) {
127     // If there is no WebContentsModalDialogManager, then this contents is
128     // probably the background page for an app. Try to find a app window to
129     // host the dialog.
130     apps::AppWindow* window = apps::AppWindowRegistry::Get(profile)
131                                   ->GetCurrentAppWindowForApp(app_id);
132     contents = window ? window->web_contents() : NULL;
133   }
134   return contents;
135 }
136 
ConstructFileSystemList(content::RenderViewHost * rvh,const Extension * extension,const std::vector<MediaFileSystemInfo> & filesystems)137 base::ListValue* ConstructFileSystemList(
138     content::RenderViewHost* rvh,
139     const Extension* extension,
140     const std::vector<MediaFileSystemInfo>& filesystems) {
141   if (!rvh)
142     return NULL;
143 
144   MediaGalleriesPermission::CheckParam read_param(
145       MediaGalleriesPermission::kReadPermission);
146   const PermissionsData* permissions_data = extension->permissions_data();
147   bool has_read_permission = permissions_data->CheckAPIPermissionWithParam(
148       APIPermission::kMediaGalleries, &read_param);
149   MediaGalleriesPermission::CheckParam copy_to_param(
150       MediaGalleriesPermission::kCopyToPermission);
151   bool has_copy_to_permission = permissions_data->CheckAPIPermissionWithParam(
152       APIPermission::kMediaGalleries, &copy_to_param);
153   MediaGalleriesPermission::CheckParam delete_param(
154       MediaGalleriesPermission::kDeletePermission);
155   bool has_delete_permission = permissions_data->CheckAPIPermissionWithParam(
156       APIPermission::kMediaGalleries, &delete_param);
157 
158   const int child_id = rvh->GetProcess()->GetID();
159   scoped_ptr<base::ListValue> list(new base::ListValue());
160   for (size_t i = 0; i < filesystems.size(); ++i) {
161     scoped_ptr<base::DictionaryValue> file_system_dict_value(
162         new base::DictionaryValue());
163 
164     // Send the file system id so the renderer can create a valid FileSystem
165     // object.
166     file_system_dict_value->SetStringWithoutPathExpansion(
167         "fsid", filesystems[i].fsid);
168 
169     file_system_dict_value->SetStringWithoutPathExpansion(
170         kNameKey, filesystems[i].name);
171     file_system_dict_value->SetStringWithoutPathExpansion(
172         kGalleryIdKey,
173         base::Uint64ToString(filesystems[i].pref_id));
174     if (!filesystems[i].transient_device_id.empty()) {
175       file_system_dict_value->SetStringWithoutPathExpansion(
176           kDeviceIdKey, filesystems[i].transient_device_id);
177     }
178     file_system_dict_value->SetBooleanWithoutPathExpansion(
179         kIsRemovableKey, filesystems[i].removable);
180     file_system_dict_value->SetBooleanWithoutPathExpansion(
181         kIsMediaDeviceKey, filesystems[i].media_device);
182     file_system_dict_value->SetBooleanWithoutPathExpansion(
183         kIsAvailableKey, true);
184 
185     list->Append(file_system_dict_value.release());
186 
187     if (filesystems[i].path.empty())
188       continue;
189 
190     if (has_read_permission) {
191       content::ChildProcessSecurityPolicy* policy =
192           content::ChildProcessSecurityPolicy::GetInstance();
193       policy->GrantReadFile(child_id, filesystems[i].path);
194       if (has_delete_permission) {
195         policy->GrantDeleteFrom(child_id, filesystems[i].path);
196         if (has_copy_to_permission) {
197           policy->GrantCopyInto(child_id, filesystems[i].path);
198         }
199       }
200     }
201   }
202 
203   return list.release();
204 }
205 
CheckScanPermission(const extensions::Extension * extension,std::string * error)206 bool CheckScanPermission(const extensions::Extension* extension,
207                          std::string* error) {
208   DCHECK(extension);
209   DCHECK(error);
210   MediaGalleriesPermission::CheckParam scan_param(
211       MediaGalleriesPermission::kScanPermission);
212   bool has_scan_permission =
213       extension->permissions_data()->CheckAPIPermissionWithParam(
214           APIPermission::kMediaGalleries, &scan_param);
215   if (!has_scan_permission)
216     *error = kNoScanPermission;
217   return has_scan_permission;
218 }
219 
220 class SelectDirectoryDialog : public ui::SelectFileDialog::Listener,
221                               public base::RefCounted<SelectDirectoryDialog> {
222  public:
223   // Selected file path, or an empty path if the user canceled.
224   typedef base::Callback<void(const base::FilePath&)> Callback;
225 
SelectDirectoryDialog(WebContents * web_contents,const Callback & callback)226   SelectDirectoryDialog(WebContents* web_contents, const Callback& callback)
227       : web_contents_(web_contents),
228         callback_(callback) {
229     select_file_dialog_ = ui::SelectFileDialog::Create(
230         this, new ChromeSelectFilePolicy(web_contents));
231   }
232 
Show(const base::FilePath & default_path)233   void Show(const base::FilePath& default_path) {
234     AddRef();  // Balanced in the two reachable listener outcomes.
235     select_file_dialog_->SelectFile(
236       ui::SelectFileDialog::SELECT_FOLDER,
237       l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY_TITLE),
238       default_path,
239       NULL,
240       0,
241       base::FilePath::StringType(),
242       platform_util::GetTopLevel(web_contents_->GetNativeView()),
243       NULL);
244   }
245 
246   // ui::SelectFileDialog::Listener implementation.
FileSelected(const base::FilePath & path,int index,void * params)247   virtual void FileSelected(const base::FilePath& path,
248                             int index,
249                             void* params) OVERRIDE {
250     callback_.Run(path);
251     Release();  // Balanced in Show().
252   }
253 
MultiFilesSelected(const std::vector<base::FilePath> & files,void * params)254   virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
255                                   void* params) OVERRIDE {
256     NOTREACHED() << "Should not be able to select multiple files";
257   }
258 
FileSelectionCanceled(void * params)259   virtual void FileSelectionCanceled(void* params) OVERRIDE {
260     callback_.Run(base::FilePath());
261     Release();  // Balanced in Show().
262   }
263 
264  private:
265   friend class base::RefCounted<SelectDirectoryDialog>;
~SelectDirectoryDialog()266   virtual ~SelectDirectoryDialog() {}
267 
268   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
269   WebContents* web_contents_;
270   Callback callback_;
271 
272   DISALLOW_COPY_AND_ASSIGN(SelectDirectoryDialog);
273 };
274 
275 }  // namespace
276 
MediaGalleriesEventRouter(content::BrowserContext * context)277 MediaGalleriesEventRouter::MediaGalleriesEventRouter(
278     content::BrowserContext* context)
279     : profile_(Profile::FromBrowserContext(context)), weak_ptr_factory_(this) {
280   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
281   DCHECK(profile_);
282   media_scan_manager()->AddObserver(profile_, this);
283 }
284 
~MediaGalleriesEventRouter()285 MediaGalleriesEventRouter::~MediaGalleriesEventRouter() {
286 }
287 
Shutdown()288 void MediaGalleriesEventRouter::Shutdown() {
289   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
290   weak_ptr_factory_.InvalidateWeakPtrs();
291   media_scan_manager()->RemoveObserver(profile_);
292   media_scan_manager()->CancelScansForProfile(profile_);
293 }
294 
295 static base::LazyInstance<
296     BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter> > g_factory =
297     LAZY_INSTANCE_INITIALIZER;
298 
299 // static
300 BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>*
GetFactoryInstance()301 MediaGalleriesEventRouter::GetFactoryInstance() {
302   return g_factory.Pointer();
303 }
304 
305 // static
Get(content::BrowserContext * context)306 MediaGalleriesEventRouter* MediaGalleriesEventRouter::Get(
307     content::BrowserContext* context) {
308   DCHECK(media_file_system_registry()
309              ->GetPreferences(Profile::FromBrowserContext(context))
310              ->IsInitialized());
311   return BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>::Get(context);
312 }
313 
ExtensionHasScanProgressListener(const std::string & extension_id) const314 bool MediaGalleriesEventRouter::ExtensionHasScanProgressListener(
315     const std::string& extension_id) const {
316   return EventRouter::Get(profile_)->ExtensionHasEventListener(
317       extension_id, MediaGalleries::OnScanProgress::kEventName);
318 }
319 
OnScanStarted(const std::string & extension_id)320 void MediaGalleriesEventRouter::OnScanStarted(const std::string& extension_id) {
321   MediaGalleries::ScanProgressDetails details;
322   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_START;
323   DispatchEventToExtension(
324       extension_id,
325       MediaGalleries::OnScanProgress::kEventName,
326       MediaGalleries::OnScanProgress::Create(details).Pass());
327 }
328 
OnScanCancelled(const std::string & extension_id)329 void MediaGalleriesEventRouter::OnScanCancelled(
330     const std::string& extension_id) {
331   MediaGalleries::ScanProgressDetails details;
332   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_CANCEL;
333   DispatchEventToExtension(
334       extension_id,
335       MediaGalleries::OnScanProgress::kEventName,
336       MediaGalleries::OnScanProgress::Create(details).Pass());
337 }
338 
OnScanFinished(const std::string & extension_id,int gallery_count,const MediaGalleryScanResult & file_counts)339 void MediaGalleriesEventRouter::OnScanFinished(
340     const std::string& extension_id, int gallery_count,
341     const MediaGalleryScanResult& file_counts) {
342   media_galleries::UsageCount(media_galleries::SCAN_FINISHED);
343   MediaGalleries::ScanProgressDetails details;
344   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_FINISH;
345   details.gallery_count.reset(new int(gallery_count));
346   details.audio_count.reset(new int(file_counts.audio_count));
347   details.image_count.reset(new int(file_counts.image_count));
348   details.video_count.reset(new int(file_counts.video_count));
349   DispatchEventToExtension(
350       extension_id,
351       MediaGalleries::OnScanProgress::kEventName,
352       MediaGalleries::OnScanProgress::Create(details).Pass());
353 }
354 
OnScanError(const std::string & extension_id)355 void MediaGalleriesEventRouter::OnScanError(
356     const std::string& extension_id) {
357   MediaGalleries::ScanProgressDetails details;
358   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_ERROR;
359   DispatchEventToExtension(
360       extension_id,
361       MediaGalleries::OnScanProgress::kEventName,
362       MediaGalleries::OnScanProgress::Create(details).Pass());
363 }
364 
DispatchEventToExtension(const std::string & extension_id,const std::string & event_name,scoped_ptr<base::ListValue> event_args)365 void MediaGalleriesEventRouter::DispatchEventToExtension(
366     const std::string& extension_id,
367     const std::string& event_name,
368     scoped_ptr<base::ListValue> event_args) {
369   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
370   EventRouter* router = EventRouter::Get(profile_);
371   if (!router->ExtensionHasEventListener(extension_id, event_name))
372     return;
373 
374   scoped_ptr<extensions::Event> event(
375       new extensions::Event(event_name, event_args.Pass()));
376   router->DispatchEventToExtension(extension_id, event.Pass());
377 }
378 
379 MediaGalleriesGetMediaFileSystemsFunction::
~MediaGalleriesGetMediaFileSystemsFunction()380     ~MediaGalleriesGetMediaFileSystemsFunction() {}
381 
RunAsync()382 bool MediaGalleriesGetMediaFileSystemsFunction::RunAsync() {
383   media_galleries::UsageCount(media_galleries::GET_MEDIA_FILE_SYSTEMS);
384   scoped_ptr<GetMediaFileSystems::Params> params(
385       GetMediaFileSystems::Params::Create(*args_));
386   EXTENSION_FUNCTION_VALIDATE(params.get());
387   MediaGalleries::GetMediaFileSystemsInteractivity interactive =
388       MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO;
389   if (params->details.get() && params->details->interactive != MediaGalleries::
390          GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE) {
391     interactive = params->details->interactive;
392   }
393 
394   return Setup(GetProfile(), &error_, base::Bind(
395       &MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit, this,
396       interactive));
397 }
398 
OnPreferencesInit(MediaGalleries::GetMediaFileSystemsInteractivity interactive)399 void MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit(
400     MediaGalleries::GetMediaFileSystemsInteractivity interactive) {
401   switch (interactive) {
402     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_YES: {
403       // The MediaFileSystemRegistry only updates preferences for extensions
404       // that it knows are in use. Since this may be the first call to
405       // chrome.getMediaFileSystems for this extension, call
406       // GetMediaFileSystemsForExtension() here solely so that
407       // MediaFileSystemRegistry will send preference changes.
408       GetMediaFileSystemsForExtension(base::Bind(
409           &MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog, this));
410       return;
411     }
412     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_IF_NEEDED: {
413       GetMediaFileSystemsForExtension(base::Bind(
414           &MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries,
415           this));
416       return;
417     }
418     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO:
419       GetAndReturnGalleries();
420       return;
421     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE:
422       NOTREACHED();
423   }
424   SendResponse(false);
425 }
426 
AlwaysShowDialog(const std::vector<MediaFileSystemInfo> &)427 void MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog(
428     const std::vector<MediaFileSystemInfo>& /*filesystems*/) {
429   ShowDialog();
430 }
431 
ShowDialogIfNoGalleries(const std::vector<MediaFileSystemInfo> & filesystems)432 void MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries(
433     const std::vector<MediaFileSystemInfo>& filesystems) {
434   if (filesystems.empty())
435     ShowDialog();
436   else
437     ReturnGalleries(filesystems);
438 }
439 
GetAndReturnGalleries()440 void MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries() {
441   GetMediaFileSystemsForExtension(base::Bind(
442       &MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries, this));
443 }
444 
ReturnGalleries(const std::vector<MediaFileSystemInfo> & filesystems)445 void MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries(
446     const std::vector<MediaFileSystemInfo>& filesystems) {
447   scoped_ptr<base::ListValue> list(
448       ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
449   if (!list.get()) {
450     SendResponse(false);
451     return;
452   }
453 
454   // The custom JS binding will use this list to create DOMFileSystem objects.
455   SetResult(list.release());
456   SendResponse(true);
457 }
458 
ShowDialog()459 void MediaGalleriesGetMediaFileSystemsFunction::ShowDialog() {
460   media_galleries::UsageCount(media_galleries::SHOW_DIALOG);
461   const Extension* extension = GetExtension();
462   WebContents* contents =
463       GetWebContents(render_view_host(), GetProfile(), extension->id());
464   if (!contents) {
465     SendResponse(false);
466     return;
467   }
468 
469   // Controller will delete itself.
470   base::Closure cb = base::Bind(
471       &MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries, this);
472   new MediaGalleriesPermissionController(contents, *extension, cb);
473 }
474 
GetMediaFileSystemsForExtension(const MediaFileSystemsCallback & cb)475 void MediaGalleriesGetMediaFileSystemsFunction::GetMediaFileSystemsForExtension(
476     const MediaFileSystemsCallback& cb) {
477   if (!render_view_host()) {
478     cb.Run(std::vector<MediaFileSystemInfo>());
479     return;
480   }
481   MediaFileSystemRegistry* registry = media_file_system_registry();
482   DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
483   registry->GetMediaFileSystemsForExtension(
484       render_view_host(), GetExtension(), cb);
485 }
486 
487 MediaGalleriesGetAllMediaFileSystemMetadataFunction::
~MediaGalleriesGetAllMediaFileSystemMetadataFunction()488     ~MediaGalleriesGetAllMediaFileSystemMetadataFunction() {}
489 
RunAsync()490 bool MediaGalleriesGetAllMediaFileSystemMetadataFunction::RunAsync() {
491   media_galleries::UsageCount(
492       media_galleries::GET_ALL_MEDIA_FILE_SYSTEM_METADATA);
493   return Setup(GetProfile(), &error_, base::Bind(
494       &MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnPreferencesInit,
495       this));
496 }
497 
OnPreferencesInit()498 void MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnPreferencesInit() {
499   MediaFileSystemRegistry* registry = media_file_system_registry();
500   MediaGalleriesPreferences* prefs = registry->GetPreferences(GetProfile());
501   DCHECK(prefs->IsInitialized());
502   MediaGalleryPrefIdSet permitted_gallery_ids =
503       prefs->GalleriesForExtension(*GetExtension());
504 
505   MediaStorageUtil::DeviceIdSet* device_ids = new MediaStorageUtil::DeviceIdSet;
506   const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
507   for (MediaGalleryPrefIdSet::const_iterator it = permitted_gallery_ids.begin();
508        it != permitted_gallery_ids.end(); ++it) {
509     MediaGalleriesPrefInfoMap::const_iterator gallery_it = galleries.find(*it);
510     DCHECK(gallery_it != galleries.end());
511     device_ids->insert(gallery_it->second.device_id);
512   }
513 
514   MediaStorageUtil::FilterAttachedDevices(
515       device_ids,
516       base::Bind(
517           &MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries,
518           this,
519           permitted_gallery_ids,
520           base::Owned(device_ids)));
521 }
522 
OnGetGalleries(const MediaGalleryPrefIdSet & permitted_gallery_ids,const MediaStorageUtil::DeviceIdSet * available_devices)523 void MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries(
524     const MediaGalleryPrefIdSet& permitted_gallery_ids,
525     const MediaStorageUtil::DeviceIdSet* available_devices) {
526   MediaFileSystemRegistry* registry = media_file_system_registry();
527   MediaGalleriesPreferences* prefs = registry->GetPreferences(GetProfile());
528 
529   base::ListValue* list = new base::ListValue();
530   const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
531   for (MediaGalleryPrefIdSet::const_iterator it = permitted_gallery_ids.begin();
532        it != permitted_gallery_ids.end(); ++it) {
533     MediaGalleriesPrefInfoMap::const_iterator gallery_it = galleries.find(*it);
534     DCHECK(gallery_it != galleries.end());
535     const MediaGalleryPrefInfo& gallery = gallery_it->second;
536     MediaGalleries::MediaFileSystemMetadata metadata;
537     metadata.name = base::UTF16ToUTF8(gallery.GetGalleryDisplayName());
538     metadata.gallery_id = base::Uint64ToString(gallery.pref_id);
539     metadata.is_removable = StorageInfo::IsRemovableDevice(gallery.device_id);
540     metadata.is_media_device = StorageInfo::IsMediaDevice(gallery.device_id);
541     metadata.is_available = ContainsKey(*available_devices, gallery.device_id);
542     list->Append(metadata.ToValue().release());
543   }
544 
545   SetResult(list);
546   SendResponse(true);
547 }
548 
549 MediaGalleriesAddUserSelectedFolderFunction::
~MediaGalleriesAddUserSelectedFolderFunction()550     ~MediaGalleriesAddUserSelectedFolderFunction() {}
551 
RunAsync()552 bool MediaGalleriesAddUserSelectedFolderFunction::RunAsync() {
553   media_galleries::UsageCount(media_galleries::ADD_USER_SELECTED_FOLDER);
554   return Setup(GetProfile(), &error_, base::Bind(
555       &MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit, this));
556 }
557 
OnPreferencesInit()558 void MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit() {
559   Profile* profile = GetProfile();
560   const std::string& app_id = GetExtension()->id();
561   WebContents* contents = GetWebContents(render_view_host(), profile, app_id);
562   if (!contents) {
563     // When the request originated from a background page, but there is no app
564     // window open, check to see if it originated from a tab and display the
565     // dialog in that tab.
566     bool found_tab = extensions::ExtensionTabUtil::GetTabById(
567         source_tab_id(), profile, profile->IsOffTheRecord(),
568         NULL, NULL, &contents, NULL);
569     if (!found_tab || !contents) {
570       SendResponse(false);
571       return;
572     }
573   }
574 
575   if (!user_gesture()) {
576     OnDirectorySelected(base::FilePath());
577     return;
578   }
579 
580   base::FilePath last_used_path =
581       extensions::file_system_api::GetLastChooseEntryDirectory(
582           extensions::ExtensionPrefs::Get(profile), app_id);
583   SelectDirectoryDialog::Callback callback = base::Bind(
584       &MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected, this);
585   scoped_refptr<SelectDirectoryDialog> select_directory_dialog =
586       new SelectDirectoryDialog(contents, callback);
587   select_directory_dialog->Show(last_used_path);
588 }
589 
OnDirectorySelected(const base::FilePath & selected_directory)590 void MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected(
591     const base::FilePath& selected_directory) {
592   if (selected_directory.empty()) {
593     // User cancelled case.
594     GetMediaFileSystemsForExtension(base::Bind(
595         &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
596         this,
597         kInvalidMediaGalleryPrefId));
598     return;
599   }
600 
601   extensions::file_system_api::SetLastChooseEntryDirectory(
602       extensions::ExtensionPrefs::Get(GetProfile()),
603       GetExtension()->id(),
604       selected_directory);
605 
606   MediaGalleriesPreferences* preferences =
607       media_file_system_registry()->GetPreferences(GetProfile());
608   MediaGalleryPrefId pref_id =
609       preferences->AddGalleryByPath(selected_directory,
610                                     MediaGalleryPrefInfo::kUserAdded);
611   preferences->SetGalleryPermissionForExtension(*GetExtension(), pref_id, true);
612 
613   GetMediaFileSystemsForExtension(base::Bind(
614       &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
615       this,
616       pref_id));
617 }
618 
ReturnGalleriesAndId(MediaGalleryPrefId pref_id,const std::vector<MediaFileSystemInfo> & filesystems)619 void MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId(
620     MediaGalleryPrefId pref_id,
621     const std::vector<MediaFileSystemInfo>& filesystems) {
622   scoped_ptr<base::ListValue> list(
623       ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
624   if (!list.get()) {
625     SendResponse(false);
626     return;
627   }
628 
629   int index = -1;
630   if (pref_id != kInvalidMediaGalleryPrefId) {
631     for (size_t i = 0; i < filesystems.size(); ++i) {
632       if (filesystems[i].pref_id == pref_id) {
633         index = i;
634         break;
635       }
636     }
637   }
638   base::DictionaryValue* results = new base::DictionaryValue;
639   results->SetWithoutPathExpansion("mediaFileSystems", list.release());
640   results->SetIntegerWithoutPathExpansion("selectedFileSystemIndex", index);
641   SetResult(results);
642   SendResponse(true);
643 }
644 
645 void
GetMediaFileSystemsForExtension(const MediaFileSystemsCallback & cb)646 MediaGalleriesAddUserSelectedFolderFunction::GetMediaFileSystemsForExtension(
647     const MediaFileSystemsCallback& cb) {
648   if (!render_view_host()) {
649     cb.Run(std::vector<MediaFileSystemInfo>());
650     return;
651   }
652   MediaFileSystemRegistry* registry = media_file_system_registry();
653   DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
654   registry->GetMediaFileSystemsForExtension(
655       render_view_host(), GetExtension(), cb);
656 }
657 
658 MediaGalleriesDropPermissionForMediaFileSystemFunction::
~MediaGalleriesDropPermissionForMediaFileSystemFunction()659     ~MediaGalleriesDropPermissionForMediaFileSystemFunction() {}
660 
RunAsync()661 bool MediaGalleriesDropPermissionForMediaFileSystemFunction::RunAsync() {
662   media_galleries::UsageCount(
663       media_galleries::DROP_PERMISSION_FOR_MEDIA_FILE_SYSTEM);
664 
665   scoped_ptr<DropPermissionForMediaFileSystem::Params> params(
666       DropPermissionForMediaFileSystem::Params::Create(*args_));
667   EXTENSION_FUNCTION_VALIDATE(params.get());
668   MediaGalleryPrefId pref_id;
669   if (!base::StringToUint64(params->gallery_id, &pref_id)) {
670     error_ = kInvalidGalleryId;
671     return false;
672   }
673 
674   base::Closure callback = base::Bind(
675       &MediaGalleriesDropPermissionForMediaFileSystemFunction::
676           OnPreferencesInit,
677       this,
678       pref_id);
679   return Setup(GetProfile(), &error_, callback);
680 }
681 
OnPreferencesInit(MediaGalleryPrefId pref_id)682 void MediaGalleriesDropPermissionForMediaFileSystemFunction::OnPreferencesInit(
683     MediaGalleryPrefId pref_id) {
684   MediaGalleriesPreferences* preferences =
685       media_file_system_registry()->GetPreferences(GetProfile());
686   if (!ContainsKey(preferences->known_galleries(), pref_id)) {
687     error_ = kNonExistentGalleryId;
688     SendResponse(false);
689     return;
690   }
691 
692   bool dropped = preferences->SetGalleryPermissionForExtension(
693       *GetExtension(), pref_id, false);
694   if (dropped)
695     SetResult(new base::StringValue(base::Uint64ToString(pref_id)));
696   else
697     error_ = kFailedToSetGalleryPermission;
698   SendResponse(dropped);
699 }
700 
~MediaGalleriesStartMediaScanFunction()701 MediaGalleriesStartMediaScanFunction::~MediaGalleriesStartMediaScanFunction() {}
702 
RunAsync()703 bool MediaGalleriesStartMediaScanFunction::RunAsync() {
704   media_galleries::UsageCount(media_galleries::START_MEDIA_SCAN);
705   if (!CheckScanPermission(GetExtension(), &error_)) {
706     MediaGalleriesEventRouter::Get(GetProfile())->OnScanError(
707         GetExtension()->id());
708     return false;
709   }
710   return Setup(GetProfile(), &error_, base::Bind(
711       &MediaGalleriesStartMediaScanFunction::OnPreferencesInit, this));
712 }
713 
OnPreferencesInit()714 void MediaGalleriesStartMediaScanFunction::OnPreferencesInit() {
715   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
716   MediaGalleriesEventRouter* api = MediaGalleriesEventRouter::Get(GetProfile());
717   if (!api->ExtensionHasScanProgressListener(GetExtension()->id())) {
718     error_ = kMissingEventListener;
719     SendResponse(false);
720     return;
721   }
722 
723   media_scan_manager()->StartScan(GetProfile(), GetExtension(), user_gesture());
724   SendResponse(true);
725 }
726 
727 MediaGalleriesCancelMediaScanFunction::
~MediaGalleriesCancelMediaScanFunction()728     ~MediaGalleriesCancelMediaScanFunction() {
729 }
730 
RunAsync()731 bool MediaGalleriesCancelMediaScanFunction::RunAsync() {
732   media_galleries::UsageCount(media_galleries::CANCEL_MEDIA_SCAN);
733   if (!CheckScanPermission(GetExtension(), &error_)) {
734     MediaGalleriesEventRouter::Get(GetProfile())->OnScanError(
735         GetExtension()->id());
736     return false;
737   }
738   return Setup(GetProfile(), &error_, base::Bind(
739       &MediaGalleriesCancelMediaScanFunction::OnPreferencesInit, this));
740 }
741 
OnPreferencesInit()742 void MediaGalleriesCancelMediaScanFunction::OnPreferencesInit() {
743   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
744   media_scan_manager()->CancelScan(GetProfile(), GetExtension());
745   SendResponse(true);
746 }
747 
~MediaGalleriesAddScanResultsFunction()748 MediaGalleriesAddScanResultsFunction::~MediaGalleriesAddScanResultsFunction() {}
749 
RunAsync()750 bool MediaGalleriesAddScanResultsFunction::RunAsync() {
751   media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS);
752   if (!CheckScanPermission(GetExtension(), &error_)) {
753     // We don't fire a scan progress error here, as it would be unintuitive.
754     return false;
755   }
756   if (!user_gesture())
757     return false;
758 
759   return Setup(GetProfile(), &error_, base::Bind(
760       &MediaGalleriesAddScanResultsFunction::OnPreferencesInit, this));
761 }
762 
763 MediaGalleriesScanResultController*
MakeDialog(content::WebContents * web_contents,const extensions::Extension & extension,const base::Closure & on_finish)764 MediaGalleriesAddScanResultsFunction::MakeDialog(
765     content::WebContents* web_contents,
766     const extensions::Extension& extension,
767     const base::Closure& on_finish) {
768   // Controller will delete itself.
769   return new MediaGalleriesScanResultController(web_contents, extension,
770                                                 on_finish);
771 }
772 
OnPreferencesInit()773 void MediaGalleriesAddScanResultsFunction::OnPreferencesInit() {
774   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
775   const Extension* extension = GetExtension();
776   MediaGalleriesPreferences* preferences =
777       media_file_system_registry()->GetPreferences(GetProfile());
778   if (MediaGalleriesScanResultController::ScanResultCountForExtension(
779           preferences, extension) == 0) {
780     GetAndReturnGalleries();
781     return;
782   }
783 
784   WebContents* contents =
785       GetWebContents(render_view_host(), GetProfile(), extension->id());
786   if (!contents) {
787     SendResponse(false);
788     return;
789   }
790 
791   base::Closure cb = base::Bind(
792       &MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries, this);
793   MakeDialog(contents, *extension, cb);
794 }
795 
GetAndReturnGalleries()796 void MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries() {
797   if (!render_view_host()) {
798     ReturnGalleries(std::vector<MediaFileSystemInfo>());
799     return;
800   }
801   MediaFileSystemRegistry* registry = media_file_system_registry();
802   DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
803   registry->GetMediaFileSystemsForExtension(
804       render_view_host(), GetExtension(),
805       base::Bind(&MediaGalleriesAddScanResultsFunction::ReturnGalleries,
806                  this));
807 }
808 
ReturnGalleries(const std::vector<MediaFileSystemInfo> & filesystems)809 void MediaGalleriesAddScanResultsFunction::ReturnGalleries(
810     const std::vector<MediaFileSystemInfo>& filesystems) {
811   scoped_ptr<base::ListValue> list(
812       ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
813   if (!list.get()) {
814     SendResponse(false);
815     return;
816   }
817 
818   // The custom JS binding will use this list to create DOMFileSystem objects.
819   SetResult(list.release());
820   SendResponse(true);
821 }
822 
~MediaGalleriesGetMetadataFunction()823 MediaGalleriesGetMetadataFunction::~MediaGalleriesGetMetadataFunction() {}
824 
RunAsync()825 bool MediaGalleriesGetMetadataFunction::RunAsync() {
826   std::string blob_uuid;
827   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &blob_uuid));
828 
829   const base::Value* options_value = NULL;
830   if (!args_->Get(1, &options_value))
831     return false;
832   scoped_ptr<MediaGalleries::MediaMetadataOptions> options =
833       MediaGalleries::MediaMetadataOptions::FromValue(*options_value);
834   if (!options)
835     return false;
836 
837   return Setup(GetProfile(), &error_, base::Bind(
838       &MediaGalleriesGetMetadataFunction::OnPreferencesInit, this,
839       options->metadata_type, blob_uuid));
840 }
841 
OnPreferencesInit(MediaGalleries::GetMetadataType metadata_type,const std::string & blob_uuid)842 void MediaGalleriesGetMetadataFunction::OnPreferencesInit(
843     MediaGalleries::GetMetadataType metadata_type,
844     const std::string& blob_uuid) {
845   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
846 
847   // BlobReader is self-deleting.
848   BlobReader* reader = new BlobReader(
849       GetProfile(),
850       blob_uuid,
851       base::Bind(&MediaGalleriesGetMetadataFunction::GetMetadata, this,
852                  metadata_type, blob_uuid));
853   reader->SetByteRange(0, net::kMaxBytesToSniff);
854   reader->Start();
855 }
856 
GetMetadata(MediaGalleries::GetMetadataType metadata_type,const std::string & blob_uuid,scoped_ptr<std::string> blob_header,int64 total_blob_length)857 void MediaGalleriesGetMetadataFunction::GetMetadata(
858     MediaGalleries::GetMetadataType metadata_type, const std::string& blob_uuid,
859     scoped_ptr<std::string> blob_header, int64 total_blob_length) {
860   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
861 
862   std::string mime_type;
863   bool mime_type_sniffed = net::SniffMimeTypeFromLocalData(
864       blob_header->c_str(), blob_header->size(), &mime_type);
865 
866   if (!mime_type_sniffed) {
867     SendResponse(false);
868     return;
869   }
870 
871   if (metadata_type == MediaGalleries::GET_METADATA_TYPE_MIMETYPEONLY) {
872     MediaGalleries::MediaMetadata metadata;
873     metadata.mime_type = mime_type;
874 
875     base::DictionaryValue* result_dictionary = new base::DictionaryValue;
876     result_dictionary->Set(kMetadataKey, metadata.ToValue().release());
877     SetResult(result_dictionary);
878     SendResponse(true);
879     return;
880   }
881 
882   // We get attached images by default. GET_METADATA_TYPE_NONE is the default
883   // value if the caller doesn't specify the metadata type.
884   bool get_attached_images =
885       metadata_type == MediaGalleries::GET_METADATA_TYPE_ALL ||
886       metadata_type == MediaGalleries::GET_METADATA_TYPE_NONE;
887 
888   scoped_refptr<metadata::SafeMediaMetadataParser> parser(
889       new metadata::SafeMediaMetadataParser(GetProfile(), blob_uuid,
890                                             total_blob_length, mime_type,
891                                             get_attached_images));
892   parser->Start(base::Bind(
893       &MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone, this));
894 }
895 
OnSafeMediaMetadataParserDone(bool parse_success,scoped_ptr<base::DictionaryValue> metadata_dictionary,scoped_ptr<std::vector<metadata::AttachedImage>> attached_images)896 void MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone(
897     bool parse_success, scoped_ptr<base::DictionaryValue> metadata_dictionary,
898     scoped_ptr<std::vector<metadata::AttachedImage> > attached_images) {
899   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
900 
901   if (!parse_success) {
902     SendResponse(false);
903     return;
904   }
905 
906   DCHECK(metadata_dictionary.get());
907   DCHECK(attached_images.get());
908 
909   scoped_ptr<base::DictionaryValue> result_dictionary(
910       new base::DictionaryValue);
911   result_dictionary->Set(kMetadataKey, metadata_dictionary.release());
912 
913   if (attached_images->empty()) {
914     SetResult(result_dictionary.release());
915     SendResponse(true);
916     return;
917   }
918 
919   result_dictionary->Set(kAttachedImagesBlobInfoKey, new base::ListValue);
920   metadata::AttachedImage* first_image = &attached_images->front();
921   content::BrowserContext::CreateMemoryBackedBlob(
922       GetProfile(),
923       first_image->data.c_str(),
924       first_image->data.size(),
925       base::Bind(&MediaGalleriesGetMetadataFunction::ConstructNextBlob,
926                  this, base::Passed(&result_dictionary),
927                  base::Passed(&attached_images),
928                  base::Passed(make_scoped_ptr(new std::vector<std::string>))));
929 }
930 
ConstructNextBlob(scoped_ptr<base::DictionaryValue> result_dictionary,scoped_ptr<std::vector<metadata::AttachedImage>> attached_images,scoped_ptr<std::vector<std::string>> blob_uuids,scoped_ptr<content::BlobHandle> current_blob)931 void MediaGalleriesGetMetadataFunction::ConstructNextBlob(
932     scoped_ptr<base::DictionaryValue> result_dictionary,
933     scoped_ptr<std::vector<metadata::AttachedImage> > attached_images,
934     scoped_ptr<std::vector<std::string> > blob_uuids,
935     scoped_ptr<content::BlobHandle> current_blob) {
936   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
937 
938   DCHECK(result_dictionary.get());
939   DCHECK(attached_images.get());
940   DCHECK(blob_uuids.get());
941   DCHECK(current_blob.get());
942 
943   DCHECK(!attached_images->empty());
944   DCHECK_LT(blob_uuids->size(), attached_images->size());
945 
946   // For the newly constructed Blob, store its image's metadata and Blob UUID.
947   base::ListValue* attached_images_list = NULL;
948   result_dictionary->GetList(kAttachedImagesBlobInfoKey, &attached_images_list);
949   DCHECK(attached_images_list);
950   DCHECK_LT(attached_images_list->GetSize(), attached_images->size());
951 
952   metadata::AttachedImage* current_image =
953       &(*attached_images)[blob_uuids->size()];
954   base::DictionaryValue* attached_image = new base::DictionaryValue;
955   attached_image->Set(kBlobUUIDKey, new base::StringValue(
956       current_blob->GetUUID()));
957   attached_image->Set(kTypeKey, new base::StringValue(
958       current_image->type));
959   attached_image->Set(kSizeKey, new base::FundamentalValue(
960       base::checked_cast<int>(current_image->data.size())));
961   attached_images_list->Append(attached_image);
962 
963   blob_uuids->push_back(current_blob->GetUUID());
964   WebContents* contents = WebContents::FromRenderViewHost(render_view_host());
965   extensions::BlobHolder* holder =
966       extensions::BlobHolder::FromRenderProcessHost(
967           contents->GetRenderProcessHost());
968   holder->HoldBlobReference(current_blob.Pass());
969 
970   // Construct the next Blob if necessary.
971   if (blob_uuids->size() < attached_images->size()) {
972     metadata::AttachedImage* next_image =
973         &(*attached_images)[blob_uuids->size()];
974     content::BrowserContext::CreateMemoryBackedBlob(
975         GetProfile(),
976         next_image->data.c_str(),
977         next_image->data.size(),
978         base::Bind(&MediaGalleriesGetMetadataFunction::ConstructNextBlob,
979                    this, base::Passed(&result_dictionary),
980                    base::Passed(&attached_images), base::Passed(&blob_uuids)));
981     return;
982   }
983 
984   // All Blobs have been constructed. The renderer will take ownership.
985   SetResult(result_dictionary.release());
986   SetTransferredBlobUUIDs(*blob_uuids);
987   SendResponse(true);
988 }
989 
990 }  // namespace extensions
991