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
60using content::WebContents;
61using storage_monitor::MediaStorageUtil;
62using storage_monitor::StorageInfo;
63using web_modal::WebContentsModalDialogManager;
64
65namespace extensions {
66
67namespace MediaGalleries = api::media_galleries;
68namespace DropPermissionForMediaFileSystem =
69    MediaGalleries::DropPermissionForMediaFileSystem;
70namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems;
71namespace AddGalleryWatch = MediaGalleries::AddGalleryWatch;
72namespace RemoveGalleryWatch = MediaGalleries::RemoveGalleryWatch;
73namespace GetAllGalleryWatch = MediaGalleries::GetAllGalleryWatch;
74
75namespace {
76
77const char kDisallowedByPolicy[] =
78    "Media Galleries API is disallowed by policy: ";
79const char kFailedToSetGalleryPermission[] =
80    "Failed to set gallery permission.";
81const char kInvalidGalleryIdMsg[] = "Invalid gallery id.";
82const char kMissingEventListener[] = "Missing event listener registration.";
83const char kNonExistentGalleryId[] = "Non-existent gallery id.";
84const char kNoScanPermission[] = "No permission to scan.";
85
86const char kDeviceIdKey[] = "deviceId";
87const char kGalleryIdKey[] = "galleryId";
88const char kIsAvailableKey[] = "isAvailable";
89const char kIsMediaDeviceKey[] = "isMediaDevice";
90const char kIsRemovableKey[] = "isRemovable";
91const char kNameKey[] = "name";
92
93const char kMetadataKey[] = "metadata";
94const char kAttachedImagesBlobInfoKey[] = "attachedImagesBlobInfo";
95const char kBlobUUIDKey[] = "blobUUID";
96const char kTypeKey[] = "type";
97const char kSizeKey[] = "size";
98
99const char kInvalidGalleryId[] = "-1";
100
101MediaFileSystemRegistry* media_file_system_registry() {
102  return g_browser_process->media_file_system_registry();
103}
104
105GalleryWatchManager* gallery_watch_manager() {
106  return media_file_system_registry()->gallery_watch_manager();
107}
108
109MediaScanManager* 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
116bool 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.
131bool 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
150WebContents* 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
167base::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
236bool 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
250class 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
307MediaGalleriesEventRouter::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
320MediaGalleriesEventRouter::~MediaGalleriesEventRouter() {
321}
322
323void 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
334static base::LazyInstance<
335    BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter> > g_factory =
336    LAZY_INSTANCE_INITIALIZER;
337
338// static
339BrowserContextKeyedAPIFactory<MediaGalleriesEventRouter>*
340MediaGalleriesEventRouter::GetFactoryInstance() {
341  return g_factory.Pointer();
342}
343
344// static
345MediaGalleriesEventRouter* 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
353bool MediaGalleriesEventRouter::ExtensionHasGalleryChangeListener(
354    const std::string& extension_id) const {
355  return EventRouter::Get(profile_)->ExtensionHasEventListener(
356      extension_id, MediaGalleries::OnGalleryChanged::kEventName);
357}
358
359bool MediaGalleriesEventRouter::ExtensionHasScanProgressListener(
360    const std::string& extension_id) const {
361  return EventRouter::Get(profile_)->ExtensionHasEventListener(
362      extension_id, MediaGalleries::OnScanProgress::kEventName);
363}
364
365void 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
374void 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
384void 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
400void 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
410void 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
424void 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
435void 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
446void 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///////////////////////////////////////////////////////////////////////////////
457MediaGalleriesGetMediaFileSystemsFunction::
458    ~MediaGalleriesGetMediaFileSystemsFunction() {}
459
460bool 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
477void 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
505void MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog(
506    const std::vector<MediaFileSystemInfo>& /*filesystems*/) {
507  ShowDialog();
508}
509
510void MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries(
511    const std::vector<MediaFileSystemInfo>& filesystems) {
512  if (filesystems.empty())
513    ShowDialog();
514  else
515    ReturnGalleries(filesystems);
516}
517
518void MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries() {
519  GetMediaFileSystemsForExtension(base::Bind(
520      &MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries, this));
521}
522
523void 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
537void 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
552void 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///////////////////////////////////////////////////////////////////////////////
568MediaGalleriesGetAllMediaFileSystemMetadataFunction::
569    ~MediaGalleriesGetAllMediaFileSystemMetadataFunction() {}
570
571bool 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
579void 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
604void 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///////////////////////////////////////////////////////////////////////////////
633MediaGalleriesAddUserSelectedFolderFunction::
634    ~MediaGalleriesAddUserSelectedFolderFunction() {}
635
636bool 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
642void 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
674void 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
703void 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
729void
730MediaGalleriesAddUserSelectedFolderFunction::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///////////////////////////////////////////////////////////////////////////////
745MediaGalleriesDropPermissionForMediaFileSystemFunction::
746    ~MediaGalleriesDropPermissionForMediaFileSystemFunction() {}
747
748bool 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
769void 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///////////////////////////////////////////////////////////////////////////////
791MediaGalleriesStartMediaScanFunction::~MediaGalleriesStartMediaScanFunction() {}
792
793bool 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
804void 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///////////////////////////////////////////////////////////////////////////////
820MediaGalleriesCancelMediaScanFunction::
821    ~MediaGalleriesCancelMediaScanFunction() {
822}
823
824bool 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
835void 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///////////////////////////////////////////////////////////////////////////////
844MediaGalleriesAddScanResultsFunction::~MediaGalleriesAddScanResultsFunction() {}
845
846bool 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
859MediaGalleriesScanResultController*
860MediaGalleriesAddScanResultsFunction::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
869void 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
891void 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
904void 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///////////////////////////////////////////////////////////////////////////////
921MediaGalleriesGetMetadataFunction::~MediaGalleriesGetMetadataFunction() {}
922
923bool 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
940void 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
955void 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
994void 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
1029void 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///////////////////////////////////////////////////////////////////////////////
1091MediaGalleriesAddGalleryWatchFunction::
1092    ~MediaGalleriesAddGalleryWatchFunction() {
1093}
1094
1095bool 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
1116void 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
1143void 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
1176MediaGalleriesRemoveGalleryWatchFunction::
1177    ~MediaGalleriesRemoveGalleryWatchFunction() {
1178}
1179
1180bool 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
1199void 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
1222MediaGalleriesGetAllGalleryWatchFunction::
1223    ~MediaGalleriesGetAllGalleryWatchFunction() {
1224}
1225
1226bool 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
1239void 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
1256MediaGalleriesRemoveAllGalleryWatchFunction::
1257    ~MediaGalleriesRemoveAllGalleryWatchFunction() {
1258}
1259
1260bool 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
1273void MediaGalleriesRemoveAllGalleryWatchFunction::OnPreferencesInit() {
1274  gallery_watch_manager()->RemoveAllWatches(GetProfile(), extension_id());
1275  SendResponse(true);
1276}
1277
1278}  // namespace extensions
1279