Upstream version 5.34.104.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/lazy_instance.h"
16 #include "base/platform_file.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/values.h"
21 #include "chrome/browser/browser_process.h"
22 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
23 #include "chrome/browser/extensions/blob_reader.h"
24 #include "chrome/browser/extensions/extension_tab_util.h"
25 #include "chrome/browser/media_galleries/media_file_system_registry.h"
26 #include "chrome/browser/media_galleries/media_galleries_dialog_controller.h"
27 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
28 #include "chrome/browser/media_galleries/media_galleries_preferences.h"
29 #include "chrome/browser/media_galleries/media_galleries_scan_result_dialog_controller.h"
30 #include "chrome/browser/media_galleries/media_scan_manager.h"
31 #include "chrome/browser/platform_util.h"
32 #include "chrome/browser/profiles/profile.h"
33 #include "chrome/browser/ui/chrome_select_file_policy.h"
34 #include "chrome/common/extensions/api/media_galleries.h"
35 #include "chrome/common/extensions/permissions/media_galleries_permission.h"
36 #include "chrome/common/pref_names.h"
37 #include "components/storage_monitor/storage_info.h"
38 #include "components/web_modal/web_contents_modal_dialog_manager.h"
39 #include "content/public/browser/browser_thread.h"
40 #include "content/public/browser/child_process_security_policy.h"
41 #include "content/public/browser/render_process_host.h"
42 #include "content/public/browser/render_view_host.h"
43 #include "content/public/browser/web_contents.h"
44 #include "content/public/browser/web_contents_view.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/permissions_data.h"
51 #include "grit/generated_resources.h"
52 #include "net/base/mime_sniffer.h"
53 #include "ui/base/l10n/l10n_util.h"
54
55 using content::WebContents;
56 using web_modal::WebContentsModalDialogManager;
57
58 namespace extensions {
59
60 namespace MediaGalleries = api::media_galleries;
61 namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems;
62
63 namespace {
64
65 const char kDisallowedByPolicy[] =
66     "Media Galleries API is disallowed by policy: ";
67 const char kMissingEventListener[] =
68     "Missing event listener registration.";
69 const char kNoScanPermission[] =
70     "No permission to scan.";
71
72 const char kDeviceIdKey[] = "deviceId";
73 const char kGalleryIdKey[] = "galleryId";
74 const char kIsAvailableKey[] = "isAvailable";
75 const char kIsMediaDeviceKey[] = "isMediaDevice";
76 const char kIsRemovableKey[] = "isRemovable";
77 const char kNameKey[] = "name";
78
79 MediaFileSystemRegistry* media_file_system_registry() {
80   return g_browser_process->media_file_system_registry();
81 }
82
83 MediaScanManager* media_scan_manager() {
84   return media_file_system_registry()->media_scan_manager();
85 }
86
87 // Checks whether the MediaGalleries API is currently accessible (it may be
88 // disallowed even if an extension has the requisite permission). Then
89 // initializes the MediaGalleriesPreferences
90 bool Setup(Profile* profile, std::string* error, base::Closure callback) {
91   if (!ChromeSelectFilePolicy::FileSelectDialogsAllowed()) {
92     *error = std::string(kDisallowedByPolicy) +
93         prefs::kAllowFileSelectionDialogs;
94     return false;
95   }
96
97   MediaGalleriesPreferences* preferences =
98       media_file_system_registry()->GetPreferences(profile);
99   preferences->EnsureInitialized(callback);
100   return true;
101 }
102
103 WebContents* GetWebContents(content::RenderViewHost* rvh,
104                             Profile* profile,
105                             const std::string& app_id) {
106   WebContents* contents = WebContents::FromRenderViewHost(rvh);
107   WebContentsModalDialogManager* web_contents_modal_dialog_manager =
108       WebContentsModalDialogManager::FromWebContents(contents);
109   if (!web_contents_modal_dialog_manager) {
110     // If there is no WebContentsModalDialogManager, then this contents is
111     // probably the background page for an app. Try to find a app window to
112     // host the dialog.
113     apps::AppWindow* window = apps::AppWindowRegistry::Get(profile)
114                                   ->GetCurrentAppWindowForApp(app_id);
115     contents = window ? window->web_contents() : NULL;
116   }
117   return contents;
118 }
119
120 base::ListValue* ConstructFileSystemList(
121     content::RenderViewHost* rvh,
122     const Extension* extension,
123     const std::vector<MediaFileSystemInfo>& filesystems) {
124   if (!rvh)
125     return NULL;
126
127   MediaGalleriesPermission::CheckParam read_param(
128       MediaGalleriesPermission::kReadPermission);
129   bool has_read_permission = PermissionsData::CheckAPIPermissionWithParam(
130       extension, APIPermission::kMediaGalleries, &read_param);
131   MediaGalleriesPermission::CheckParam copy_to_param(
132       MediaGalleriesPermission::kCopyToPermission);
133   bool has_copy_to_permission = PermissionsData::CheckAPIPermissionWithParam(
134       extension, APIPermission::kMediaGalleries, &copy_to_param);
135   MediaGalleriesPermission::CheckParam delete_param(
136       MediaGalleriesPermission::kDeletePermission);
137   bool has_delete_permission = PermissionsData::CheckAPIPermissionWithParam(
138       extension, APIPermission::kMediaGalleries, &delete_param);
139
140   const int child_id = rvh->GetProcess()->GetID();
141   scoped_ptr<base::ListValue> list(new base::ListValue());
142   for (size_t i = 0; i < filesystems.size(); ++i) {
143     scoped_ptr<base::DictionaryValue> file_system_dict_value(
144         new base::DictionaryValue());
145
146     // Send the file system id so the renderer can create a valid FileSystem
147     // object.
148     file_system_dict_value->SetStringWithoutPathExpansion(
149         "fsid", filesystems[i].fsid);
150
151     file_system_dict_value->SetStringWithoutPathExpansion(
152         kNameKey, filesystems[i].name);
153     file_system_dict_value->SetStringWithoutPathExpansion(
154         kGalleryIdKey,
155         base::Uint64ToString(filesystems[i].pref_id));
156     if (!filesystems[i].transient_device_id.empty()) {
157       file_system_dict_value->SetStringWithoutPathExpansion(
158           kDeviceIdKey, filesystems[i].transient_device_id);
159     }
160     file_system_dict_value->SetBooleanWithoutPathExpansion(
161         kIsRemovableKey, filesystems[i].removable);
162     file_system_dict_value->SetBooleanWithoutPathExpansion(
163         kIsMediaDeviceKey, filesystems[i].media_device);
164     file_system_dict_value->SetBooleanWithoutPathExpansion(
165         kIsAvailableKey, true);
166
167     list->Append(file_system_dict_value.release());
168
169     if (filesystems[i].path.empty())
170       continue;
171
172     if (has_read_permission) {
173       content::ChildProcessSecurityPolicy* policy =
174           content::ChildProcessSecurityPolicy::GetInstance();
175       policy->GrantReadFileSystem(child_id, filesystems[i].fsid);
176       if (has_delete_permission) {
177         policy->GrantDeleteFromFileSystem(child_id, filesystems[i].fsid);
178         if (has_copy_to_permission) {
179           policy->GrantCopyIntoFileSystem(child_id, filesystems[i].fsid);
180         }
181       }
182     }
183   }
184
185   return list.release();
186 }
187
188 bool CheckScanPermission(const extensions::Extension* extension,
189                          std::string* error) {
190   DCHECK(extension);
191   DCHECK(error);
192   MediaGalleriesPermission::CheckParam scan_param(
193       MediaGalleriesPermission::kScanPermission);
194   bool has_scan_permission = PermissionsData::CheckAPIPermissionWithParam(
195       extension, APIPermission::kMediaGalleries, &scan_param);
196   if (!has_scan_permission)
197     *error = kNoScanPermission;
198   return has_scan_permission;
199 }
200
201 class SelectDirectoryDialog : public ui::SelectFileDialog::Listener,
202                               public base::RefCounted<SelectDirectoryDialog> {
203  public:
204   // Selected file path, or an empty path if the user canceled.
205   typedef base::Callback<void(const base::FilePath&)> Callback;
206
207   SelectDirectoryDialog(WebContents* web_contents, const Callback& callback)
208       : web_contents_(web_contents),
209         callback_(callback) {
210     select_file_dialog_ = ui::SelectFileDialog::Create(
211         this, new ChromeSelectFilePolicy(web_contents));
212   }
213
214   void Show(const base::FilePath& default_path) {
215     AddRef();  // Balanced in the two reachable listener outcomes.
216     select_file_dialog_->SelectFile(
217       ui::SelectFileDialog::SELECT_FOLDER,
218       l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY_TITLE),
219       default_path,
220       NULL,
221       0,
222       base::FilePath::StringType(),
223       platform_util::GetTopLevel(web_contents_->GetView()->GetNativeView()),
224       NULL);
225   }
226
227   // ui::SelectFileDialog::Listener implementation.
228   virtual void FileSelected(const base::FilePath& path,
229                             int index,
230                             void* params) OVERRIDE {
231     callback_.Run(path);
232     Release();  // Balanced in Show().
233   }
234
235   virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
236                                   void* params) OVERRIDE {
237     NOTREACHED() << "Should not be able to select multiple files";
238   }
239
240   virtual void FileSelectionCanceled(void* params) OVERRIDE {
241     callback_.Run(base::FilePath());
242     Release();  // Balanced in Show().
243   }
244
245  private:
246   friend class base::RefCounted<SelectDirectoryDialog>;
247   virtual ~SelectDirectoryDialog() {}
248
249   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
250   WebContents* web_contents_;
251   Callback callback_;
252
253   DISALLOW_COPY_AND_ASSIGN(SelectDirectoryDialog);
254 };
255
256 }  // namespace
257
258 MediaGalleriesEventRouter::MediaGalleriesEventRouter(Profile* profile)
259     : profile_(profile),
260       weak_ptr_factory_(this) {
261   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
262   DCHECK(profile_);
263   media_scan_manager()->AddObserver(profile_, this);
264 }
265
266 MediaGalleriesEventRouter::~MediaGalleriesEventRouter() {
267 }
268
269 void MediaGalleriesEventRouter::Shutdown() {
270   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
271   weak_ptr_factory_.InvalidateWeakPtrs();
272   media_scan_manager()->RemoveObserver(profile_);
273   media_scan_manager()->CancelScansForProfile(profile_);
274 }
275
276 static base::LazyInstance<ProfileKeyedAPIFactory<MediaGalleriesEventRouter> >
277 g_factory = LAZY_INSTANCE_INITIALIZER;
278
279 // static
280 ProfileKeyedAPIFactory<MediaGalleriesEventRouter>*
281 MediaGalleriesEventRouter::GetFactoryInstance() {
282   return g_factory.Pointer();
283 }
284
285 // static
286 MediaGalleriesEventRouter* MediaGalleriesEventRouter::Get(Profile* profile) {
287   DCHECK(media_file_system_registry()->GetPreferences(profile)->
288              IsInitialized());
289   return ProfileKeyedAPIFactory<MediaGalleriesEventRouter>::GetForProfile(
290       profile);
291 }
292
293 bool MediaGalleriesEventRouter::ExtensionHasScanProgressListener(
294     const std::string& extension_id) const {
295   EventRouter* router = ExtensionSystem::Get(profile_)->event_router();
296   return router->ExtensionHasEventListener(
297       extension_id,
298       MediaGalleries::OnScanProgress::kEventName);
299 }
300
301 void MediaGalleriesEventRouter::OnScanStarted(const std::string& extension_id) {
302   MediaGalleries::ScanProgressDetails details;
303   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_START;
304   DispatchEventToExtension(
305       extension_id,
306       MediaGalleries::OnScanProgress::kEventName,
307       MediaGalleries::OnScanProgress::Create(details).Pass());
308 }
309
310 void MediaGalleriesEventRouter::OnScanCancelled(
311     const std::string& extension_id) {
312   MediaGalleries::ScanProgressDetails details;
313   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_CANCEL;
314   DispatchEventToExtension(
315       extension_id,
316       MediaGalleries::OnScanProgress::kEventName,
317       MediaGalleries::OnScanProgress::Create(details).Pass());
318 }
319
320 void MediaGalleriesEventRouter::OnScanFinished(
321     const std::string& extension_id, int gallery_count,
322     const MediaGalleryScanResult& file_counts) {
323   MediaGalleries::ScanProgressDetails details;
324   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_FINISH;
325   details.gallery_count.reset(new int(gallery_count));
326   details.audio_count.reset(new int(file_counts.audio_count));
327   details.image_count.reset(new int(file_counts.image_count));
328   details.video_count.reset(new int(file_counts.video_count));
329   DispatchEventToExtension(
330       extension_id,
331       MediaGalleries::OnScanProgress::kEventName,
332       MediaGalleries::OnScanProgress::Create(details).Pass());
333 }
334
335 void MediaGalleriesEventRouter::OnScanError(
336     const std::string& extension_id) {
337   MediaGalleries::ScanProgressDetails details;
338   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_ERROR;
339   DispatchEventToExtension(
340       extension_id,
341       MediaGalleries::OnScanProgress::kEventName,
342       MediaGalleries::OnScanProgress::Create(details).Pass());
343 }
344
345 void MediaGalleriesEventRouter::DispatchEventToExtension(
346     const std::string& extension_id,
347     const std::string& event_name,
348     scoped_ptr<base::ListValue> event_args) {
349   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
350   EventRouter* router =
351       extensions::ExtensionSystem::Get(profile_)->event_router();
352   if (!router->ExtensionHasEventListener(extension_id, event_name))
353     return;
354
355   scoped_ptr<extensions::Event> event(
356       new extensions::Event(event_name, event_args.Pass()));
357   router->DispatchEventToExtension(extension_id, event.Pass());
358 }
359
360 MediaGalleriesGetMediaFileSystemsFunction::
361     ~MediaGalleriesGetMediaFileSystemsFunction() {}
362
363 bool MediaGalleriesGetMediaFileSystemsFunction::RunImpl() {
364   media_galleries::UsageCount(media_galleries::GET_MEDIA_FILE_SYSTEMS);
365   scoped_ptr<GetMediaFileSystems::Params> params(
366       GetMediaFileSystems::Params::Create(*args_));
367   EXTENSION_FUNCTION_VALIDATE(params.get());
368   MediaGalleries::GetMediaFileSystemsInteractivity interactive =
369       MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO;
370   if (params->details.get() && params->details->interactive != MediaGalleries::
371          GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE) {
372     interactive = params->details->interactive;
373   }
374
375   return Setup(GetProfile(), &error_, base::Bind(
376       &MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit, this,
377       interactive));
378 }
379
380 void MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit(
381     MediaGalleries::GetMediaFileSystemsInteractivity interactive) {
382   switch (interactive) {
383     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_YES: {
384       // The MediaFileSystemRegistry only updates preferences for extensions
385       // that it knows are in use. Since this may be the first call to
386       // chrome.getMediaFileSystems for this extension, call
387       // GetMediaFileSystemsForExtension() here solely so that
388       // MediaFileSystemRegistry will send preference changes.
389       GetMediaFileSystemsForExtension(base::Bind(
390           &MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog, this));
391       return;
392     }
393     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_IF_NEEDED: {
394       GetMediaFileSystemsForExtension(base::Bind(
395           &MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries,
396           this));
397       return;
398     }
399     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO:
400       GetAndReturnGalleries();
401       return;
402     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE:
403       NOTREACHED();
404   }
405   SendResponse(false);
406 }
407
408 void MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog(
409     const std::vector<MediaFileSystemInfo>& /*filesystems*/) {
410   ShowDialog();
411 }
412
413 void MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries(
414     const std::vector<MediaFileSystemInfo>& filesystems) {
415   if (filesystems.empty())
416     ShowDialog();
417   else
418     ReturnGalleries(filesystems);
419 }
420
421 void MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries() {
422   GetMediaFileSystemsForExtension(base::Bind(
423       &MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries, this));
424 }
425
426 void MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries(
427     const std::vector<MediaFileSystemInfo>& filesystems) {
428   scoped_ptr<base::ListValue> list(
429       ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
430   if (!list.get()) {
431     SendResponse(false);
432     return;
433   }
434
435   // The custom JS binding will use this list to create DOMFileSystem objects.
436   SetResult(list.release());
437   SendResponse(true);
438 }
439
440 void MediaGalleriesGetMediaFileSystemsFunction::ShowDialog() {
441   media_galleries::UsageCount(media_galleries::SHOW_DIALOG);
442   const Extension* extension = GetExtension();
443   WebContents* contents =
444       GetWebContents(render_view_host(), GetProfile(), extension->id());
445   if (!contents) {
446     SendResponse(false);
447     return;
448   }
449
450   // Controller will delete itself.
451   base::Closure cb = base::Bind(
452       &MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries, this);
453   new MediaGalleriesDialogController(contents, *extension, cb);
454 }
455
456 void MediaGalleriesGetMediaFileSystemsFunction::GetMediaFileSystemsForExtension(
457     const MediaFileSystemsCallback& cb) {
458   if (!render_view_host()) {
459     cb.Run(std::vector<MediaFileSystemInfo>());
460     return;
461   }
462   MediaFileSystemRegistry* registry = media_file_system_registry();
463   DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
464   registry->GetMediaFileSystemsForExtension(
465       render_view_host(), GetExtension(), cb);
466 }
467
468 MediaGalleriesGetAllMediaFileSystemMetadataFunction::
469     ~MediaGalleriesGetAllMediaFileSystemMetadataFunction() {}
470
471 bool MediaGalleriesGetAllMediaFileSystemMetadataFunction::RunImpl() {
472   media_galleries::UsageCount(
473       media_galleries::GET_ALL_MEDIA_FILE_SYSTEM_METADATA);
474   return Setup(GetProfile(), &error_, base::Bind(
475       &MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnPreferencesInit,
476       this));
477 }
478
479 void MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnPreferencesInit() {
480   MediaFileSystemRegistry* registry = media_file_system_registry();
481   MediaGalleriesPreferences* prefs = registry->GetPreferences(GetProfile());
482   DCHECK(prefs->IsInitialized());
483   MediaGalleryPrefIdSet permitted_gallery_ids =
484       prefs->GalleriesForExtension(*GetExtension());
485
486   MediaStorageUtil::DeviceIdSet* device_ids = new MediaStorageUtil::DeviceIdSet;
487   const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
488   for (MediaGalleryPrefIdSet::const_iterator it = permitted_gallery_ids.begin();
489        it != permitted_gallery_ids.end(); ++it) {
490     MediaGalleriesPrefInfoMap::const_iterator gallery_it = galleries.find(*it);
491     DCHECK(gallery_it != galleries.end());
492     device_ids->insert(gallery_it->second.device_id);
493   }
494
495   MediaStorageUtil::FilterAttachedDevices(
496       device_ids,
497       base::Bind(
498           &MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries,
499           this,
500           permitted_gallery_ids,
501           base::Owned(device_ids)));
502 }
503
504 void MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries(
505     const MediaGalleryPrefIdSet& permitted_gallery_ids,
506     const MediaStorageUtil::DeviceIdSet* available_devices) {
507   MediaFileSystemRegistry* registry = media_file_system_registry();
508   MediaGalleriesPreferences* prefs = registry->GetPreferences(GetProfile());
509
510   base::ListValue* list = new base::ListValue();
511   const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
512   for (MediaGalleryPrefIdSet::const_iterator it = permitted_gallery_ids.begin();
513        it != permitted_gallery_ids.end(); ++it) {
514     MediaGalleriesPrefInfoMap::const_iterator gallery_it = galleries.find(*it);
515     DCHECK(gallery_it != galleries.end());
516     const MediaGalleryPrefInfo& gallery = gallery_it->second;
517     MediaGalleries::MediaFileSystemMetadata metadata;
518     metadata.name = base::UTF16ToUTF8(gallery.GetGalleryDisplayName());
519     metadata.gallery_id = base::Uint64ToString(gallery.pref_id);
520     metadata.is_removable = StorageInfo::IsRemovableDevice(gallery.device_id);
521     metadata.is_media_device = StorageInfo::IsMediaDevice(gallery.device_id);
522     metadata.is_available = ContainsKey(*available_devices, gallery.device_id);
523     list->Append(metadata.ToValue().release());
524   }
525
526   SetResult(list);
527   SendResponse(true);
528 }
529
530 MediaGalleriesAddUserSelectedFolderFunction::
531     ~MediaGalleriesAddUserSelectedFolderFunction() {}
532
533 bool MediaGalleriesAddUserSelectedFolderFunction::RunImpl() {
534   media_galleries::UsageCount(media_galleries::ADD_USER_SELECTED_FOLDER);
535   return Setup(GetProfile(), &error_, base::Bind(
536       &MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit, this));
537 }
538
539 void MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit() {
540   Profile* profile = GetProfile();
541   const std::string& app_id = GetExtension()->id();
542   WebContents* contents = GetWebContents(render_view_host(), profile, app_id);
543   if (!contents) {
544     // When the request originated from a background page, but there is no app
545     // window open, check to see if it originated from a tab and display the
546     // dialog in that tab.
547     bool found_tab = extensions::ExtensionTabUtil::GetTabById(
548         source_tab_id(), profile, profile->IsOffTheRecord(),
549         NULL, NULL, &contents, NULL);
550     if (!found_tab || !contents) {
551       SendResponse(false);
552       return;
553     }
554   }
555
556   if (!user_gesture()) {
557     OnDirectorySelected(base::FilePath());
558     return;
559   }
560
561   base::FilePath last_used_path =
562       extensions::file_system_api::GetLastChooseEntryDirectory(
563           extensions::ExtensionPrefs::Get(profile), app_id);
564   SelectDirectoryDialog::Callback callback = base::Bind(
565       &MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected, this);
566   scoped_refptr<SelectDirectoryDialog> select_directory_dialog =
567       new SelectDirectoryDialog(contents, callback);
568   select_directory_dialog->Show(last_used_path);
569 }
570
571 void MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected(
572     const base::FilePath& selected_directory) {
573   if (selected_directory.empty()) {
574     // User cancelled case.
575     GetMediaFileSystemsForExtension(base::Bind(
576         &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
577         this,
578         kInvalidMediaGalleryPrefId));
579     return;
580   }
581
582   extensions::file_system_api::SetLastChooseEntryDirectory(
583       extensions::ExtensionPrefs::Get(GetProfile()),
584       GetExtension()->id(),
585       selected_directory);
586
587   MediaGalleriesPreferences* preferences =
588       media_file_system_registry()->GetPreferences(GetProfile());
589   MediaGalleryPrefId pref_id =
590       preferences->AddGalleryByPath(selected_directory,
591                                     MediaGalleryPrefInfo::kUserAdded);
592   preferences->SetGalleryPermissionForExtension(*GetExtension(), pref_id, true);
593
594   GetMediaFileSystemsForExtension(base::Bind(
595       &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
596       this,
597       pref_id));
598 }
599
600 void MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId(
601     MediaGalleryPrefId pref_id,
602     const std::vector<MediaFileSystemInfo>& filesystems) {
603   scoped_ptr<base::ListValue> list(
604       ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
605   if (!list.get()) {
606     SendResponse(false);
607     return;
608   }
609
610   int index = -1;
611   if (pref_id != kInvalidMediaGalleryPrefId) {
612     for (size_t i = 0; i < filesystems.size(); ++i) {
613       if (filesystems[i].pref_id == pref_id) {
614         index = i;
615         break;
616       }
617     }
618   }
619   base::DictionaryValue* results = new base::DictionaryValue;
620   results->SetWithoutPathExpansion("mediaFileSystems", list.release());
621   results->SetIntegerWithoutPathExpansion("selectedFileSystemIndex", index);
622   SetResult(results);
623   SendResponse(true);
624 }
625
626 void
627 MediaGalleriesAddUserSelectedFolderFunction::GetMediaFileSystemsForExtension(
628     const MediaFileSystemsCallback& cb) {
629   if (!render_view_host()) {
630     cb.Run(std::vector<MediaFileSystemInfo>());
631     return;
632   }
633   MediaFileSystemRegistry* registry = media_file_system_registry();
634   DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
635   registry->GetMediaFileSystemsForExtension(
636       render_view_host(), GetExtension(), cb);
637 }
638
639 MediaGalleriesStartMediaScanFunction::~MediaGalleriesStartMediaScanFunction() {}
640
641 bool MediaGalleriesStartMediaScanFunction::RunImpl() {
642   media_galleries::UsageCount(media_galleries::START_MEDIA_SCAN);
643   if (!CheckScanPermission(GetExtension(), &error_)) {
644     MediaGalleriesEventRouter::Get(GetProfile())->OnScanError(
645         GetExtension()->id());
646     return false;
647   }
648   return Setup(GetProfile(), &error_, base::Bind(
649       &MediaGalleriesStartMediaScanFunction::OnPreferencesInit, this));
650 }
651
652 void MediaGalleriesStartMediaScanFunction::OnPreferencesInit() {
653   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
654   MediaGalleriesEventRouter* api = MediaGalleriesEventRouter::Get(GetProfile());
655   if (!api->ExtensionHasScanProgressListener(GetExtension()->id())) {
656     error_ = kMissingEventListener;
657     SendResponse(false);
658     return;
659   }
660
661   media_scan_manager()->StartScan(GetProfile(), GetExtension(), user_gesture());
662   SendResponse(true);
663 }
664
665 MediaGalleriesCancelMediaScanFunction::
666     ~MediaGalleriesCancelMediaScanFunction() {
667 }
668
669 bool MediaGalleriesCancelMediaScanFunction::RunImpl() {
670   media_galleries::UsageCount(media_galleries::CANCEL_MEDIA_SCAN);
671   if (!CheckScanPermission(GetExtension(), &error_)) {
672     MediaGalleriesEventRouter::Get(GetProfile())->OnScanError(
673         GetExtension()->id());
674     return false;
675   }
676   return Setup(GetProfile(), &error_, base::Bind(
677       &MediaGalleriesCancelMediaScanFunction::OnPreferencesInit, this));
678 }
679
680 void MediaGalleriesCancelMediaScanFunction::OnPreferencesInit() {
681   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
682   media_scan_manager()->CancelScan(GetProfile(), GetExtension());
683   SendResponse(true);
684 }
685
686 MediaGalleriesAddScanResultsFunction::~MediaGalleriesAddScanResultsFunction() {}
687
688 bool MediaGalleriesAddScanResultsFunction::RunImpl() {
689   media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS);
690   if (!CheckScanPermission(GetExtension(), &error_)) {
691     // We don't fire a scan progress error here, as it would be unintuitive.
692     return false;
693   }
694   if (!user_gesture())
695     return false;
696
697   return Setup(GetProfile(), &error_, base::Bind(
698       &MediaGalleriesAddScanResultsFunction::OnPreferencesInit, this));
699 }
700
701 void MediaGalleriesAddScanResultsFunction::OnPreferencesInit() {
702   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
703   const Extension* extension = GetExtension();
704   MediaGalleriesPreferences* preferences =
705       media_file_system_registry()->GetPreferences(GetProfile());
706   if (MediaGalleriesScanResultDialogController::ScanResultCountForExtension(
707           preferences, extension) == 0) {
708     GetAndReturnGalleries();
709     return;
710   }
711
712   WebContents* contents =
713       GetWebContents(render_view_host(), GetProfile(), extension->id());
714   if (!contents) {
715     SendResponse(false);
716     return;
717   }
718
719   // Controller will delete itself.
720   base::Closure cb = base::Bind(
721       &MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries, this);
722   new MediaGalleriesScanResultDialogController(contents, *extension, cb);
723 }
724
725 void MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries() {
726   if (!render_view_host()) {
727     ReturnGalleries(std::vector<MediaFileSystemInfo>());
728     return;
729   }
730   MediaFileSystemRegistry* registry = media_file_system_registry();
731   DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
732   registry->GetMediaFileSystemsForExtension(
733       render_view_host(), GetExtension(),
734       base::Bind(&MediaGalleriesAddScanResultsFunction::ReturnGalleries,
735                  this));
736 }
737
738 void MediaGalleriesAddScanResultsFunction::ReturnGalleries(
739     const std::vector<MediaFileSystemInfo>& filesystems) {
740   scoped_ptr<base::ListValue> list(
741       ConstructFileSystemList(render_view_host(), GetExtension(), filesystems));
742   if (!list.get()) {
743     SendResponse(false);
744     return;
745   }
746
747   // The custom JS binding will use this list to create DOMFileSystem objects.
748   SetResult(list.release());
749   SendResponse(true);
750 }
751
752 MediaGalleriesGetMetadataFunction::~MediaGalleriesGetMetadataFunction() {}
753
754 bool MediaGalleriesGetMetadataFunction::RunImpl() {
755   std::string blob_uuid;
756   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &blob_uuid));
757
758   const base::Value* options_value = NULL;
759   if (!args_->Get(1, &options_value))
760     return false;
761   scoped_ptr<MediaGalleries::MediaMetadataOptions> options =
762       MediaGalleries::MediaMetadataOptions::FromValue(*options_value);
763   if (!options)
764     return false;
765
766   bool mime_type_only = options->metadata_type ==
767       MediaGalleries::GET_METADATA_TYPE_MIMETYPEONLY;
768
769   return Setup(GetProfile(), &error_, base::Bind(
770       &MediaGalleriesGetMetadataFunction::OnPreferencesInit, this,
771       mime_type_only, blob_uuid));
772 }
773
774 void MediaGalleriesGetMetadataFunction::OnPreferencesInit(
775     bool mime_type_only, const std::string& blob_uuid) {
776   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
777
778   // BlobReader is self-deleting.
779   BlobReader* reader = new BlobReader(
780       GetProfile(),
781       blob_uuid,
782       base::Bind(&MediaGalleriesGetMetadataFunction::SniffMimeType, this,
783                  mime_type_only));
784   reader->SetByteRange(0, net::kMaxBytesToSniff);
785   reader->Start();
786 }
787
788 void MediaGalleriesGetMetadataFunction::SniffMimeType(
789     bool mime_type_only, scoped_ptr<std::string> blob_header,
790     int64 /* total_blob_length */) {
791   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
792
793   MediaGalleries::MediaMetadata metadata;
794
795   std::string mime_type;
796   bool mime_type_sniffed = net::SniffMimeTypeFromLocalData(
797       blob_header->c_str(), blob_header->size(), &mime_type);
798   if (mime_type_sniffed)
799     metadata.mime_type = mime_type;
800
801   // TODO(tommycli): Kick off SafeMediaMetadataParser if |mime_type_only| false.
802
803   SetResult(metadata.ToValue().release());
804   SendResponse(true);
805 }
806
807 }  // namespace extensions