Upstream version 7.35.139.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / media_galleries / media_galleries_api.cc
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/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"
56
57 using content::WebContents;
58 using storage_monitor::MediaStorageUtil;
59 using storage_monitor::StorageInfo;
60 using web_modal::WebContentsModalDialogManager;
61
62 namespace extensions {
63
64 namespace MediaGalleries = api::media_galleries;
65 namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems;
66
67 namespace {
68
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.";
75
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";
82
83 MediaFileSystemRegistry* media_file_system_registry() {
84   return g_browser_process->media_file_system_registry();
85 }
86
87 MediaScanManager* media_scan_manager() {
88   return media_file_system_registry()->media_scan_manager();
89 }
90
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;
98     return false;
99   }
100
101   MediaGalleriesPreferences* preferences =
102       media_file_system_registry()->GetPreferences(profile);
103   preferences->EnsureInitialized(callback);
104   return true;
105 }
106
107 WebContents* GetWebContents(content::RenderViewHost* rvh,
108                             Profile* profile,
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
116     // host the dialog.
117     apps::AppWindow* window = apps::AppWindowRegistry::Get(profile)
118                                   ->GetCurrentAppWindowForApp(app_id);
119     contents = window ? window->web_contents() : NULL;
120   }
121   return contents;
122 }
123
124 base::ListValue* ConstructFileSystemList(
125     content::RenderViewHost* rvh,
126     const Extension* extension,
127     const std::vector<MediaFileSystemInfo>& filesystems) {
128   if (!rvh)
129     return NULL;
130
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, &copy_to_param);
139   MediaGalleriesPermission::CheckParam delete_param(
140       MediaGalleriesPermission::kDeletePermission);
141   bool has_delete_permission = PermissionsData::CheckAPIPermissionWithParam(
142       extension, APIPermission::kMediaGalleries, &delete_param);
143
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());
149
150     // Send the file system id so the renderer can create a valid FileSystem
151     // object.
152     file_system_dict_value->SetStringWithoutPathExpansion(
153         "fsid", filesystems[i].fsid);
154
155     file_system_dict_value->SetStringWithoutPathExpansion(
156         kNameKey, filesystems[i].name);
157     file_system_dict_value->SetStringWithoutPathExpansion(
158         kGalleryIdKey,
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);
163     }
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);
170
171     list->Append(file_system_dict_value.release());
172
173     if (filesystems[i].path.empty())
174       continue;
175
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);
184         }
185       }
186     }
187   }
188
189   return list.release();
190 }
191
192 bool CheckScanPermission(const extensions::Extension* extension,
193                          std::string* error) {
194   DCHECK(extension);
195   DCHECK(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;
203 }
204
205 class SelectDirectoryDialog : public ui::SelectFileDialog::Listener,
206                               public base::RefCounted<SelectDirectoryDialog> {
207  public:
208   // Selected file path, or an empty path if the user canceled.
209   typedef base::Callback<void(const base::FilePath&)> Callback;
210
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));
216   }
217
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),
223       default_path,
224       NULL,
225       0,
226       base::FilePath::StringType(),
227       platform_util::GetTopLevel(web_contents_->GetView()->GetNativeView()),
228       NULL);
229   }
230
231   // ui::SelectFileDialog::Listener implementation.
232   virtual void FileSelected(const base::FilePath& path,
233                             int index,
234                             void* params) OVERRIDE {
235     callback_.Run(path);
236     Release();  // Balanced in Show().
237   }
238
239   virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
240                                   void* params) OVERRIDE {
241     NOTREACHED() << "Should not be able to select multiple files";
242   }
243
244   virtual void FileSelectionCanceled(void* params) OVERRIDE {
245     callback_.Run(base::FilePath());
246     Release();  // Balanced in Show().
247   }
248
249  private:
250   friend class base::RefCounted<SelectDirectoryDialog>;
251   virtual ~SelectDirectoryDialog() {}
252
253   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
254   WebContents* web_contents_;
255   Callback callback_;
256
257   DISALLOW_COPY_AND_ASSIGN(SelectDirectoryDialog);
258 };
259
260 }  // namespace
261
262 MediaGalleriesEventRouter::MediaGalleriesEventRouter(
263     content::BrowserContext* context)
264     : profile_(Profile::FromBrowserContext(context)), weak_ptr_factory_(this) {
265   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
266   DCHECK(profile_);
267   media_scan_manager()->AddObserver(profile_, this);
268 }
269
270 MediaGalleriesEventRouter::~MediaGalleriesEventRouter() {
271 }
272
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_);
278 }
279
280 static base::LazyInstance<
281     BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter> > g_factory =
282     LAZY_INSTANCE_INITIALIZER;
283
284 // static
285 BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>*
286 MediaGalleriesEventRouter::GetFactoryInstance() {
287   return g_factory.Pointer();
288 }
289
290 // static
291 MediaGalleriesEventRouter* MediaGalleriesEventRouter::Get(
292     content::BrowserContext* context) {
293   DCHECK(media_file_system_registry()
294              ->GetPreferences(Profile::FromBrowserContext(context))
295              ->IsInitialized());
296   return BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>::Get(context);
297 }
298
299 bool MediaGalleriesEventRouter::ExtensionHasScanProgressListener(
300     const std::string& extension_id) const {
301   EventRouter* router = ExtensionSystem::Get(profile_)->event_router();
302   return router->ExtensionHasEventListener(
303       extension_id,
304       MediaGalleries::OnScanProgress::kEventName);
305 }
306
307 void MediaGalleriesEventRouter::OnScanStarted(const std::string& extension_id) {
308   MediaGalleries::ScanProgressDetails details;
309   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_START;
310   DispatchEventToExtension(
311       extension_id,
312       MediaGalleries::OnScanProgress::kEventName,
313       MediaGalleries::OnScanProgress::Create(details).Pass());
314 }
315
316 void MediaGalleriesEventRouter::OnScanCancelled(
317     const std::string& extension_id) {
318   MediaGalleries::ScanProgressDetails details;
319   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_CANCEL;
320   DispatchEventToExtension(
321       extension_id,
322       MediaGalleries::OnScanProgress::kEventName,
323       MediaGalleries::OnScanProgress::Create(details).Pass());
324 }
325
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(
337       extension_id,
338       MediaGalleries::OnScanProgress::kEventName,
339       MediaGalleries::OnScanProgress::Create(details).Pass());
340 }
341
342 void MediaGalleriesEventRouter::OnScanError(
343     const std::string& extension_id) {
344   MediaGalleries::ScanProgressDetails details;
345   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_ERROR;
346   DispatchEventToExtension(
347       extension_id,
348       MediaGalleries::OnScanProgress::kEventName,
349       MediaGalleries::OnScanProgress::Create(details).Pass());
350 }
351
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))
360     return;
361
362   scoped_ptr<extensions::Event> event(
363       new extensions::Event(event_name, event_args.Pass()));
364   router->DispatchEventToExtension(extension_id, event.Pass());
365 }
366
367 MediaGalleriesGetMediaFileSystemsFunction::
368     ~MediaGalleriesGetMediaFileSystemsFunction() {}
369
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;
380   }
381
382   return Setup(GetProfile(), &error_, base::Bind(
383       &MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit, this,
384       interactive));
385 }
386
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));
398       return;
399     }
400     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_IF_NEEDED: {
401       GetMediaFileSystemsForExtension(base::Bind(
402           &MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries,
403           this));
404       return;
405     }
406     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO:
407       GetAndReturnGalleries();
408       return;
409     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE:
410       NOTREACHED();
411   }
412   SendResponse(false);
413 }
414
415 void MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog(
416     const std::vector<MediaFileSystemInfo>& /*filesystems*/) {
417   ShowDialog();
418 }
419
420 void MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries(
421     const std::vector<MediaFileSystemInfo>& filesystems) {
422   if (filesystems.empty())
423     ShowDialog();
424   else
425     ReturnGalleries(filesystems);
426 }
427
428 void MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries() {
429   GetMediaFileSystemsForExtension(base::Bind(
430       &MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries, this));
431 }
432
433 void MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries(
434     const std::vector<MediaFileSystemInfo>& filesystems) {
435   scoped_ptr<base::ListValue> list(
436       ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
437   if (!list.get()) {
438     SendResponse(false);
439     return;
440   }
441
442   // The custom JS binding will use this list to create DOMFileSystem objects.
443   SetResult(list.release());
444   SendResponse(true);
445 }
446
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());
452   if (!contents) {
453     SendResponse(false);
454     return;
455   }
456
457   // Controller will delete itself.
458   base::Closure cb = base::Bind(
459       &MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries, this);
460   new MediaGalleriesDialogController(contents, *extension, cb);
461 }
462
463 void MediaGalleriesGetMediaFileSystemsFunction::GetMediaFileSystemsForExtension(
464     const MediaFileSystemsCallback& cb) {
465   if (!render_view_host()) {
466     cb.Run(std::vector<MediaFileSystemInfo>());
467     return;
468   }
469   MediaFileSystemRegistry* registry = media_file_system_registry();
470   DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
471   registry->GetMediaFileSystemsForExtension(
472       render_view_host(), GetExtension(), cb);
473 }
474
475 MediaGalleriesGetAllMediaFileSystemMetadataFunction::
476     ~MediaGalleriesGetAllMediaFileSystemMetadataFunction() {}
477
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,
483       this));
484 }
485
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());
492
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);
500   }
501
502   MediaStorageUtil::FilterAttachedDevices(
503       device_ids,
504       base::Bind(
505           &MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries,
506           this,
507           permitted_gallery_ids,
508           base::Owned(device_ids)));
509 }
510
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());
516
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());
531   }
532
533   SetResult(list);
534   SendResponse(true);
535 }
536
537 MediaGalleriesAddUserSelectedFolderFunction::
538     ~MediaGalleriesAddUserSelectedFolderFunction() {}
539
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));
544 }
545
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);
550   if (!contents) {
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) {
558       SendResponse(false);
559       return;
560     }
561   }
562
563   if (!user_gesture()) {
564     OnDirectorySelected(base::FilePath());
565     return;
566   }
567
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);
576 }
577
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,
584         this,
585         kInvalidMediaGalleryPrefId));
586     return;
587   }
588
589   extensions::file_system_api::SetLastChooseEntryDirectory(
590       extensions::ExtensionPrefs::Get(GetProfile()),
591       GetExtension()->id(),
592       selected_directory);
593
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);
600
601   GetMediaFileSystemsForExtension(base::Bind(
602       &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
603       this,
604       pref_id));
605 }
606
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));
612   if (!list.get()) {
613     SendResponse(false);
614     return;
615   }
616
617   int index = -1;
618   if (pref_id != kInvalidMediaGalleryPrefId) {
619     for (size_t i = 0; i < filesystems.size(); ++i) {
620       if (filesystems[i].pref_id == pref_id) {
621         index = i;
622         break;
623       }
624     }
625   }
626   base::DictionaryValue* results = new base::DictionaryValue;
627   results->SetWithoutPathExpansion("mediaFileSystems", list.release());
628   results->SetIntegerWithoutPathExpansion("selectedFileSystemIndex", index);
629   SetResult(results);
630   SendResponse(true);
631 }
632
633 void
634 MediaGalleriesAddUserSelectedFolderFunction::GetMediaFileSystemsForExtension(
635     const MediaFileSystemsCallback& cb) {
636   if (!render_view_host()) {
637     cb.Run(std::vector<MediaFileSystemInfo>());
638     return;
639   }
640   MediaFileSystemRegistry* registry = media_file_system_registry();
641   DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
642   registry->GetMediaFileSystemsForExtension(
643       render_view_host(), GetExtension(), cb);
644 }
645
646 MediaGalleriesStartMediaScanFunction::~MediaGalleriesStartMediaScanFunction() {}
647
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());
653     return false;
654   }
655   return Setup(GetProfile(), &error_, base::Bind(
656       &MediaGalleriesStartMediaScanFunction::OnPreferencesInit, this));
657 }
658
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;
664     SendResponse(false);
665     return;
666   }
667
668   media_scan_manager()->StartScan(GetProfile(), GetExtension(), user_gesture());
669   SendResponse(true);
670 }
671
672 MediaGalleriesCancelMediaScanFunction::
673     ~MediaGalleriesCancelMediaScanFunction() {
674 }
675
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());
681     return false;
682   }
683   return Setup(GetProfile(), &error_, base::Bind(
684       &MediaGalleriesCancelMediaScanFunction::OnPreferencesInit, this));
685 }
686
687 void MediaGalleriesCancelMediaScanFunction::OnPreferencesInit() {
688   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
689   media_scan_manager()->CancelScan(GetProfile(), GetExtension());
690   SendResponse(true);
691 }
692
693 MediaGalleriesAddScanResultsFunction::~MediaGalleriesAddScanResultsFunction() {}
694
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.
699     return false;
700   }
701   if (!user_gesture())
702     return false;
703
704   return Setup(GetProfile(), &error_, base::Bind(
705       &MediaGalleriesAddScanResultsFunction::OnPreferencesInit, this));
706 }
707
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,
715                                                       on_finish);
716 }
717
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();
726     return;
727   }
728
729   WebContents* contents =
730       GetWebContents(render_view_host(), GetProfile(), extension->id());
731   if (!contents) {
732     SendResponse(false);
733     return;
734   }
735
736   base::Closure cb = base::Bind(
737       &MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries, this);
738   MakeDialog(contents, *extension, cb);
739 }
740
741 void MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries() {
742   if (!render_view_host()) {
743     ReturnGalleries(std::vector<MediaFileSystemInfo>());
744     return;
745   }
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,
751                  this));
752 }
753
754 void MediaGalleriesAddScanResultsFunction::ReturnGalleries(
755     const std::vector<MediaFileSystemInfo>& filesystems) {
756   scoped_ptr<base::ListValue> list(
757       ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
758   if (!list.get()) {
759     SendResponse(false);
760     return;
761   }
762
763   // The custom JS binding will use this list to create DOMFileSystem objects.
764   SetResult(list.release());
765   SendResponse(true);
766 }
767
768 MediaGalleriesGetMetadataFunction::~MediaGalleriesGetMetadataFunction() {}
769
770 bool MediaGalleriesGetMetadataFunction::RunImpl() {
771   std::string blob_uuid;
772   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &blob_uuid));
773
774   const base::Value* options_value = NULL;
775   if (!args_->Get(1, &options_value))
776     return false;
777   scoped_ptr<MediaGalleries::MediaMetadataOptions> options =
778       MediaGalleries::MediaMetadataOptions::FromValue(*options_value);
779   if (!options)
780     return false;
781
782   bool mime_type_only = options->metadata_type ==
783       MediaGalleries::GET_METADATA_TYPE_MIMETYPEONLY;
784
785   return Setup(GetProfile(), &error_, base::Bind(
786       &MediaGalleriesGetMetadataFunction::OnPreferencesInit, this,
787       mime_type_only, blob_uuid));
788 }
789
790 void MediaGalleriesGetMetadataFunction::OnPreferencesInit(
791     bool mime_type_only, const std::string& blob_uuid) {
792   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
793
794   // BlobReader is self-deleting.
795   BlobReader* reader = new BlobReader(
796       GetProfile(),
797       blob_uuid,
798       base::Bind(&MediaGalleriesGetMetadataFunction::SniffMimeType, this,
799                  mime_type_only, blob_uuid));
800   reader->SetByteRange(0, net::kMaxBytesToSniff);
801   reader->Start();
802 }
803
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));
808
809   std::string mime_type;
810   bool mime_type_sniffed = net::SniffMimeTypeFromLocalData(
811       blob_header->c_str(), blob_header->size(), &mime_type);
812
813   if (!mime_type_sniffed) {
814     SendResponse(false);
815     return;
816   }
817
818   if (mime_type_only) {
819     MediaGalleries::MediaMetadata metadata;
820     metadata.mime_type = mime_type;
821     SetResult(metadata.ToValue().release());
822     SendResponse(true);
823     return;
824   }
825
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));
831 }
832
833 void MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone(
834     bool parse_success, base::DictionaryValue* metadata_dictionary) {
835   if (!parse_success) {
836     SendResponse(false);
837     return;
838   }
839
840   SetResult(metadata_dictionary->DeepCopy());
841   SendResponse(true);
842 }
843
844 }  // namespace extensions