media_galleries_api.cc revision ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16
1dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor// Use of this source code is governed by a BSD-style license that can be 3dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor// found in the LICENSE file. 4dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor 5dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor// Implements the Chrome Extensions Media Galleries API. 6dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor 7dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "chrome/browser/extensions/api/media_galleries/media_galleries_api.h" 8dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor 9dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include <set> 10dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include <string> 11dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include <vector> 12dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor 13dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "apps/shell_window.h" 14dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "apps/shell_window_registry.h" 15dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "base/memory/scoped_ptr.h" 16dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "base/platform_file.h" 17dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "base/stl_util.h" 18dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "base/strings/string_number_conversions.h" 19dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "base/values.h" 20dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "chrome/browser/browser_process.h" 21dc8453422bec3bbf70c03920e01498d75783d122Douglas Gregor#include "chrome/browser/media_galleries/media_file_system_registry.h" 22#include "chrome/browser/media_galleries/media_galleries_dialog_controller.h" 23#include "chrome/browser/storage_monitor/storage_monitor.h" 24#include "chrome/browser/ui/chrome_select_file_policy.h" 25#include "chrome/common/extensions/api/media_galleries.h" 26#include "chrome/common/extensions/extension.h" 27#include "chrome/common/extensions/permissions/api_permission.h" 28#include "chrome/common/extensions/permissions/media_galleries_permission.h" 29#include "chrome/common/extensions/permissions/permissions_data.h" 30#include "chrome/common/pref_names.h" 31#include "components/web_modal/web_contents_modal_dialog_manager.h" 32#include "content/public/browser/child_process_security_policy.h" 33#include "content/public/browser/render_process_host.h" 34#include "content/public/browser/render_view_host.h" 35#include "content/public/browser/web_contents.h" 36 37#if defined(OS_WIN) 38#include "base/strings/sys_string_conversions.h" 39#endif 40 41using apps::ShellWindow; 42using chrome::MediaFileSystemInfo; 43using chrome::MediaFileSystemRegistry; 44using chrome::MediaFileSystemsCallback; 45using content::ChildProcessSecurityPolicy; 46using content::WebContents; 47using web_modal::WebContentsModalDialogManager; 48 49namespace MediaGalleries = extensions::api::media_galleries; 50namespace GetMediaFileSystems = MediaGalleries::GetMediaFileSystems; 51 52namespace extensions { 53 54namespace { 55 56const char kDisallowedByPolicy[] = 57 "Media Galleries API is disallowed by policy: "; 58 59const char kDeviceIdKey[] = "deviceId"; 60const char kGalleryIdKey[] = "galleryId"; 61const char kIsMediaDeviceKey[] = "isMediaDevice"; 62const char kIsRemovableKey[] = "isRemovable"; 63const char kNameKey[] = "name"; 64 65// Checks whether the MediaGalleries API is currently accessible (it may be 66// disallowed even if an extension has the requisite permission). 67bool ApiIsAccessible(std::string* error) { 68 if (!ChromeSelectFilePolicy::FileSelectDialogsAllowed()) { 69 *error = std::string(kDisallowedByPolicy) + 70 prefs::kAllowFileSelectionDialogs; 71 return false; 72 } 73 74 return true; 75} 76 77} // namespace 78 79MediaGalleriesGetMediaFileSystemsFunction:: 80 ~MediaGalleriesGetMediaFileSystemsFunction() {} 81 82bool MediaGalleriesGetMediaFileSystemsFunction::RunImpl() { 83 if (!ApiIsAccessible(&error_)) 84 return false; 85 86 scoped_ptr<GetMediaFileSystems::Params> params( 87 GetMediaFileSystems::Params::Create(*args_)); 88 EXTENSION_FUNCTION_VALIDATE(params.get()); 89 MediaGalleries::GetMediaFileSystemsInteractivity interactive = 90 MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO; 91 if (params->details.get() && params->details->interactive != MediaGalleries:: 92 GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE) { 93 interactive = params->details->interactive; 94 } 95 96 chrome::StorageMonitor::GetInstance()->EnsureInitialized(base::Bind( 97 &MediaGalleriesGetMediaFileSystemsFunction::OnStorageMonitorInit, 98 this, 99 interactive)); 100 return true; 101} 102 103void MediaGalleriesGetMediaFileSystemsFunction::OnStorageMonitorInit( 104 MediaGalleries::GetMediaFileSystemsInteractivity interactive) { 105 switch (interactive) { 106 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_YES: { 107 // The MediaFileSystemRegistry only updates preferences for extensions 108 // that it knows are in use. Since this may be the first call to 109 // chrome.getMediaFileSystems for this extension, call 110 // GetMediaFileSystemsForExtension() here solely so that 111 // MediaFileSystemRegistry will send preference changes. 112 GetMediaFileSystemsForExtension(base::Bind( 113 &MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog, this)); 114 return; 115 } 116 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_IF_NEEDED: { 117 GetMediaFileSystemsForExtension(base::Bind( 118 &MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries, 119 this)); 120 return; 121 } 122 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NO: 123 GetAndReturnGalleries(); 124 return; 125 case MediaGalleries::GET_MEDIA_FILE_SYSTEMS_INTERACTIVITY_NONE: 126 NOTREACHED(); 127 } 128 SendResponse(false); 129} 130 131void MediaGalleriesGetMediaFileSystemsFunction::AlwaysShowDialog( 132 const std::vector<MediaFileSystemInfo>& /*filesystems*/) { 133 ShowDialog(); 134} 135 136void MediaGalleriesGetMediaFileSystemsFunction::ShowDialogIfNoGalleries( 137 const std::vector<MediaFileSystemInfo>& filesystems) { 138 if (filesystems.empty()) 139 ShowDialog(); 140 else 141 ReturnGalleries(filesystems); 142} 143 144void MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries() { 145 GetMediaFileSystemsForExtension(base::Bind( 146 &MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries, this)); 147} 148 149void MediaGalleriesGetMediaFileSystemsFunction::ReturnGalleries( 150 const std::vector<MediaFileSystemInfo>& filesystems) { 151 content::RenderViewHost* rvh = render_view_host(); 152 if (!rvh) { 153 SendResponse(false); 154 return; 155 } 156 MediaGalleriesPermission::CheckParam read_param( 157 MediaGalleriesPermission::kReadPermission); 158 bool has_read_permission = PermissionsData::CheckAPIPermissionWithParam( 159 GetExtension(), APIPermission::kMediaGalleries, &read_param); 160 MediaGalleriesPermission::CheckParam copy_to_param( 161 MediaGalleriesPermission::kCopyToPermission); 162 bool has_copy_to_permission = PermissionsData::CheckAPIPermissionWithParam( 163 GetExtension(), APIPermission::kMediaGalleries, ©_to_param); 164 165 const int child_id = rvh->GetProcess()->GetID(); 166 base::ListValue* list = new base::ListValue(); 167 for (size_t i = 0; i < filesystems.size(); i++) { 168 scoped_ptr<base::DictionaryValue> file_system_dict_value( 169 new base::DictionaryValue()); 170 171 // Send the file system id so the renderer can create a valid FileSystem 172 // object. 173 file_system_dict_value->SetStringWithoutPathExpansion( 174 "fsid", filesystems[i].fsid); 175 176 file_system_dict_value->SetStringWithoutPathExpansion( 177 kNameKey, filesystems[i].name); 178 file_system_dict_value->SetStringWithoutPathExpansion( 179 kGalleryIdKey, 180 base::Uint64ToString(filesystems[i].pref_id)); 181 if (!filesystems[i].transient_device_id.empty()) { 182 file_system_dict_value->SetStringWithoutPathExpansion( 183 kDeviceIdKey, filesystems[i].transient_device_id); 184 } 185 file_system_dict_value->SetBooleanWithoutPathExpansion( 186 kIsRemovableKey, filesystems[i].removable); 187 file_system_dict_value->SetBooleanWithoutPathExpansion( 188 kIsMediaDeviceKey, filesystems[i].media_device); 189 190 list->Append(file_system_dict_value.release()); 191 192 if (filesystems[i].path.empty()) 193 continue; 194 195 if (has_read_permission || has_copy_to_permission) { 196 content::ChildProcessSecurityPolicy* policy = 197 ChildProcessSecurityPolicy::GetInstance(); 198 if (has_read_permission) 199 policy->GrantReadFileSystem(child_id, filesystems[i].fsid); 200 if (has_copy_to_permission) 201 policy->GrantCopyIntoFileSystem(child_id, filesystems[i].fsid); 202 } 203 } 204 205 SetResult(list); 206 SendResponse(true); 207} 208 209void MediaGalleriesGetMediaFileSystemsFunction::ShowDialog() { 210 WebContents* contents = WebContents::FromRenderViewHost(render_view_host()); 211 WebContentsModalDialogManager* web_contents_modal_dialog_manager = 212 WebContentsModalDialogManager::FromWebContents(contents); 213 if (!web_contents_modal_dialog_manager) { 214 // If there is no WebContentsModalDialogManager, then this contents is 215 // probably the background page for an app. Try to find a shell window to 216 // host the dialog. 217 ShellWindow* window = apps::ShellWindowRegistry::Get(profile())-> 218 GetCurrentShellWindowForApp(GetExtension()->id()); 219 if (window) { 220 contents = window->web_contents(); 221 } else { 222 // Abort showing the dialog. TODO(estade) Perhaps return an error instead. 223 GetAndReturnGalleries(); 224 return; 225 } 226 } 227 228 // Controller will delete itself. 229 base::Closure cb = base::Bind( 230 &MediaGalleriesGetMediaFileSystemsFunction::GetAndReturnGalleries, this); 231 new chrome::MediaGalleriesDialogController(contents, *GetExtension(), cb); 232} 233 234void MediaGalleriesGetMediaFileSystemsFunction::GetMediaFileSystemsForExtension( 235 const chrome::MediaFileSystemsCallback& cb) { 236 if (!render_view_host()) { 237 cb.Run(std::vector<MediaFileSystemInfo>()); 238 return; 239 } 240 241 DCHECK(chrome::StorageMonitor::GetInstance()->IsInitialized()); 242 MediaFileSystemRegistry* registry = 243 g_browser_process->media_file_system_registry(); 244 registry->GetMediaFileSystemsForExtension( 245 render_view_host(), GetExtension(), cb); 246} 247 248} // namespace extensions 249