Upstream version 10.39.225.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 "base/callback.h"
14 #include "base/lazy_instance.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/values.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/extensions/api/file_system/file_system_api.h"
22 #include "chrome/browser/extensions/blob_reader.h"
23 #include "chrome/browser/extensions/extension_tab_util.h"
24 #include "chrome/browser/media_galleries/fileapi/safe_media_metadata_parser.h"
25 #include "chrome/browser/media_galleries/gallery_watch_manager.h"
26 #include "chrome/browser/media_galleries/media_file_system_registry.h"
27 #include "chrome/browser/media_galleries/media_galleries_histograms.h"
28 #include "chrome/browser/media_galleries/media_galleries_permission_controller.h"
29 #include "chrome/browser/media_galleries/media_galleries_preferences.h"
30 #include "chrome/browser/media_galleries/media_galleries_scan_result_controller.h"
31 #include "chrome/browser/media_galleries/media_scan_manager.h"
32 #include "chrome/browser/platform_util.h"
33 #include "chrome/browser/profiles/profile.h"
34 #include "chrome/browser/ui/chrome_select_file_policy.h"
35 #include "chrome/common/extensions/api/media_galleries.h"
36 #include "chrome/common/pref_names.h"
37 #include "chrome/grit/generated_resources.h"
38 #include "components/storage_monitor/storage_info.h"
39 #include "components/web_modal/web_contents_modal_dialog_manager.h"
40 #include "content/public/browser/blob_handle.h"
41 #include "content/public/browser/browser_context.h"
42 #include "content/public/browser/browser_thread.h"
43 #include "content/public/browser/child_process_security_policy.h"
44 #include "content/public/browser/render_process_host.h"
45 #include "content/public/browser/render_view_host.h"
46 #include "content/public/browser/web_contents.h"
47 #include "extensions/browser/app_window/app_window.h"
48 #include "extensions/browser/app_window/app_window_registry.h"
49 #include "extensions/browser/blob_holder.h"
50 #include "extensions/browser/extension_prefs.h"
51 #include "extensions/browser/extension_system.h"
52 #include "extensions/common/extension.h"
53 #include "extensions/common/permissions/api_permission.h"
54 #include "extensions/common/permissions/media_galleries_permission.h"
55 #include "extensions/common/permissions/permissions_data.h"
56 #include "net/base/mime_sniffer.h"
57 #include "storage/browser/blob/blob_data_handle.h"
58 #include "ui/base/l10n/l10n_util.h"
59
60 using content::WebContents;
61 using storage_monitor::MediaStorageUtil;
62 using storage_monitor::StorageInfo;
63 using web_modal::WebContentsModalDialogManager;
64
65 namespace extensions {
66
67 namespace MediaGalleries = api::media_galleries;
68 namespace DropPermissionForMediaFileSystem =
69     MediaGalleries::DropPermissionForMediaFileSystem;
70 namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems;
71 namespace AddGalleryWatch = MediaGalleries::AddGalleryWatch;
72 namespace RemoveGalleryWatch = MediaGalleries::RemoveGalleryWatch;
73 namespace GetAllGalleryWatch = MediaGalleries::GetAllGalleryWatch;
74
75 namespace {
76
77 const char kDisallowedByPolicy[] =
78     "Media Galleries API is disallowed by policy: ";
79 const char kFailedToSetGalleryPermission[] =
80     "Failed to set gallery permission.";
81 const char kInvalidGalleryIdMsg[] = "Invalid gallery id.";
82 const char kMissingEventListener[] = "Missing event listener registration.";
83 const char kNonExistentGalleryId[] = "Non-existent gallery id.";
84 const char kNoScanPermission[] = "No permission to scan.";
85
86 const char kDeviceIdKey[] = "deviceId";
87 const char kGalleryIdKey[] = "galleryId";
88 const char kIsAvailableKey[] = "isAvailable";
89 const char kIsMediaDeviceKey[] = "isMediaDevice";
90 const char kIsRemovableKey[] = "isRemovable";
91 const char kNameKey[] = "name";
92
93 const char kMetadataKey[] = "metadata";
94 const char kAttachedImagesBlobInfoKey[] = "attachedImagesBlobInfo";
95 const char kBlobUUIDKey[] = "blobUUID";
96 const char kTypeKey[] = "type";
97 const char kSizeKey[] = "size";
98
99 const char kInvalidGalleryId[] = "-1";
100
101 MediaFileSystemRegistry* media_file_system_registry() {
102   return g_browser_process->media_file_system_registry();
103 }
104
105 GalleryWatchManager* gallery_watch_manager() {
106   return media_file_system_registry()->gallery_watch_manager();
107 }
108
109 MediaScanManager* media_scan_manager() {
110   return media_file_system_registry()->media_scan_manager();
111 }
112
113 // Checks whether the MediaGalleries API is currently accessible (it may be
114 // disallowed even if an extension has the requisite permission). Then
115 // initializes the MediaGalleriesPreferences
116 bool Setup(Profile* profile, std::string* error, base::Closure callback) {
117   if (!ChromeSelectFilePolicy::FileSelectDialogsAllowed()) {
118     *error = std::string(kDisallowedByPolicy) +
119         prefs::kAllowFileSelectionDialogs;
120     return false;
121   }
122
123   MediaGalleriesPreferences* preferences =
124       media_file_system_registry()->GetPreferences(profile);
125   preferences->EnsureInitialized(callback);
126   return true;
127 }
128
129 // Returns true and sets |gallery_file_path| and |gallery_pref_id| if the
130 // |gallery_id| is valid and returns false otherwise.
131 bool GetGalleryFilePathAndId(const std::string& gallery_id,
132                              Profile* profile,
133                              const Extension* extension,
134                              base::FilePath* gallery_file_path,
135                              MediaGalleryPrefId* gallery_pref_id) {
136   MediaGalleryPrefId pref_id;
137   if (!base::StringToUint64(gallery_id, &pref_id))
138     return false;
139   MediaGalleriesPreferences* preferences =
140       g_browser_process->media_file_system_registry()->GetPreferences(profile);
141   base::FilePath file_path(
142       preferences->LookUpGalleryPathForExtension(pref_id, extension, false));
143   if (file_path.empty())
144     return false;
145   *gallery_pref_id = pref_id;
146   *gallery_file_path = file_path;
147   return true;
148 }
149
150 WebContents* GetWebContents(content::RenderViewHost* rvh,
151                             Profile* profile,
152                             const std::string& app_id) {
153   WebContents* contents = WebContents::FromRenderViewHost(rvh);
154   WebContentsModalDialogManager* web_contents_modal_dialog_manager =
155       WebContentsModalDialogManager::FromWebContents(contents);
156   if (!web_contents_modal_dialog_manager) {
157     // If there is no WebContentsModalDialogManager, then this contents is
158     // probably the background page for an app. Try to find a app window to
159     // host the dialog.
160     AppWindow* window = AppWindowRegistry::Get(profile)
161                             ->GetCurrentAppWindowForApp(app_id);
162     contents = window ? window->web_contents() : NULL;
163   }
164   return contents;
165 }
166
167 base::ListValue* ConstructFileSystemList(
168     content::RenderViewHost* rvh,
169     const Extension* extension,
170     const std::vector<MediaFileSystemInfo>& filesystems) {
171   if (!rvh)
172     return NULL;
173
174   MediaGalleriesPermission::CheckParam read_param(
175       MediaGalleriesPermission::kReadPermission);
176   const PermissionsData* permissions_data = extension->permissions_data();
177   bool has_read_permission = permissions_data->CheckAPIPermissionWithParam(
178       APIPermission::kMediaGalleries, &read_param);
179   MediaGalleriesPermission::CheckParam copy_to_param(
180       MediaGalleriesPermission::kCopyToPermission);
181   bool has_copy_to_permission = permissions_data->CheckAPIPermissionWithParam(
182       APIPermission::kMediaGalleries, &copy_to_param);
183   MediaGalleriesPermission::CheckParam delete_param(
184       MediaGalleriesPermission::kDeletePermission);
185   bool has_delete_permission = permissions_data->CheckAPIPermissionWithParam(
186       APIPermission::kMediaGalleries, &delete_param);
187
188   const int child_id = rvh->GetProcess()->GetID();
189   scoped_ptr<base::ListValue> list(new base::ListValue());
190   for (size_t i = 0; i < filesystems.size(); ++i) {
191     scoped_ptr<base::DictionaryValue> file_system_dict_value(
192         new base::DictionaryValue());
193
194     // Send the file system id so the renderer can create a valid FileSystem
195     // object.
196     file_system_dict_value->SetStringWithoutPathExpansion(
197         "fsid", filesystems[i].fsid);
198
199     file_system_dict_value->SetStringWithoutPathExpansion(
200         kNameKey, filesystems[i].name);
201     file_system_dict_value->SetStringWithoutPathExpansion(
202         kGalleryIdKey,
203         base::Uint64ToString(filesystems[i].pref_id));
204     if (!filesystems[i].transient_device_id.empty()) {
205       file_system_dict_value->SetStringWithoutPathExpansion(
206           kDeviceIdKey, filesystems[i].transient_device_id);
207     }
208     file_system_dict_value->SetBooleanWithoutPathExpansion(
209         kIsRemovableKey, filesystems[i].removable);
210     file_system_dict_value->SetBooleanWithoutPathExpansion(
211         kIsMediaDeviceKey, filesystems[i].media_device);
212     file_system_dict_value->SetBooleanWithoutPathExpansion(
213         kIsAvailableKey, true);
214
215     list->Append(file_system_dict_value.release());
216
217     if (filesystems[i].path.empty())
218       continue;
219
220     if (has_read_permission) {
221       content::ChildProcessSecurityPolicy* policy =
222           content::ChildProcessSecurityPolicy::GetInstance();
223       policy->GrantReadFile(child_id, filesystems[i].path);
224       if (has_delete_permission) {
225         policy->GrantDeleteFrom(child_id, filesystems[i].path);
226         if (has_copy_to_permission) {
227           policy->GrantCopyInto(child_id, filesystems[i].path);
228         }
229       }
230     }
231   }
232
233   return list.release();
234 }
235
236 bool CheckScanPermission(const extensions::Extension* extension,
237                          std::string* error) {
238   DCHECK(extension);
239   DCHECK(error);
240   MediaGalleriesPermission::CheckParam scan_param(
241       MediaGalleriesPermission::kScanPermission);
242   bool has_scan_permission =
243       extension->permissions_data()->CheckAPIPermissionWithParam(
244           APIPermission::kMediaGalleries, &scan_param);
245   if (!has_scan_permission)
246     *error = kNoScanPermission;
247   return has_scan_permission;
248 }
249
250 class SelectDirectoryDialog : public ui::SelectFileDialog::Listener,
251                               public base::RefCounted<SelectDirectoryDialog> {
252  public:
253   // Selected file path, or an empty path if the user canceled.
254   typedef base::Callback<void(const base::FilePath&)> Callback;
255
256   SelectDirectoryDialog(WebContents* web_contents, const Callback& callback)
257       : web_contents_(web_contents),
258         callback_(callback) {
259     select_file_dialog_ = ui::SelectFileDialog::Create(
260         this, new ChromeSelectFilePolicy(web_contents));
261   }
262
263   void Show(const base::FilePath& default_path) {
264     AddRef();  // Balanced in the two reachable listener outcomes.
265     select_file_dialog_->SelectFile(
266       ui::SelectFileDialog::SELECT_FOLDER,
267       l10n_util::GetStringUTF16(IDS_MEDIA_GALLERIES_DIALOG_ADD_GALLERY_TITLE),
268       default_path,
269       NULL,
270       0,
271       base::FilePath::StringType(),
272       platform_util::GetTopLevel(web_contents_->GetNativeView()),
273       NULL);
274   }
275
276   // ui::SelectFileDialog::Listener implementation.
277   virtual void FileSelected(const base::FilePath& path,
278                             int index,
279                             void* params) OVERRIDE {
280     callback_.Run(path);
281     Release();  // Balanced in Show().
282   }
283
284   virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
285                                   void* params) OVERRIDE {
286     NOTREACHED() << "Should not be able to select multiple files";
287   }
288
289   virtual void FileSelectionCanceled(void* params) OVERRIDE {
290     callback_.Run(base::FilePath());
291     Release();  // Balanced in Show().
292   }
293
294  private:
295   friend class base::RefCounted<SelectDirectoryDialog>;
296   virtual ~SelectDirectoryDialog() {}
297
298   scoped_refptr<ui::SelectFileDialog> select_file_dialog_;
299   WebContents* web_contents_;
300   Callback callback_;
301
302   DISALLOW_COPY_AND_ASSIGN(SelectDirectoryDialog);
303 };
304
305 }  // namespace
306
307 MediaGalleriesEventRouter::MediaGalleriesEventRouter(
308     content::BrowserContext* context)
309     : profile_(Profile::FromBrowserContext(context)), weak_ptr_factory_(this) {
310   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
311   DCHECK(profile_);
312
313   EventRouter::Get(profile_)->RegisterObserver(
314       this, MediaGalleries::OnGalleryChanged::kEventName);
315
316   gallery_watch_manager()->AddObserver(profile_, this);
317   media_scan_manager()->AddObserver(profile_, this);
318 }
319
320 MediaGalleriesEventRouter::~MediaGalleriesEventRouter() {
321 }
322
323 void MediaGalleriesEventRouter::Shutdown() {
324   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
325   weak_ptr_factory_.InvalidateWeakPtrs();
326
327   EventRouter::Get(profile_)->UnregisterObserver(this);
328
329   gallery_watch_manager()->RemoveObserver(profile_);
330   media_scan_manager()->RemoveObserver(profile_);
331   media_scan_manager()->CancelScansForProfile(profile_);
332 }
333
334 static base::LazyInstance<
335     BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter> > g_factory =
336     LAZY_INSTANCE_INITIALIZER;
337
338 // static
339 BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>*
340 MediaGalleriesEventRouter::GetFactoryInstance() {
341   return g_factory.Pointer();
342 }
343
344 // static
345 MediaGalleriesEventRouter* MediaGalleriesEventRouter::Get(
346     content::BrowserContext* context) {
347   DCHECK(media_file_system_registry()
348              ->GetPreferences(Profile::FromBrowserContext(context))
349              ->IsInitialized());
350   return BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>::Get(context);
351 }
352
353 bool MediaGalleriesEventRouter::ExtensionHasGalleryChangeListener(
354     const std::string& extension_id) const {
355   return EventRouter::Get(profile_)->ExtensionHasEventListener(
356       extension_id, MediaGalleries::OnGalleryChanged::kEventName);
357 }
358
359 bool MediaGalleriesEventRouter::ExtensionHasScanProgressListener(
360     const std::string& extension_id) const {
361   return EventRouter::Get(profile_)->ExtensionHasEventListener(
362       extension_id, MediaGalleries::OnScanProgress::kEventName);
363 }
364
365 void MediaGalleriesEventRouter::OnScanStarted(const std::string& extension_id) {
366   MediaGalleries::ScanProgressDetails details;
367   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_START;
368   DispatchEventToExtension(
369       extension_id,
370       MediaGalleries::OnScanProgress::kEventName,
371       MediaGalleries::OnScanProgress::Create(details).Pass());
372 }
373
374 void MediaGalleriesEventRouter::OnScanCancelled(
375     const std::string& extension_id) {
376   MediaGalleries::ScanProgressDetails details;
377   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_CANCEL;
378   DispatchEventToExtension(
379       extension_id,
380       MediaGalleries::OnScanProgress::kEventName,
381       MediaGalleries::OnScanProgress::Create(details).Pass());
382 }
383
384 void MediaGalleriesEventRouter::OnScanFinished(
385     const std::string& extension_id, int gallery_count,
386     const MediaGalleryScanResult& file_counts) {
387   media_galleries::UsageCount(media_galleries::SCAN_FINISHED);
388   MediaGalleries::ScanProgressDetails details;
389   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_FINISH;
390   details.gallery_count.reset(new int(gallery_count));
391   details.audio_count.reset(new int(file_counts.audio_count));
392   details.image_count.reset(new int(file_counts.image_count));
393   details.video_count.reset(new int(file_counts.video_count));
394   DispatchEventToExtension(
395       extension_id,
396       MediaGalleries::OnScanProgress::kEventName,
397       MediaGalleries::OnScanProgress::Create(details).Pass());
398 }
399
400 void MediaGalleriesEventRouter::OnScanError(
401     const std::string& extension_id) {
402   MediaGalleries::ScanProgressDetails details;
403   details.type = MediaGalleries::SCAN_PROGRESS_TYPE_ERROR;
404   DispatchEventToExtension(
405       extension_id,
406       MediaGalleries::OnScanProgress::kEventName,
407       MediaGalleries::OnScanProgress::Create(details).Pass());
408 }
409
410 void MediaGalleriesEventRouter::DispatchEventToExtension(
411     const std::string& extension_id,
412     const std::string& event_name,
413     scoped_ptr<base::ListValue> event_args) {
414   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
415   EventRouter* router = EventRouter::Get(profile_);
416   if (!router->ExtensionHasEventListener(extension_id, event_name))
417     return;
418
419   scoped_ptr<extensions::Event> event(
420       new extensions::Event(event_name, event_args.Pass()));
421   router->DispatchEventToExtension(extension_id, event.Pass());
422 }
423
424 void MediaGalleriesEventRouter::OnGalleryChanged(
425     const std::string& extension_id, MediaGalleryPrefId gallery_id) {
426   MediaGalleries::GalleryChangeDetails details;
427   details.type = MediaGalleries::GALLERY_CHANGE_TYPE_CONTENTS_CHANGED;
428   details.gallery_id = base::Uint64ToString(gallery_id);
429   DispatchEventToExtension(
430       extension_id,
431       MediaGalleries::OnGalleryChanged::kEventName,
432       MediaGalleries::OnGalleryChanged::Create(details).Pass());
433 }
434
435 void MediaGalleriesEventRouter::OnGalleryWatchDropped(
436     const std::string& extension_id, MediaGalleryPrefId gallery_id) {
437   MediaGalleries::GalleryChangeDetails details;
438   details.type = MediaGalleries::GALLERY_CHANGE_TYPE_WATCH_DROPPED;
439   details.gallery_id = gallery_id;
440   DispatchEventToExtension(
441       extension_id,
442       MediaGalleries::OnGalleryChanged::kEventName,
443       MediaGalleries::OnGalleryChanged::Create(details).Pass());
444 }
445
446 void MediaGalleriesEventRouter::OnListenerRemoved(
447     const EventListenerInfo& details) {
448   if (details.event_name == MediaGalleries::OnGalleryChanged::kEventName &&
449       !ExtensionHasGalleryChangeListener(details.extension_id)) {
450     gallery_watch_manager()->RemoveAllWatches(profile_, details.extension_id);
451   }
452 }
453
454 ///////////////////////////////////////////////////////////////////////////////
455 //               MediaGalleriesGetMediaFileSystemsFunction                   //
456 ///////////////////////////////////////////////////////////////////////////////
457 MediaGalleriesGetMediaFileSystemsFunction::
458     ~MediaGalleriesGetMediaFileSystemsFunction() {}
459
460 bool MediaGalleriesGetMediaFileSystemsFunction::RunAsync() {
461   media_galleries::UsageCount(media_galleries::GET_MEDIA_FILE_SYSTEMS);
462   scoped_ptr<GetMediaFileSystems::Params> params(
463       GetMediaFileSystems::Params::Create(*args_));
464   EXTENSION_FUNCTION_VALIDATE(params.get());
465   MediaGalleries::GetMediaFileSystemsInteractivity interactive =
466       MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO;
467   if (params->details.get() && params->details->interactive != MediaGalleries::
468          GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE) {
469     interactive = params->details->interactive;
470   }
471
472   return Setup(GetProfile(), &error_, base::Bind(
473       &MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit, this,
474       interactive));
475 }
476
477 void MediaGalleriesGetMediaFileSystemsFunction::OnPreferencesInit(
478     MediaGalleries::GetMediaFileSystemsInteractivity interactive) {
479   switch (interactive) {
480     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_YES: {
481       // The MediaFileSystemRegistry only updates preferences for extensions
482       // that it knows are in use. Since this may be the first call to
483       // chrome.getMediaFileSystems for this extension, call
484       // GetMediaFileSystemsForExtension() here solely so that
485       // MediaFileSystemRegistry will send preference changes.
486       GetMediaFileSystemsForExtension(base::Bind(
487           &MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog, this));
488       return;
489     }
490     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_IF_NEEDED: {
491       GetMediaFileSystemsForExtension(base::Bind(
492           &MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries,
493           this));
494       return;
495     }
496     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO:
497       GetAndReturnGalleries();
498       return;
499     case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE:
500       NOTREACHED();
501   }
502   SendResponse(false);
503 }
504
505 void MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog(
506     const std::vector<MediaFileSystemInfo>& /*filesystems*/) {
507   ShowDialog();
508 }
509
510 void MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries(
511     const std::vector<MediaFileSystemInfo>& filesystems) {
512   if (filesystems.empty())
513     ShowDialog();
514   else
515     ReturnGalleries(filesystems);
516 }
517
518 void MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries() {
519   GetMediaFileSystemsForExtension(base::Bind(
520       &MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries, this));
521 }
522
523 void MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries(
524     const std::vector<MediaFileSystemInfo>& filesystems) {
525   scoped_ptr<base::ListValue> list(
526       ConstructFileSystemList(render_view_host(), extension(), filesystems));
527   if (!list.get()) {
528     SendResponse(false);
529     return;
530   }
531
532   // The custom JS binding will use this list to create DOMFileSystem objects.
533   SetResult(list.release());
534   SendResponse(true);
535 }
536
537 void MediaGalleriesGetMediaFileSystemsFunction::ShowDialog() {
538   media_galleries::UsageCount(media_galleries::SHOW_DIALOG);
539   WebContents* contents =
540       GetWebContents(render_view_host(), GetProfile(), extension()->id());
541   if (!contents) {
542     SendResponse(false);
543     return;
544   }
545
546   // Controller will delete itself.
547   base::Closure cb = base::Bind(
548       &MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries, this);
549   new MediaGalleriesPermissionController(contents, *extension(), cb);
550 }
551
552 void MediaGalleriesGetMediaFileSystemsFunction::GetMediaFileSystemsForExtension(
553     const MediaFileSystemsCallback& cb) {
554   if (!render_view_host()) {
555     cb.Run(std::vector<MediaFileSystemInfo>());
556     return;
557   }
558   MediaFileSystemRegistry* registry = media_file_system_registry();
559   DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
560   registry->GetMediaFileSystemsForExtension(
561       render_view_host(), extension(), cb);
562 }
563
564
565 ///////////////////////////////////////////////////////////////////////////////
566 //          MediaGalleriesGetAllMediaFileSystemMetadataFunction              //
567 ///////////////////////////////////////////////////////////////////////////////
568 MediaGalleriesGetAllMediaFileSystemMetadataFunction::
569     ~MediaGalleriesGetAllMediaFileSystemMetadataFunction() {}
570
571 bool MediaGalleriesGetAllMediaFileSystemMetadataFunction::RunAsync() {
572   media_galleries::UsageCount(
573       media_galleries::GET_ALL_MEDIA_FILE_SYSTEM_METADATA);
574   return Setup(GetProfile(), &error_, base::Bind(
575       &MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnPreferencesInit,
576       this));
577 }
578
579 void MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnPreferencesInit() {
580   MediaFileSystemRegistry* registry = media_file_system_registry();
581   MediaGalleriesPreferences* prefs = registry->GetPreferences(GetProfile());
582   DCHECK(prefs->IsInitialized());
583   MediaGalleryPrefIdSet permitted_gallery_ids =
584       prefs->GalleriesForExtension(*extension());
585
586   MediaStorageUtil::DeviceIdSet* device_ids = new MediaStorageUtil::DeviceIdSet;
587   const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
588   for (MediaGalleryPrefIdSet::const_iterator it = permitted_gallery_ids.begin();
589        it != permitted_gallery_ids.end(); ++it) {
590     MediaGalleriesPrefInfoMap::const_iterator gallery_it = galleries.find(*it);
591     DCHECK(gallery_it != galleries.end());
592     device_ids->insert(gallery_it->second.device_id);
593   }
594
595   MediaStorageUtil::FilterAttachedDevices(
596       device_ids,
597       base::Bind(
598           &MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries,
599           this,
600           permitted_gallery_ids,
601           base::Owned(device_ids)));
602 }
603
604 void MediaGalleriesGetAllMediaFileSystemMetadataFunction::OnGetGalleries(
605     const MediaGalleryPrefIdSet& permitted_gallery_ids,
606     const MediaStorageUtil::DeviceIdSet* available_devices) {
607   MediaFileSystemRegistry* registry = media_file_system_registry();
608   MediaGalleriesPreferences* prefs = registry->GetPreferences(GetProfile());
609
610   base::ListValue* list = new base::ListValue();
611   const MediaGalleriesPrefInfoMap& galleries = prefs->known_galleries();
612   for (MediaGalleryPrefIdSet::const_iterator it = permitted_gallery_ids.begin();
613        it != permitted_gallery_ids.end(); ++it) {
614     MediaGalleriesPrefInfoMap::const_iterator gallery_it = galleries.find(*it);
615     DCHECK(gallery_it != galleries.end());
616     const MediaGalleryPrefInfo& gallery = gallery_it->second;
617     MediaGalleries::MediaFileSystemMetadata metadata;
618     metadata.name = base::UTF16ToUTF8(gallery.GetGalleryDisplayName());
619     metadata.gallery_id = base::Uint64ToString(gallery.pref_id);
620     metadata.is_removable = StorageInfo::IsRemovableDevice(gallery.device_id);
621     metadata.is_media_device = StorageInfo::IsMediaDevice(gallery.device_id);
622     metadata.is_available = ContainsKey(*available_devices, gallery.device_id);
623     list->Append(metadata.ToValue().release());
624   }
625
626   SetResult(list);
627   SendResponse(true);
628 }
629
630 ///////////////////////////////////////////////////////////////////////////////
631 //               MediaGalleriesAddUserSelectedFolderFunction                 //
632 ///////////////////////////////////////////////////////////////////////////////
633 MediaGalleriesAddUserSelectedFolderFunction::
634     ~MediaGalleriesAddUserSelectedFolderFunction() {}
635
636 bool MediaGalleriesAddUserSelectedFolderFunction::RunAsync() {
637   media_galleries::UsageCount(media_galleries::ADD_USER_SELECTED_FOLDER);
638   return Setup(GetProfile(), &error_, base::Bind(
639       &MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit, this));
640 }
641
642 void MediaGalleriesAddUserSelectedFolderFunction::OnPreferencesInit() {
643   Profile* profile = GetProfile();
644   const std::string& app_id = extension()->id();
645   WebContents* contents = GetWebContents(render_view_host(), profile, app_id);
646   if (!contents) {
647     // When the request originated from a background page, but there is no app
648     // window open, check to see if it originated from a tab and display the
649     // dialog in that tab.
650     bool found_tab = extensions::ExtensionTabUtil::GetTabById(
651         source_tab_id(), profile, profile->IsOffTheRecord(),
652         NULL, NULL, &contents, NULL);
653     if (!found_tab || !contents) {
654       SendResponse(false);
655       return;
656     }
657   }
658
659   if (!user_gesture()) {
660     OnDirectorySelected(base::FilePath());
661     return;
662   }
663
664   base::FilePath last_used_path =
665       extensions::file_system_api::GetLastChooseEntryDirectory(
666           extensions::ExtensionPrefs::Get(profile), app_id);
667   SelectDirectoryDialog::Callback callback = base::Bind(
668       &MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected, this);
669   scoped_refptr<SelectDirectoryDialog> select_directory_dialog =
670       new SelectDirectoryDialog(contents, callback);
671   select_directory_dialog->Show(last_used_path);
672 }
673
674 void MediaGalleriesAddUserSelectedFolderFunction::OnDirectorySelected(
675     const base::FilePath& selected_directory) {
676   if (selected_directory.empty()) {
677     // User cancelled case.
678     GetMediaFileSystemsForExtension(base::Bind(
679         &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
680         this,
681         kInvalidMediaGalleryPrefId));
682     return;
683   }
684
685   extensions::file_system_api::SetLastChooseEntryDirectory(
686       extensions::ExtensionPrefs::Get(GetProfile()),
687       extension()->id(),
688       selected_directory);
689
690   MediaGalleriesPreferences* preferences =
691       media_file_system_registry()->GetPreferences(GetProfile());
692   MediaGalleryPrefId pref_id =
693       preferences->AddGalleryByPath(selected_directory,
694                                     MediaGalleryPrefInfo::kUserAdded);
695   preferences->SetGalleryPermissionForExtension(*extension(), pref_id, true);
696
697   GetMediaFileSystemsForExtension(base::Bind(
698       &MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId,
699       this,
700       pref_id));
701 }
702
703 void MediaGalleriesAddUserSelectedFolderFunction::ReturnGalleriesAndId(
704     MediaGalleryPrefId pref_id,
705     const std::vector<MediaFileSystemInfo>& filesystems) {
706   scoped_ptr<base::ListValue> list(
707       ConstructFileSystemList(render_view_host(), extension(), filesystems));
708   if (!list.get()) {
709     SendResponse(false);
710     return;
711   }
712
713   int index = -1;
714   if (pref_id != kInvalidMediaGalleryPrefId) {
715     for (size_t i = 0; i < filesystems.size(); ++i) {
716       if (filesystems[i].pref_id == pref_id) {
717         index = i;
718         break;
719       }
720     }
721   }
722   base::DictionaryValue* results = new base::DictionaryValue;
723   results->SetWithoutPathExpansion("mediaFileSystems", list.release());
724   results->SetIntegerWithoutPathExpansion("selectedFileSystemIndex", index);
725   SetResult(results);
726   SendResponse(true);
727 }
728
729 void
730 MediaGalleriesAddUserSelectedFolderFunction::GetMediaFileSystemsForExtension(
731     const MediaFileSystemsCallback& cb) {
732   if (!render_view_host()) {
733     cb.Run(std::vector<MediaFileSystemInfo>());
734     return;
735   }
736   MediaFileSystemRegistry* registry = media_file_system_registry();
737   DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
738   registry->GetMediaFileSystemsForExtension(
739       render_view_host(), extension(), cb);
740 }
741
742 ///////////////////////////////////////////////////////////////////////////////
743 //         MediaGalleriesDropPermissionForMediaFileSystemFunction            //
744 ///////////////////////////////////////////////////////////////////////////////
745 MediaGalleriesDropPermissionForMediaFileSystemFunction::
746     ~MediaGalleriesDropPermissionForMediaFileSystemFunction() {}
747
748 bool MediaGalleriesDropPermissionForMediaFileSystemFunction::RunAsync() {
749   media_galleries::UsageCount(
750       media_galleries::DROP_PERMISSION_FOR_MEDIA_FILE_SYSTEM);
751
752   scoped_ptr<DropPermissionForMediaFileSystem::Params> params(
753       DropPermissionForMediaFileSystem::Params::Create(*args_));
754   EXTENSION_FUNCTION_VALIDATE(params.get());
755   MediaGalleryPrefId pref_id;
756   if (!base::StringToUint64(params->gallery_id, &pref_id)) {
757     error_ = kInvalidGalleryIdMsg;
758     return false;
759   }
760
761   base::Closure callback = base::Bind(
762       &MediaGalleriesDropPermissionForMediaFileSystemFunction::
763           OnPreferencesInit,
764       this,
765       pref_id);
766   return Setup(GetProfile(), &error_, callback);
767 }
768
769 void MediaGalleriesDropPermissionForMediaFileSystemFunction::OnPreferencesInit(
770     MediaGalleryPrefId pref_id) {
771   MediaGalleriesPreferences* preferences =
772       media_file_system_registry()->GetPreferences(GetProfile());
773   if (!ContainsKey(preferences->known_galleries(), pref_id)) {
774     error_ = kNonExistentGalleryId;
775     SendResponse(false);
776     return;
777   }
778
779   bool dropped = preferences->SetGalleryPermissionForExtension(
780       *extension(), pref_id, false);
781   if (dropped)
782     SetResult(new base::StringValue(base::Uint64ToString(pref_id)));
783   else
784     error_ = kFailedToSetGalleryPermission;
785   SendResponse(dropped);
786 }
787
788 ///////////////////////////////////////////////////////////////////////////////
789 //                 MediaGalleriesStartMediaScanFunction                      //
790 ///////////////////////////////////////////////////////////////////////////////
791 MediaGalleriesStartMediaScanFunction::~MediaGalleriesStartMediaScanFunction() {}
792
793 bool MediaGalleriesStartMediaScanFunction::RunAsync() {
794   media_galleries::UsageCount(media_galleries::START_MEDIA_SCAN);
795   if (!CheckScanPermission(extension(), &error_)) {
796     MediaGalleriesEventRouter::Get(GetProfile())
797         ->OnScanError(extension()->id());
798     return false;
799   }
800   return Setup(GetProfile(), &error_, base::Bind(
801       &MediaGalleriesStartMediaScanFunction::OnPreferencesInit, this));
802 }
803
804 void MediaGalleriesStartMediaScanFunction::OnPreferencesInit() {
805   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
806   MediaGalleriesEventRouter* api = MediaGalleriesEventRouter::Get(GetProfile());
807   if (!api->ExtensionHasScanProgressListener(extension()->id())) {
808     error_ = kMissingEventListener;
809     SendResponse(false);
810     return;
811   }
812
813   media_scan_manager()->StartScan(GetProfile(), extension(), user_gesture());
814   SendResponse(true);
815 }
816
817 ///////////////////////////////////////////////////////////////////////////////
818 //                MediaGalleriesCancelMediaScanFunction                      //
819 ///////////////////////////////////////////////////////////////////////////////
820 MediaGalleriesCancelMediaScanFunction::
821     ~MediaGalleriesCancelMediaScanFunction() {
822 }
823
824 bool MediaGalleriesCancelMediaScanFunction::RunAsync() {
825   media_galleries::UsageCount(media_galleries::CANCEL_MEDIA_SCAN);
826   if (!CheckScanPermission(extension(), &error_)) {
827     MediaGalleriesEventRouter::Get(GetProfile())
828         ->OnScanError(extension()->id());
829     return false;
830   }
831   return Setup(GetProfile(), &error_, base::Bind(
832       &MediaGalleriesCancelMediaScanFunction::OnPreferencesInit, this));
833 }
834
835 void MediaGalleriesCancelMediaScanFunction::OnPreferencesInit() {
836   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
837   media_scan_manager()->CancelScan(GetProfile(), extension());
838   SendResponse(true);
839 }
840
841 ///////////////////////////////////////////////////////////////////////////////
842 //                MediaGalleriesAddScanResultsFunction                       //
843 ///////////////////////////////////////////////////////////////////////////////
844 MediaGalleriesAddScanResultsFunction::~MediaGalleriesAddScanResultsFunction() {}
845
846 bool MediaGalleriesAddScanResultsFunction::RunAsync() {
847   media_galleries::UsageCount(media_galleries::ADD_SCAN_RESULTS);
848   if (!CheckScanPermission(extension(), &error_)) {
849     // We don't fire a scan progress error here, as it would be unintuitive.
850     return false;
851   }
852   if (!user_gesture())
853     return false;
854
855   return Setup(GetProfile(), &error_, base::Bind(
856       &MediaGalleriesAddScanResultsFunction::OnPreferencesInit, this));
857 }
858
859 MediaGalleriesScanResultController*
860 MediaGalleriesAddScanResultsFunction::MakeDialog(
861     content::WebContents* web_contents,
862     const extensions::Extension& extension,
863     const base::Closure& on_finish) {
864   // Controller will delete itself.
865   return new MediaGalleriesScanResultController(web_contents, extension,
866                                                 on_finish);
867 }
868
869 void MediaGalleriesAddScanResultsFunction::OnPreferencesInit() {
870   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
871   MediaGalleriesPreferences* preferences =
872       media_file_system_registry()->GetPreferences(GetProfile());
873   if (MediaGalleriesScanResultController::ScanResultCountForExtension(
874           preferences, extension()) == 0) {
875     GetAndReturnGalleries();
876     return;
877   }
878
879   WebContents* contents =
880       GetWebContents(render_view_host(), GetProfile(), extension()->id());
881   if (!contents) {
882     SendResponse(false);
883     return;
884   }
885
886   base::Closure cb = base::Bind(
887       &MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries, this);
888   MakeDialog(contents, *extension(), cb);
889 }
890
891 void MediaGalleriesAddScanResultsFunction::GetAndReturnGalleries() {
892   if (!render_view_host()) {
893     ReturnGalleries(std::vector<MediaFileSystemInfo>());
894     return;
895   }
896   MediaFileSystemRegistry* registry = media_file_system_registry();
897   DCHECK(registry->GetPreferences(GetProfile())->IsInitialized());
898   registry->GetMediaFileSystemsForExtension(
899       render_view_host(),
900       extension(),
901       base::Bind(&MediaGalleriesAddScanResultsFunction::ReturnGalleries, this));
902 }
903
904 void MediaGalleriesAddScanResultsFunction::ReturnGalleries(
905     const std::vector<MediaFileSystemInfo>& filesystems) {
906   scoped_ptr<base::ListValue> list(
907       ConstructFileSystemList(render_view_host(), extension(), filesystems));
908   if (!list.get()) {
909     SendResponse(false);
910     return;
911   }
912
913   // The custom JS binding will use this list to create DOMFileSystem objects.
914   SetResult(list.release());
915   SendResponse(true);
916 }
917
918 ///////////////////////////////////////////////////////////////////////////////
919 //                 MediaGalleriesGetMetadataFunction                         //
920 ///////////////////////////////////////////////////////////////////////////////
921 MediaGalleriesGetMetadataFunction::~MediaGalleriesGetMetadataFunction() {}
922
923 bool MediaGalleriesGetMetadataFunction::RunAsync() {
924   std::string blob_uuid;
925   EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &blob_uuid));
926
927   const base::Value* options_value = NULL;
928   if (!args_->Get(1, &options_value))
929     return false;
930   scoped_ptr<MediaGalleries::MediaMetadataOptions> options =
931       MediaGalleries::MediaMetadataOptions::FromValue(*options_value);
932   if (!options)
933     return false;
934
935   return Setup(GetProfile(), &error_, base::Bind(
936       &MediaGalleriesGetMetadataFunction::OnPreferencesInit, this,
937       options->metadata_type, blob_uuid));
938 }
939
940 void MediaGalleriesGetMetadataFunction::OnPreferencesInit(
941     MediaGalleries::GetMetadataType metadata_type,
942     const std::string& blob_uuid) {
943   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
944
945   // BlobReader is self-deleting.
946   BlobReader* reader = new BlobReader(
947       GetProfile(),
948       blob_uuid,
949       base::Bind(&MediaGalleriesGetMetadataFunction::GetMetadata, this,
950                  metadata_type, blob_uuid));
951   reader->SetByteRange(0, net::kMaxBytesToSniff);
952   reader->Start();
953 }
954
955 void MediaGalleriesGetMetadataFunction::GetMetadata(
956     MediaGalleries::GetMetadataType metadata_type, const std::string& blob_uuid,
957     scoped_ptr<std::string> blob_header, int64 total_blob_length) {
958   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
959
960   std::string mime_type;
961   bool mime_type_sniffed = net::SniffMimeTypeFromLocalData(
962       blob_header->c_str(), blob_header->size(), &mime_type);
963
964   if (!mime_type_sniffed) {
965     SendResponse(false);
966     return;
967   }
968
969   if (metadata_type == MediaGalleries::GET_METADATA_TYPE_MIMETYPEONLY) {
970     MediaGalleries::MediaMetadata metadata;
971     metadata.mime_type = mime_type;
972
973     base::DictionaryValue* result_dictionary = new base::DictionaryValue;
974     result_dictionary->Set(kMetadataKey, metadata.ToValue().release());
975     SetResult(result_dictionary);
976     SendResponse(true);
977     return;
978   }
979
980   // We get attached images by default. GET_METADATA_TYPE_NONE is the default
981   // value if the caller doesn't specify the metadata type.
982   bool get_attached_images =
983       metadata_type == MediaGalleries::GET_METADATA_TYPE_ALL ||
984       metadata_type == MediaGalleries::GET_METADATA_TYPE_NONE;
985
986   scoped_refptr<metadata::SafeMediaMetadataParser> parser(
987       new metadata::SafeMediaMetadataParser(GetProfile(), blob_uuid,
988                                             total_blob_length, mime_type,
989                                             get_attached_images));
990   parser->Start(base::Bind(
991       &MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone, this));
992 }
993
994 void MediaGalleriesGetMetadataFunction::OnSafeMediaMetadataParserDone(
995     bool parse_success, scoped_ptr<base::DictionaryValue> metadata_dictionary,
996     scoped_ptr<std::vector<metadata::AttachedImage> > attached_images) {
997   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
998
999   if (!parse_success) {
1000     SendResponse(false);
1001     return;
1002   }
1003
1004   DCHECK(metadata_dictionary.get());
1005   DCHECK(attached_images.get());
1006
1007   scoped_ptr<base::DictionaryValue> result_dictionary(
1008       new base::DictionaryValue);
1009   result_dictionary->Set(kMetadataKey, metadata_dictionary.release());
1010
1011   if (attached_images->empty()) {
1012     SetResult(result_dictionary.release());
1013     SendResponse(true);
1014     return;
1015   }
1016
1017   result_dictionary->Set(kAttachedImagesBlobInfoKey, new base::ListValue);
1018   metadata::AttachedImage* first_image = &attached_images->front();
1019   content::BrowserContext::CreateMemoryBackedBlob(
1020       GetProfile(),
1021       first_image->data.c_str(),
1022       first_image->data.size(),
1023       base::Bind(&MediaGalleriesGetMetadataFunction::ConstructNextBlob,
1024                  this, base::Passed(&result_dictionary),
1025                  base::Passed(&attached_images),
1026                  base::Passed(make_scoped_ptr(new std::vector<std::string>))));
1027 }
1028
1029 void MediaGalleriesGetMetadataFunction::ConstructNextBlob(
1030     scoped_ptr<base::DictionaryValue> result_dictionary,
1031     scoped_ptr<std::vector<metadata::AttachedImage> > attached_images,
1032     scoped_ptr<std::vector<std::string> > blob_uuids,
1033     scoped_ptr<content::BlobHandle> current_blob) {
1034   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1035
1036   DCHECK(result_dictionary.get());
1037   DCHECK(attached_images.get());
1038   DCHECK(blob_uuids.get());
1039   DCHECK(current_blob.get());
1040
1041   DCHECK(!attached_images->empty());
1042   DCHECK_LT(blob_uuids->size(), attached_images->size());
1043
1044   // For the newly constructed Blob, store its image's metadata and Blob UUID.
1045   base::ListValue* attached_images_list = NULL;
1046   result_dictionary->GetList(kAttachedImagesBlobInfoKey, &attached_images_list);
1047   DCHECK(attached_images_list);
1048   DCHECK_LT(attached_images_list->GetSize(), attached_images->size());
1049
1050   metadata::AttachedImage* current_image =
1051       &(*attached_images)[blob_uuids->size()];
1052   base::DictionaryValue* attached_image = new base::DictionaryValue;
1053   attached_image->Set(kBlobUUIDKey, new base::StringValue(
1054       current_blob->GetUUID()));
1055   attached_image->Set(kTypeKey, new base::StringValue(
1056       current_image->type));
1057   attached_image->Set(kSizeKey, new base::FundamentalValue(
1058       base::checked_cast<int>(current_image->data.size())));
1059   attached_images_list->Append(attached_image);
1060
1061   blob_uuids->push_back(current_blob->GetUUID());
1062   WebContents* contents = WebContents::FromRenderViewHost(render_view_host());
1063   extensions::BlobHolder* holder =
1064       extensions::BlobHolder::FromRenderProcessHost(
1065           contents->GetRenderProcessHost());
1066   holder->HoldBlobReference(current_blob.Pass());
1067
1068   // Construct the next Blob if necessary.
1069   if (blob_uuids->size() < attached_images->size()) {
1070     metadata::AttachedImage* next_image =
1071         &(*attached_images)[blob_uuids->size()];
1072     content::BrowserContext::CreateMemoryBackedBlob(
1073         GetProfile(),
1074         next_image->data.c_str(),
1075         next_image->data.size(),
1076         base::Bind(&MediaGalleriesGetMetadataFunction::ConstructNextBlob,
1077                    this, base::Passed(&result_dictionary),
1078                    base::Passed(&attached_images), base::Passed(&blob_uuids)));
1079     return;
1080   }
1081
1082   // All Blobs have been constructed. The renderer will take ownership.
1083   SetResult(result_dictionary.release());
1084   SetTransferredBlobUUIDs(*blob_uuids);
1085   SendResponse(true);
1086 }
1087
1088 ///////////////////////////////////////////////////////////////////////////////
1089 //              MediaGalleriesAddGalleryWatchFunction                        //
1090 ///////////////////////////////////////////////////////////////////////////////
1091 MediaGalleriesAddGalleryWatchFunction::
1092     ~MediaGalleriesAddGalleryWatchFunction() {
1093 }
1094
1095 bool MediaGalleriesAddGalleryWatchFunction::RunAsync() {
1096   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1097   DCHECK(GetProfile());
1098   if (!render_view_host() || !render_view_host()->GetProcess())
1099     return false;
1100
1101   scoped_ptr<AddGalleryWatch::Params> params(
1102       AddGalleryWatch::Params::Create(*args_));
1103   EXTENSION_FUNCTION_VALIDATE(params.get());
1104
1105   MediaGalleriesPreferences* preferences =
1106       g_browser_process->media_file_system_registry()->GetPreferences(
1107           GetProfile());
1108   preferences->EnsureInitialized(
1109       base::Bind(&MediaGalleriesAddGalleryWatchFunction::OnPreferencesInit,
1110                  this,
1111                  params->gallery_id));
1112
1113   return true;
1114 }
1115
1116 void MediaGalleriesAddGalleryWatchFunction::OnPreferencesInit(
1117     const std::string& pref_id) {
1118   base::FilePath gallery_file_path;
1119   MediaGalleryPrefId gallery_pref_id = kInvalidMediaGalleryPrefId;
1120   if (!GetGalleryFilePathAndId(pref_id,
1121                                GetProfile(),
1122                                extension(),
1123                                &gallery_file_path,
1124                                &gallery_pref_id)) {
1125     api::media_galleries::AddGalleryWatchResult result;
1126     error_ = kInvalidGalleryIdMsg;
1127     result.gallery_id = kInvalidGalleryId;
1128     result.success = false;
1129     SetResult(result.ToValue().release());
1130     SendResponse(false);
1131     return;
1132   }
1133
1134   gallery_watch_manager()->AddWatch(
1135       GetProfile(),
1136       extension(),
1137       gallery_pref_id,
1138       base::Bind(&MediaGalleriesAddGalleryWatchFunction::HandleResponse,
1139                  this,
1140                  gallery_pref_id));
1141 }
1142
1143 void MediaGalleriesAddGalleryWatchFunction::HandleResponse(
1144     MediaGalleryPrefId gallery_id,
1145     const std::string& error) {
1146   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1147
1148   // If an app added a file watch without any event listeners on the
1149   // onGalleryChanged event, that's an error.
1150   MediaGalleriesEventRouter* api = MediaGalleriesEventRouter::Get(GetProfile());
1151   api::media_galleries::AddGalleryWatchResult result;
1152   result.gallery_id = base::Uint64ToString(gallery_id);
1153
1154   if (!api->ExtensionHasGalleryChangeListener(extension()->id())) {
1155     result.success = false;
1156     SetResult(result.ToValue().release());
1157     error_ = kMissingEventListener;
1158     SendResponse(false);
1159     return;
1160   }
1161
1162   result.success = error.empty();
1163   SetResult(result.ToValue().release());
1164   if (error.empty()) {
1165     SendResponse(true);
1166   } else {
1167     error_ = error.c_str();
1168     SendResponse(false);
1169   }
1170 }
1171
1172 ///////////////////////////////////////////////////////////////////////////////
1173 //              MediaGalleriesRemoveGalleryWatchFunction                     //
1174 ///////////////////////////////////////////////////////////////////////////////
1175
1176 MediaGalleriesRemoveGalleryWatchFunction::
1177     ~MediaGalleriesRemoveGalleryWatchFunction() {
1178 }
1179
1180 bool MediaGalleriesRemoveGalleryWatchFunction::RunAsync() {
1181   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1182   if (!render_view_host() || !render_view_host()->GetProcess())
1183     return false;
1184
1185   scoped_ptr<RemoveGalleryWatch::Params> params(
1186       RemoveGalleryWatch::Params::Create(*args_));
1187   EXTENSION_FUNCTION_VALIDATE(params.get());
1188
1189   MediaGalleriesPreferences* preferences =
1190       g_browser_process->media_file_system_registry()->GetPreferences(
1191           GetProfile());
1192   preferences->EnsureInitialized(
1193       base::Bind(&MediaGalleriesRemoveGalleryWatchFunction::OnPreferencesInit,
1194                  this,
1195                  params->gallery_id));
1196   return true;
1197 }
1198
1199 void MediaGalleriesRemoveGalleryWatchFunction::OnPreferencesInit(
1200     const std::string& pref_id) {
1201   base::FilePath gallery_file_path;
1202   MediaGalleryPrefId gallery_pref_id = 0;
1203   if (!GetGalleryFilePathAndId(pref_id,
1204                                GetProfile(),
1205                                extension(),
1206                                &gallery_file_path,
1207                                &gallery_pref_id)) {
1208     error_ = kInvalidGalleryIdMsg;
1209     SendResponse(false);
1210     return;
1211   }
1212
1213   gallery_watch_manager()->RemoveWatch(
1214       GetProfile(), extension_id(), gallery_pref_id);
1215   SendResponse(true);
1216 }
1217
1218 ///////////////////////////////////////////////////////////////////////////////
1219 //              MediaGalleriesGetAllGalleryWatchFunction                     //
1220 ///////////////////////////////////////////////////////////////////////////////
1221
1222 MediaGalleriesGetAllGalleryWatchFunction::
1223     ~MediaGalleriesGetAllGalleryWatchFunction() {
1224 }
1225
1226 bool MediaGalleriesGetAllGalleryWatchFunction::RunAsync() {
1227   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1228   if (!render_view_host() || !render_view_host()->GetProcess())
1229     return false;
1230
1231   MediaGalleriesPreferences* preferences =
1232       g_browser_process->media_file_system_registry()->GetPreferences(
1233           GetProfile());
1234   preferences->EnsureInitialized(base::Bind(
1235       &MediaGalleriesGetAllGalleryWatchFunction::OnPreferencesInit, this));
1236   return true;
1237 }
1238
1239 void MediaGalleriesGetAllGalleryWatchFunction::OnPreferencesInit() {
1240   std::vector<std::string> result;
1241   MediaGalleryPrefIdSet gallery_ids =
1242       gallery_watch_manager()->GetWatchSet(GetProfile(), extension_id());
1243   for (MediaGalleryPrefIdSet::const_iterator iter = gallery_ids.begin();
1244        iter != gallery_ids.end();
1245        ++iter) {
1246     result.push_back(base::Uint64ToString(*iter));
1247   }
1248   results_ = GetAllGalleryWatch::Results::Create(result);
1249   SendResponse(true);
1250 }
1251
1252 ///////////////////////////////////////////////////////////////////////////////
1253 //              MediaGalleriesRemoveAllGalleryWatchFunction                  //
1254 ///////////////////////////////////////////////////////////////////////////////
1255
1256 MediaGalleriesRemoveAllGalleryWatchFunction::
1257     ~MediaGalleriesRemoveAllGalleryWatchFunction() {
1258 }
1259
1260 bool MediaGalleriesRemoveAllGalleryWatchFunction::RunAsync() {
1261   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
1262   if (!render_view_host() || !render_view_host()->GetProcess())
1263     return false;
1264
1265   MediaGalleriesPreferences* preferences =
1266       g_browser_process->media_file_system_registry()->GetPreferences(
1267           GetProfile());
1268   preferences->EnsureInitialized(base::Bind(
1269       &MediaGalleriesRemoveAllGalleryWatchFunction::OnPreferencesInit, this));
1270   return true;
1271 }
1272
1273 void MediaGalleriesRemoveAllGalleryWatchFunction::OnPreferencesInit() {
1274   gallery_watch_manager()->RemoveAllWatches(GetProfile(), extension_id());
1275   SendResponse(true);
1276 }
1277
1278 }  // namespace extensions