Home | History | Annotate | Download | only in media_galleries
      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
   1279