Upstream version 7.36.149.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 "extensions/browser/event_router.h"
46 #include "extensions/browser/extension_prefs.h"
47 #include "extensions/browser/extension_system.h"
48 #include "extensions/common/extension.h"
49 #include "extensions/common/permissions/api_permission.h"
50 #include "extensions/common/permissions/media_galleries_permission.h"
51 #include "extensions/common/permissions/permissions_data.h"
52 #include "grit/generated_resources.h"
53 #include "net/base/mime_sniffer.h"
54 #include "ui/base/l10n/l10n_util.h"
55
56 using content::WebContents;
57 using storage_monitor::MediaStorageUtil;
58 using storage_monitor::StorageInfo;
59 using web_modal::WebContentsModalDialogManager;
60
61 namespace extensions {
62
63 namespace MediaGalleries = api::media_galleries;
64 namespace DropPermissionForMediaFileSystem =
65     MediaGalleries::DropPermissionForMediaFileSystem;
66 namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems;
67
68 namespace {
69
70 const char kDisallowedByPolicy[] =
71     "Media Galleries API is disallowed by policy: ";
72 const char kFailedToSetGalleryPermission[] =
73     "Failed to set gallery permission.";
74 const char kInvalidGalleryId[] = "Invalid gallery id.";
75 const char kMissingEventListener[] = "Missing event listener registration.";
76 const char kNonExistentGalleryId[] = "Non-existent gallery id.";
77 const char kNoScanPermission[] = "No permission to scan.";
78
79 const char kDeviceIdKey[] = "deviceId";
80 const char kGalleryIdKey[] = "galleryId";
81 const char kIsAvailableKey[] = "isAvailable";
82 const char kIsMediaDeviceKey[] = "isMediaDevice";
83 const char kIsRemovableKey[] = "isRemovable";
84 const char kNameKey[] = "name";
85
86 MediaFileSystemRegistry* media_file_system_registry() {
87   return g_browser_process->media_file_system_registry();
88 }
89
90 MediaScanManager* media_scan_manager() {
91   return media_file_system_registry()->media_scan_manager();
92 }
93
94 // Checks whether the MediaGalleries API is currently accessible (it may be
95 // disallowed even if an extension has the requisite permission). Then
96 // initializes the MediaGalleriesPreferences
97 bool Setup(Profile* profile, std::string* error, base::Closure callback) {
98   if (!ChromeSelectFilePolicy::FileSelectDialogsAllowed()) {
99     *error = std::string(kDisallowedByPolicy) +
100         prefs::kAllowFileSelectionDialogs;
101     return false;
102   }
103
104   MediaGalleriesPreferences* preferences =
105       media_file_system_registry()->GetPreferences(profile);
106   preferences->EnsureInitialized(callback);
107   return true;
108 }
109
110 WebContents* GetWebContents(content::RenderViewHost* rvh,
111                             Profile* profile,
112                             const std::string& app_id) {
113   WebContents* contents = WebContents::FromRenderViewHost(rvh);
114   WebContentsModalDialogManager* web_contents_modal_dialog_manager =
115       WebContentsModalDialogManager::FromWebContents(contents);
116   if (!web_contents_modal_dialog_manager) {
117     // If there is no WebContentsModalDialogManager, then this contents is
118     // probably the background page for an app. Try to find a app window to
119     // host the dialog.
120     apps::AppWindow* window = apps::AppWindowRegistry::Get(profile)
121                                   ->GetCurrentAppWindowForApp(app_id);
122     contents = window ? window->web_contents() : NULL;
123   }
124   return contents;
125 }
126
127 base::ListValue* ConstructFileSystemList(
128     content::RenderViewHost* rvh,
129     const Extension* extension,
130     const std::vector<MediaFileSystemInfo>& filesystems) {
131   if (!rvh)
132     return NULL;
133
134   MediaGalleriesPermission::CheckParam read_param(
135       MediaGalleriesPermission::kReadPermission);
136   bool has_read_permission = PermissionsData::CheckAPIPermissionWithParam(
137       extension, APIPermission::kMediaGalleries, &read_param);
138   MediaGalleriesPermission::CheckParam copy_to_param(
139       MediaGalleriesPermission::kCopyToPermission);
140   bool has_copy_to_permission = PermissionsData::CheckAPIPermissionWithParam(
141       extension, APIPermission::kMediaGalleries, &copy_to_param);
142   MediaGalleriesPermission::CheckParam delete_param(
143       MediaGalleriesPermission::kDeletePermission);
144   bool has_delete_permission = PermissionsData::CheckAPIPermissionWithParam(
145       extension, APIPermission::kMediaGalleries, &delete_param);
146
147   const int child_id = rvh->GetProcess()->GetID();
148   scoped_ptr<base::ListValue> list(new base::ListValue());
149   for (size_t i = 0; i < filesystems.size(); ++i) {
150     scoped_ptr<base::DictionaryValue> file_system_dict_value(
151         new base::DictionaryValue());
152
153     // Send the file system id so the renderer can create a valid FileSystem
154     // object.
155     file_system_dict_value->SetStringWithoutPathExpansion(
156         "fsid", filesystems[i].fsid);
157
158     file_system_dict_value->SetStringWithoutPathExpansion(
159         kNameKey, filesystems[i].name);
160     file_system_dict_value->SetStringWithoutPathExpansion(
161         kGalleryIdKey,
162         base::Uint64ToString(filesystems[i].pref_id));
163     if (!filesystems[i].transient_device_id.empty()) {
164       file_system_dict_value->SetStringWithoutPathExpansion(
165           kDeviceIdKey, filesystems[i].transient_device_id);
166     }
167     file_system_dict_value->SetBooleanWithoutPathExpansion(
168         kIsRemovableKey, filesystems[i].removable);
169     file_system_dict_value->SetBooleanWithoutPathExpansion(
170         kIsMediaDeviceKey, filesystems[i].media_device);
171     file_system_dict_value->SetBooleanWithoutPathExpansion(
172         kIsAvailableKey, true);
173
174     list->Append(file_system_dict_value.release());
175
176     if (filesystems[i].path.empty())
177       continue;
178
179     if (has_read_permission) {
180       content::ChildProcessSecurityPolicy* policy =
181           content::ChildProcessSecurityPolicy::GetInstance();
182       policy->GrantReadFile(child_id, filesystems[i].path);
183       if (has_delete_permission) {
184         policy->GrantDeleteFrom(child_id, filesystems[i].path);
185         if (has_copy_to_permission) {
186           policy->GrantCopyInto(child_id, filesystems[i].path);
187         }
188       }
189     }
190   }
191
192   return list.release();
193 }
194
195 bool CheckScanPermission(const extensions::Extension* extension,
196                          std::string* error) {
197   DCHECK(extension);
198   DCHECK(error);
199   MediaGalleriesPermission::CheckParam scan_param(
200       MediaGalleriesPermission::kScanPermission);
201   bool has_scan_permission = PermissionsData::CheckAPIPermissionWithParam(
202       extension, APIPermission::kMediaGalleries, &scan_param);
203   if (!has_scan_permission)
204     *error = kNoScanPermission;
205   return has_scan_permission;
206 }
207
208 class SelectDirectoryDialog : public ui::SelectFileDialog::Listener,
209                               public base::RefCounted<SelectDirectoryDialog> {
210  public:
211   // Selected file path, or an empty path if the user canceled.
212   typedef base::Callback<void(const base::FilePath&)> Callback;
213
214   SelectDirectoryDialog(WebContents* web_contents, const Callback& callback)
215       : web_contents_(web_contents),
216         callback_(callback) {
217     select_file_dialog_ = ui::SelectFileDialog::Create(
218         this, new ChromeSelectFilePolicy(web_contents));
219   }
220
221   void Show(const base::FilePath& default_path) {
222     AddRef();  // Balanced in the two reachable listener outcomes.
223     select_file_dialog_->SelectFile(
224       ui::SelectFileDialog::SELECT_FOLDER,
225       l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY_TITLE),
226       default_path,
227       NULL,
228       0,
229       base::FilePath::StringType(),
230       platform_util::GetTopLevel(web_contents_->GetNativeView()),
231       NULL);
232   }
233
234   // ui::SelectFileDialog::Listener implementation.
235   virtual void FileSelected(const base::FilePath& path,
236                             int index,
237                             void* params) OVERRIDE {
238     callback_.Run(path);
239     Release();  // Balanced in Show().
240   }
241
242   virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
243                                   void* params) OVERRIDE {
244     NOTREACHED() << "Should not be able to select multiple files";
245   }
246
247   virtual void FileSelectionCanceled(void* params) OVERRIDE {
248     callback_.Run(base::FilePath());
249     Release();  // Balanced in Show().
250   }
251
252  private:
253   friend class base::RefCounted<SelectDirectoryDialog>;
254   virtual ~SelectDirectoryDialog() {}
255
256   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
257   WebContents* web_contents_;
258   Callback callback_;
259
260   DISALLOW_COPY_AND_ASSIGN(SelectDirectoryDialog);
261 };
262
263 }  // namespace
264
265 MediaGalleriesEventRouter::MediaGalleriesEventRouter(
266     content::BrowserContext* context)
267     : profile_(Profile::FromBrowserContext(context)), weak_ptr_factory_(this) {
268   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
269   DCHECK(profile_);
270   media_scan_manager()->AddObserver(profile_, this);
271 }
272
273 MediaGalleriesEventRouter::~MediaGalleriesEventRouter() {
274 }
275
276 void MediaGalleriesEventRouter::Shutdown() {
277   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
278   weak_ptr_factory_.InvalidateWeakPtrs();
279   media_scan_manager()->RemoveObserver(profile_);
280   media_scan_manager()->CancelScansForProfile(profile_);
281 }
282
283 static base::LazyInstance<
284     BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter> > g_factory =
285     LAZY_INSTANCE_INITIALIZER;
286
287 // static
288 BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>*
289 MediaGalleriesEventRouter::GetFactoryInstance() {
290   return g_factory.Pointer();
291 }
292
293 // static
294 MediaGalleriesEventRouter* MediaGalleriesEventRouter::Get(
295     content::BrowserContext* context) {
296   DCHECK(media_file_system_registry()
297              ->GetPreferences(Profile::FromBrowserContext(context))
298              ->IsInitialized());
299   return BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>::Get(context);
300 }
301
302 bool MediaGalleriesEventRouter::ExtensionHasScanProgressListener(
303     const std::string& extension_id) const {
304   return EventRouter::Get(profile_)->ExtensionHasEventListener(
305       extension_id, MediaGalleries::OnScanProgress::kEventName);
306 }
307
308 void MediaGalleriesEventRouter::OnScanStarted(const std::string& extension_id) {
309   MediaGalleries::ScanProgressDetails details;
310   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_START;
311   DispatchEventToExtension(
312       extension_id,
313       MediaGalleries::OnScanProgress::kEventName,
314       MediaGalleries::OnScanProgress::Create(details).Pass());
315 }
316
317 void MediaGalleriesEventRouter::OnScanCancelled(
318     const std::string& extension_id) {
319   MediaGalleries::ScanProgressDetails details;
320   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_CANCEL;
321   DispatchEventToExtension(
322       extension_id,
323       MediaGalleries::OnScanProgress::kEventName,
324       MediaGalleries::OnScanProgress::Create(details).Pass());
325 }
326
327 void MediaGalleriesEventRouter::OnScanFinished(
328     const std::string& extension_id, int gallery_count,
329     const MediaGalleryScanResult& file_counts) {
330   media_galleries::UsageCount(media_galleries::SCAN_FINISHED);
331   MediaGalleries::ScanProgressDetails details;
332   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_FINISH;
333   details.gallery_count.reset(new int(gallery_count));
334   details.audio_count.reset(new int(file_counts.audio_count));
335   details.image_count.reset(new int(file_counts.image_count));
336   details.video_count.reset(new int(file_counts.video_count));
337   DispatchEventToExtension(
338       extension_id,
339       MediaGalleries::OnScanProgress::kEventName,
340       MediaGalleries::OnScanProgress::Create(details).Pass());
341 }
342
343 void MediaGalleriesEventRouter::OnScanError(
344     const std::string& extension_id) {
345   MediaGalleries::ScanProgressDetails details;
346   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_ERROR;
347   DispatchEventToExtension(
348       extension_id,
349       MediaGalleries::OnScanProgress::kEventName,
350       MediaGalleries::OnScanProgress::Create(details).Pass());
351 }
352
353 void MediaGalleriesEventRouter::DispatchEventToExtension(
354     const std::string& extension_id,
355     const std::string& event_name,
356     scoped_ptr<base::ListValue> event_args) {
357   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
358   EventRouter* router = EventRouter::Get(profile_);
359   if (!router->ExtensionHasEventListener(extension_id, event_name))
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::RunAsync() {
371   media_galleries::UsageCount(media_galleries::GET_MEDIA_FILE_SYSTEMS);
372   scoped_ptr<GetMediaFileSystems::Params> params(
373       GetMediaFileSystems::Params::Create(*args_));
374   EXTENSION_FUNCTION_VALIDATE(params.get());
375   MediaGalleries::GetMediaFileSystemsInteractivity interactive =
376       MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO;
377   if (params->details.get() && params->details->interactive != MediaGalleries::
378          GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE) {
379     interactive = params->details->interactive;
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::RunAsync() {
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::RunAsync() {
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 MediaGalleriesDropPermissionForMediaFileSystemFunction::
647     ~MediaGalleriesDropPermissionForMediaFileSystemFunction() {}
648
649 bool MediaGalleriesDropPermissionForMediaFileSystemFunction::RunAsync() {
650   media_galleries::UsageCount(
651       media_galleries::DROP_PERMISSION_FOR_MEDIA_FILE_SYSTEM);
652
653   scoped_ptr<DropPermissionForMediaFileSystem::Params> params(
654       DropPermissionForMediaFileSystem::Params::Create(*args_));
655   EXTENSION_FUNCTION_VALIDATE(params.get());
656   MediaGalleryPrefId pref_id;
657   if (!base::StringToUint64(params->gallery_id, &pref_id)) {
658     error_ = kInvalidGalleryId;
659     return false;
660   }
661
662   base::Closure callback = base::Bind(
663       &MediaGalleriesDropPermissionForMediaFileSystemFunction::
664           OnPreferencesInit,
665       this,
666       pref_id);
667   return Setup(GetProfile(), &error_, callback);
668 }
669
670 void MediaGalleriesDropPermissionForMediaFileSystemFunction::OnPreferencesInit(
671     MediaGalleryPrefId pref_id) {
672   MediaGalleriesPreferences* preferences =
673       media_file_system_registry()->GetPreferences(GetProfile());
674   if (!ContainsKey(preferences->known_galleries(), pref_id)) {
675     error_ = kNonExistentGalleryId;
676     SendResponse(false);
677     return;
678   }
679
680   bool dropped = preferences->SetGalleryPermissionForExtension(
681       *GetExtension(), pref_id, false);
682   if (dropped)
683     SetResult(new base::StringValue(base::Uint64ToString(pref_id)));
684   else
685     error_ = kFailedToSetGalleryPermission;
686   SendResponse(dropped);
687 }
688
689 MediaGalleriesStartMediaScanFunction::~MediaGalleriesStartMediaScanFunction() {}
690
691 bool MediaGalleriesStartMediaScanFunction::RunAsync() {
692   media_galleries::UsageCount(media_galleries::START_MEDIA_SCAN);
693   if (!CheckScanPermission(GetExtension(), &error_)) {
694     MediaGalleriesEventRouter::Get(GetProfile())->OnScanError(
695         GetExtension()->id());
696     return false;
697   }
698   return Setup(GetProfile(), &error_, base::Bind(
699       &MediaGalleriesStartMediaScanFunction::OnPreferencesInit, this));
700 }
701
702 void MediaGalleriesStartMediaScanFunction::OnPreferencesInit() {
703   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
704   MediaGalleriesEventRouter* api = MediaGalleriesEventRouter::Get(GetProfile());
705   if (!api->ExtensionHasScanProgressListener(GetExtension()->id())) {
706     error_ = kMissingEventListener;
707     SendResponse(false);
708     return;
709   }
710
711   media_scan_manager()->StartScan(GetProfile(), GetExtension(), user_gesture());
712   SendResponse(true);
713 }
714
715 MediaGalleriesCancelMediaScanFunction::
716     ~MediaGalleriesCancelMediaScanFunction() {
717 }
718
719 bool MediaGalleriesCancelMediaScanFunction::RunAsync() {
720   media_galleries::UsageCount(media_galleries::CANCEL_MEDIA_SCAN);
721   if (!CheckScanPermission(GetExtension(), &error_)) {
722     MediaGalleriesEventRouter::Get(GetProfile())->OnScanError(
723         GetExtension()->id());
724     return false;
725   }
726   return Setup(GetProfile(), &error_, base::Bind(
727       &MediaGalleriesCancelMediaScanFunction::OnPreferencesInit, this));
728 }
729
730 void MediaGalleriesCancelMediaScanFunction::OnPreferencesInit() {
731   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
732   media_scan_manager()->CancelScan(GetProfile(), GetExtension());
733   SendResponse(true);
734 }
735
736 MediaGalleriesAddScanResultsFunction::~MediaGalleriesAddScanResultsFunction() {}
737
738 bool MediaGalleriesAddScanResultsFunction::RunAsync() {
739   media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS);
740   if (!CheckScanPermission(GetExtension(), &error_)) {
741     // We don't fire a scan progress error here, as it would be unintuitive.
742     return false;
743   }
744   if (!user_gesture())
745     return false;
746
747   return Setup(GetProfile(), &error_, base::Bind(
748       &MediaGalleriesAddScanResultsFunction::OnPreferencesInit, this));
749 }
750
751 MediaGalleriesScanResultDialogController*
752 MediaGalleriesAddScanResultsFunction::MakeDialog(
753     content::WebContents* web_contents,
754     const extensions::Extension& extension,
755     const base::Closure& on_finish) {
756   // Controller will delete itself.
757   return new MediaGalleriesScanResultDialogController(web_contents, extension,
758                                                       on_finish);
759 }
760
761 void MediaGalleriesAddScanResultsFunction::OnPreferencesInit() {
762   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
763   const Extension* extension = GetExtension();
764   MediaGalleriesPreferences* preferences =
765       media_file_system_registry()->GetPreferences(GetProfile());
766   if (MediaGalleriesScanResultDialogController::ScanResultCountForExtension(
767           preferences, extension) == 0) {
768     GetAndReturnGalleries();
769     return;
770   }
771
772   WebContents* contents =
773       GetWebContents(render_view_host(), GetProfile(), extension->id());
774   if (!contents) {
775     SendResponse(false);
776     return;
777   }
778
779   base::Closure cb = base::Bind(
780       &MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries, this);
781   MakeDialog(contents, *extension, cb);
782 }
783
784 void MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries() {
785   if (!render_view_host()) {
786     ReturnGalleries(std::vector<MediaFileSystemInfo>());
787     return;
788   }
789   MediaFileSystemRegistry* registry = media_file_system_registry();
790   DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
791   registry->GetMediaFileSystemsForExtension(
792       render_view_host(), GetExtension(),
793       base::Bind(&MediaGalleriesAddScanResultsFunction::ReturnGalleries,
794                  this));
795 }
796
797 void MediaGalleriesAddScanResultsFunction::ReturnGalleries(
798     const std::vector<MediaFileSystemInfo>& filesystems) {
799   scoped_ptr<base::ListValue> list(
800       ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
801   if (!list.get()) {
802     SendResponse(false);
803     return;
804   }
805
806   // The custom JS binding will use this list to create DOMFileSystem objects.
807   SetResult(list.release());
808   SendResponse(true);
809 }
810
811 MediaGalleriesGetMetadataFunction::~MediaGalleriesGetMetadataFunction() {}
812
813 bool MediaGalleriesGetMetadataFunction::RunAsync() {
814   std::string blob_uuid;
815   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &blob_uuid));
816
817   const base::Value* options_value = NULL;
818   if (!args_->Get(1, &options_value))
819     return false;
820   scoped_ptr<MediaGalleries::MediaMetadataOptions> options =
821       MediaGalleries::MediaMetadataOptions::FromValue(*options_value);
822   if (!options)
823     return false;
824
825   bool mime_type_only = options->metadata_type ==
826       MediaGalleries::GET_METADATA_TYPE_MIMETYPEONLY;
827
828   return Setup(GetProfile(), &error_, base::Bind(
829       &MediaGalleriesGetMetadataFunction::OnPreferencesInit, this,
830       mime_type_only, blob_uuid));
831 }
832
833 void MediaGalleriesGetMetadataFunction::OnPreferencesInit(
834     bool mime_type_only, const std::string& blob_uuid) {
835   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
836
837   // BlobReader is self-deleting.
838   BlobReader* reader = new BlobReader(
839       GetProfile(),
840       blob_uuid,
841       base::Bind(&MediaGalleriesGetMetadataFunction::SniffMimeType, this,
842                  mime_type_only, blob_uuid));
843   reader->SetByteRange(0, net::kMaxBytesToSniff);
844   reader->Start();
845 }
846
847 void MediaGalleriesGetMetadataFunction::SniffMimeType(
848     bool mime_type_only, const std::string& blob_uuid,
849     scoped_ptr<std::string> blob_header, int64 total_blob_length) {
850   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
851
852   std::string mime_type;
853   bool mime_type_sniffed = net::SniffMimeTypeFromLocalData(
854       blob_header->c_str(), blob_header->size(), &mime_type);
855
856   if (!mime_type_sniffed) {
857     SendResponse(false);
858     return;
859   }
860
861   if (mime_type_only) {
862     MediaGalleries::MediaMetadata metadata;
863     metadata.mime_type = mime_type;
864     SetResult(metadata.ToValue().release());
865     SendResponse(true);
866     return;
867   }
868
869   scoped_refptr<metadata::SafeMediaMetadataParser> parser(
870       new metadata::SafeMediaMetadataParser(GetProfile(), blob_uuid,
871                                             total_blob_length, mime_type));
872   parser->Start(base::Bind(
873       &MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone, this));
874 }
875
876 void MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone(
877     bool parse_success, base::DictionaryValue* metadata_dictionary) {
878   if (!parse_success) {
879     SendResponse(false);
880     return;
881   }
882
883   SetResult(metadata_dictionary->DeepCopy());
884   SendResponse(true);
885 }
886
887 }  // namespace extensions