Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(314)

Side by Side Diff: chrome/browser/extensions/api/file_system/chrome_file_system_delegate.cc

Issue 2934143002: Move chrome.fileSystem implementation to //extensions (Closed)
Patch Set: rebase Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/extensions/api/file_system/file_system_api.h" 5 #include "chrome/browser/extensions/api/file_system/chrome_file_system_delegate. h"
6 6
7 #include <stddef.h>
8
9 #include <memory>
10 #include <set>
11 #include <utility> 7 #include <utility>
12 #include <vector> 8 #include <vector>
13 9
14 #include "apps/saved_files_service.h" 10 #include "apps/saved_files_service.h"
15 #include "base/bind.h" 11 #include "base/bind.h"
16 #include "base/callback.h" 12 #include "base/callback.h"
17 #include "base/files/file_path.h" 13 #include "base/files/file_path.h"
18 #include "base/files/file_util.h" 14 #include "base/logging.h"
19 #include "base/macros.h"
20 #include "base/memory/ptr_util.h" 15 #include "base/memory/ptr_util.h"
21 #include "base/path_service.h" 16 #include "base/path_service.h"
22 #include "base/stl_util.h" 17 #include "base/strings/string16.h"
23 #include "base/strings/string_util.h"
24 #include "base/strings/stringprintf.h"
25 #include "base/strings/sys_string_conversions.h"
26 #include "base/strings/utf_string_conversions.h"
27 #include "base/task_scheduler/post_task.h"
28 #include "base/value_conversions.h"
29 #include "base/values.h"
30 #include "build/build_config.h"
31 #include "chrome/browser/extensions/api/file_system/file_entry_picker.h" 18 #include "chrome/browser/extensions/api/file_system/file_entry_picker.h"
32 #include "chrome/browser/platform_util.h" 19 #include "chrome/browser/extensions/chrome_extension_function_details.h"
33 #include "chrome/browser/profiles/profile.h" 20 #include "chrome/browser/profiles/profile.h"
34 #include "chrome/browser/ui/apps/directory_access_confirmation_dialog.h" 21 #include "chrome/browser/ui/apps/directory_access_confirmation_dialog.h"
35 #include "chrome/browser/ui/chrome_select_file_policy.h"
36 #include "chrome/common/chrome_paths.h" 22 #include "chrome/common/chrome_paths.h"
37 #include "chrome/common/extensions/api/file_system.h"
38 #include "chrome/grit/generated_resources.h" 23 #include "chrome/grit/generated_resources.h"
39 #include "content/public/browser/browser_thread.h" 24 #include "content/public/browser/browser_context.h"
40 #include "content/public/browser/child_process_security_policy.h" 25 #include "content/public/browser/child_process_security_policy.h"
41 #include "content/public/browser/render_frame_host.h" 26 #include "content/public/browser/render_frame_host.h"
42 #include "content/public/browser/render_process_host.h" 27 #include "content/public/browser/render_process_host.h"
43 #include "content/public/browser/storage_partition.h" 28 #include "content/public/browser/storage_partition.h"
44 #include "content/public/browser/web_contents.h" 29 #include "content/public/browser/web_contents.h"
45 #include "extensions/browser/api/file_handlers/app_file_handler_util.h" 30 #include "extensions/browser/api/file_handlers/app_file_handler_util.h"
46 #include "extensions/browser/api/file_system/saved_file_entry.h" 31 #include "extensions/browser/api/file_system/saved_files_service_interface.h"
47 #include "extensions/browser/app_window/app_window.h" 32 #include "extensions/browser/app_window/app_window.h"
48 #include "extensions/browser/app_window/app_window_registry.h" 33 #include "extensions/browser/app_window/app_window_registry.h"
34 #include "extensions/browser/extension_function.h"
49 #include "extensions/browser/extension_prefs.h" 35 #include "extensions/browser/extension_prefs.h"
50 #include "extensions/browser/extension_system.h" 36 #include "extensions/browser/extension_system.h"
51 #include "extensions/browser/extension_util.h" 37 #include "extensions/browser/extension_util.h"
52 #include "extensions/browser/granted_file_entry.h" 38 #include "extensions/common/api/file_system.h"
53 #include "extensions/browser/path_util.h" 39 #include "extensions/common/extension.h"
54 #include "extensions/common/permissions/api_permission.h"
55 #include "extensions/common/permissions/permissions_data.h"
56 #include "net/base/mime_util.h"
57 #include "storage/browser/fileapi/external_mount_points.h" 40 #include "storage/browser/fileapi/external_mount_points.h"
58 #include "storage/browser/fileapi/file_system_operation_runner.h"
59 #include "storage/browser/fileapi/isolated_context.h" 41 #include "storage/browser/fileapi/isolated_context.h"
60 #include "storage/common/fileapi/file_system_types.h" 42 #include "storage/common/fileapi/file_system_types.h"
61 #include "storage/common/fileapi/file_system_util.h" 43 #include "storage/common/fileapi/file_system_util.h"
62 #include "ui/base/l10n/l10n_util.h"
63 #include "ui/base/ui_base_types.h"
64 #include "ui/shell_dialogs/select_file_dialog.h" 44 #include "ui/shell_dialogs/select_file_dialog.h"
65 #include "ui/shell_dialogs/select_file_policy.h"
66 45
67 #if defined(OS_MACOSX) 46 #if defined(OS_MACOSX)
68 #include <CoreFoundation/CoreFoundation.h> 47 #include <CoreFoundation/CoreFoundation.h>
69 #include "base/mac/foundation_util.h" 48 #include "base/mac/foundation_util.h"
70 #endif 49 #endif
71 50
72 #if defined(OS_CHROMEOS) 51 #if defined(OS_CHROMEOS)
73 #include "base/strings/string16.h"
74 #include "chrome/browser/chromeos/file_manager/filesystem_api_util.h"
75 #include "chrome/browser/chromeos/file_manager/volume_manager.h" 52 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
53 #include "chrome/browser/extensions/api/file_system/consent_provider.h"
76 #include "extensions/browser/event_router.h" 54 #include "extensions/browser/event_router.h"
77 #include "extensions/browser/extension_registry.h" 55 #include "extensions/browser/extension_registry.h"
78 #include "extensions/common/constants.h" 56 #include "extensions/common/constants.h"
79 #include "url/url_constants.h" 57 #include "url/url_constants.h"
80 #endif 58 #endif
81 59
82 using apps::SavedFilesService; 60 namespace extensions {
83 using storage::IsolatedContext; 61
84 62 namespace file_system = api::file_system;
85 const char kInvalidCallingPage[] = 63
86 "Invalid calling page. " 64 #if defined(OS_CHROMEOS)
87 "This function can't be called from a background page."; 65 using file_system_api::ConsentProvider;
88 const char kUserCancelled[] = "User cancelled"; 66 using file_system_api::ConsentProviderDelegate;
89 const char kWritableFileErrorFormat[] = "Error opening %s"; 67
68 namespace {
69
70 const char kConsentImpossible[] =
71 "Impossible to ask for user consent as there is no app window visible.";
72 const char kNotSupportedOnNonKioskSessionError[] =
73 "Operation only supported for kiosk apps running in a kiosk session.";
90 const char kRequiresFileSystemWriteError[] = 74 const char kRequiresFileSystemWriteError[] =
91 "Operation requires fileSystem.write permission"; 75 "Operation requires fileSystem.write permission";
92 const char kRequiresFileSystemDirectoryError[] = 76 const char kSecurityError[] = "Security error.";
93 "Operation requires fileSystem.directory permission";
94 const char kMultipleUnsupportedError[] =
95 "acceptsMultiple: true is only supported for 'openFile'";
96 const char kUnknownIdError[] = "Unknown id";
97
98 #if !defined(OS_CHROMEOS)
99 const char kNotSupportedOnCurrentPlatformError[] =
100 "Operation not supported on the current platform.";
101 #else
102 const char kNotSupportedOnNonKioskSessionError[] =
103 "Operation only supported for kiosk apps running in a kiosk session.";
104 const char kVolumeNotFoundError[] = "Volume not found."; 77 const char kVolumeNotFoundError[] = "Volume not found.";
105 const char kSecurityError[] = "Security error."; 78
106 const char kConsentImpossible[] =
107 "Impossible to ask for user consent as there is no app window visible.";
108 #endif
109
110 namespace extensions {
111
112 namespace file_system = api::file_system;
113 namespace ChooseEntry = file_system::ChooseEntry;
114
115 namespace {
116
117 bool g_skip_picker_for_test = false;
118 bool g_use_suggested_path_for_test = false;
119 base::FilePath* g_path_to_be_picked_for_test;
120 std::vector<base::FilePath>* g_paths_to_be_picked_for_test;
121 bool g_skip_directory_confirmation_for_test = false;
122 bool g_allow_directory_access_for_test = false;
123
124 // Expand the mime-types and extensions provided in an AcceptOption, returning
125 // them within the passed extension vector. Returns false if no valid types
126 // were found.
127 bool GetFileTypesFromAcceptOption(
128 const file_system::AcceptOption& accept_option,
129 std::vector<base::FilePath::StringType>* extensions,
130 base::string16* description) {
131 std::set<base::FilePath::StringType> extension_set;
132 int description_id = 0;
133
134 if (accept_option.mime_types.get()) {
135 std::vector<std::string>* list = accept_option.mime_types.get();
136 bool valid_type = false;
137 for (std::vector<std::string>::const_iterator iter = list->begin();
138 iter != list->end(); ++iter) {
139 std::vector<base::FilePath::StringType> inner;
140 std::string accept_type = base::ToLowerASCII(*iter);
141 net::GetExtensionsForMimeType(accept_type, &inner);
142 if (inner.empty())
143 continue;
144
145 if (valid_type)
146 description_id = 0; // We already have an accept type with label; if
147 // we find another, give up and use the default.
148 else if (accept_type == "image/*")
149 description_id = IDS_IMAGE_FILES;
150 else if (accept_type == "audio/*")
151 description_id = IDS_AUDIO_FILES;
152 else if (accept_type == "video/*")
153 description_id = IDS_VIDEO_FILES;
154
155 extension_set.insert(inner.begin(), inner.end());
156 valid_type = true;
157 }
158 }
159
160 if (accept_option.extensions.get()) {
161 std::vector<std::string>* list = accept_option.extensions.get();
162 for (std::vector<std::string>::const_iterator iter = list->begin();
163 iter != list->end(); ++iter) {
164 std::string extension = base::ToLowerASCII(*iter);
165 #if defined(OS_WIN)
166 extension_set.insert(base::UTF8ToWide(*iter));
167 #else
168 extension_set.insert(*iter);
169 #endif
170 }
171 }
172
173 extensions->assign(extension_set.begin(), extension_set.end());
174 if (extensions->empty())
175 return false;
176
177 if (accept_option.description.get())
178 *description = base::UTF8ToUTF16(*accept_option.description);
179 else if (description_id)
180 *description = l10n_util::GetStringUTF16(description_id);
181
182 return true;
183 }
184
185 // Key for the path of the directory of the file last chosen by the user in
186 // response to a chrome.fileSystem.chooseEntry() call.
187 const char kLastChooseEntryDirectory[] = "last_choose_file_directory";
188
189 const int kGraylistedPaths[] = {
190 base::DIR_HOME,
191 #if defined(OS_WIN)
192 base::DIR_PROGRAM_FILES, base::DIR_PROGRAM_FILESX86, base::DIR_WINDOWS,
193 #endif
194 };
195
196 typedef base::Callback<void(std::unique_ptr<base::File::Info>)>
197 FileInfoOptCallback;
198
199 // Passes optional file info to the UI thread depending on |result| and |info|.
200 void PassFileInfoToUIThread(const FileInfoOptCallback& callback,
201 base::File::Error result,
202 const base::File::Info& info) {
203 DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
204 std::unique_ptr<base::File::Info> file_info(
205 result == base::File::FILE_OK ? new base::File::Info(info) : NULL);
206 content::BrowserThread::PostTask(
207 content::BrowserThread::UI, FROM_HERE,
208 base::BindOnce(callback, base::Passed(&file_info)));
209 }
210
211 // Gets a WebContents instance handle for a platform app hosted in
212 // |render_frame_host|. If not found, then returns NULL.
213 content::WebContents* GetWebContentsForRenderFrameHost(
214 content::BrowserContext* browser_context,
215 content::RenderFrameHost* render_frame_host) {
216 content::WebContents* web_contents =
217 content::WebContents::FromRenderFrameHost(render_frame_host);
218 // Check if there is an app window associated with the web contents; if not,
219 // return null.
220 return AppWindowRegistry::Get(browser_context)
221 ->GetAppWindowForWebContents(web_contents)
222 ? web_contents
223 : nullptr;
224 }
225
226 #if defined(OS_CHROMEOS)
227 // Fills a list of volumes mounted in the system. 79 // Fills a list of volumes mounted in the system.
228 void FillVolumeList(Profile* profile, 80 void FillVolumeList(content::BrowserContext* browser_context,
229 std::vector<api::file_system::Volume>* result) { 81 std::vector<file_system::Volume>* result) {
230 file_manager::VolumeManager* const volume_manager = 82 file_manager::VolumeManager* const volume_manager =
231 file_manager::VolumeManager::Get(profile); 83 file_manager::VolumeManager::Get(browser_context);
232 DCHECK(volume_manager); 84 DCHECK(volume_manager);
233 85
234 const auto& volume_list = volume_manager->GetVolumeList(); 86 const auto& volume_list = volume_manager->GetVolumeList();
235 // Convert volume_list to result_volume_list. 87 // Convert volume_list to result_volume_list.
236 for (const auto& volume : volume_list) { 88 for (const auto& volume : volume_list) {
237 api::file_system::Volume result_volume; 89 file_system::Volume result_volume;
238 result_volume.volume_id = volume->volume_id(); 90 result_volume.volume_id = volume->volume_id();
239 result_volume.writable = !volume->is_read_only(); 91 result_volume.writable = !volume->is_read_only();
240 result->push_back(std::move(result_volume)); 92 result->push_back(std::move(result_volume));
241 } 93 }
242 } 94 }
243 #endif 95
244 96 // Callback called when consent is granted or denied.
245 // Creates and shows a SelectFileDialog, or returns false if the dialog could 97 void OnConsentReceived(
246 // not be created. 98 content::BrowserContext* browser_context,
247 bool ShowSelectFileDialog( 99 scoped_refptr<UIThreadExtensionFunction> requester,
100 const FileSystemDelegate::FileSystemCallback& success_callback,
101 const FileSystemDelegate::ErrorCallback& error_callback,
102 const std::string& extension_id,
103 const base::WeakPtr<file_manager::Volume>& volume,
104 bool writable,
105 ConsentProvider::Consent result) {
106 using file_manager::VolumeManager;
107 using file_manager::Volume;
108
109 // Render frame host can be gone before this callback method is executed.
110 if (!requester->render_frame_host()) {
111 error_callback.Run(std::string());
112 return;
113 }
114
115 switch (result) {
116 case ConsentProvider::CONSENT_REJECTED:
117 error_callback.Run(kSecurityError);
118 return;
119
120 case ConsentProvider::CONSENT_IMPOSSIBLE:
121 error_callback.Run(kConsentImpossible);
122 return;
123
124 case ConsentProvider::CONSENT_GRANTED:
125 break;
126 }
127
128 if (!volume.get()) {
129 error_callback.Run(kVolumeNotFoundError);
130 return;
131 }
132
133 const GURL site = util::GetSiteForExtensionId(extension_id, browser_context);
134 scoped_refptr<storage::FileSystemContext> file_system_context =
135 content::BrowserContext::GetStoragePartitionForSite(browser_context, site)
136 ->GetFileSystemContext();
137 storage::ExternalFileSystemBackend* const backend =
138 file_system_context->external_backend();
139 DCHECK(backend);
140
141 base::FilePath virtual_path;
142 if (!backend->GetVirtualPath(volume->mount_path(), &virtual_path)) {
143 error_callback.Run(kSecurityError);
144 return;
145 }
146
147 storage::IsolatedContext* const isolated_context =
148 storage::IsolatedContext::GetInstance();
149 DCHECK(isolated_context);
150
151 const storage::FileSystemURL original_url =
152 file_system_context->CreateCrackedFileSystemURL(
153 GURL(std::string(kExtensionScheme) + url::kStandardSchemeSeparator +
154 extension_id),
155 storage::kFileSystemTypeExternal, virtual_path);
156
157 // Set a fixed register name, as the automatic one would leak the mount point
158 // directory.
159 std::string register_name = "fs";
160 const std::string file_system_id =
161 isolated_context->RegisterFileSystemForPath(
162 storage::kFileSystemTypeNativeForPlatformApp,
163 std::string() /* file_system_id */, original_url.path(),
164 &register_name);
165 if (file_system_id.empty()) {
166 error_callback.Run(kSecurityError);
167 return;
168 }
169
170 backend->GrantFileAccessToExtension(extension_id, virtual_path);
171
172 // Grant file permissions to the renderer hosting component.
173 content::ChildProcessSecurityPolicy* policy =
174 content::ChildProcessSecurityPolicy::GetInstance();
175 DCHECK(policy);
176
177 const auto process_id = requester->render_frame_host()->GetProcess()->GetID();
178 // Read-only permisisons.
179 policy->GrantReadFile(process_id, volume->mount_path());
180 policy->GrantReadFileSystem(process_id, file_system_id);
181
182 // Additional write permissions.
183 if (writable) {
184 policy->GrantCreateReadWriteFile(process_id, volume->mount_path());
185 policy->GrantCopyInto(process_id, volume->mount_path());
186 policy->GrantWriteFileSystem(process_id, file_system_id);
187 policy->GrantDeleteFromFileSystem(process_id, file_system_id);
188 policy->GrantCreateFileForFileSystem(process_id, file_system_id);
189 }
190
191 success_callback.Run(file_system_id, register_name);
192 }
193
194 } // namespace
195
196 namespace file_system_api {
197
198 void DispatchVolumeListChangeEvent(content::BrowserContext* browser_context) {
199 DCHECK(browser_context);
200 EventRouter* const event_router = EventRouter::Get(browser_context);
201 if (!event_router) // Possible on shutdown.
202 return;
203
204 ExtensionRegistry* const registry = ExtensionRegistry::Get(browser_context);
205 if (!registry) // Possible on shutdown.
206 return;
207
208 ConsentProviderDelegate consent_provider_delegate(
209 Profile::FromBrowserContext(browser_context));
210 ConsentProvider consent_provider(&consent_provider_delegate);
211 file_system::VolumeListChangedEvent event_args;
212 FillVolumeList(browser_context, &event_args.volumes);
213 for (const auto& extension : registry->enabled_extensions()) {
214 if (!consent_provider.IsGrantable(*extension.get()))
215 continue;
216 event_router->DispatchEventToExtension(
217 extension->id(),
218 base::MakeUnique<Event>(
219 events::FILE_SYSTEM_ON_VOLUME_LIST_CHANGED,
220 file_system::OnVolumeListChanged::kEventName,
221 file_system::OnVolumeListChanged::Create(event_args)));
222 }
223 }
224
225 } // namespace file_system_api
226 #endif // defined(OS_CHROMEOS)
227
228 ChromeFileSystemDelegate::ChromeFileSystemDelegate() {}
229
230 ChromeFileSystemDelegate::~ChromeFileSystemDelegate() {}
231
232 base::FilePath ChromeFileSystemDelegate::GetDefaultDirectory() {
233 base::FilePath documents_dir;
234 PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir);
235 return documents_dir;
236 }
237
238 bool ChromeFileSystemDelegate::ShowSelectFileDialog(
248 scoped_refptr<UIThreadExtensionFunction> extension_function, 239 scoped_refptr<UIThreadExtensionFunction> extension_function,
249 ui::SelectFileDialog::Type type, 240 ui::SelectFileDialog::Type type,
250 const base::FilePath& default_path, 241 const base::FilePath& default_path,
251 const ui::SelectFileDialog::FileTypeInfo* file_types, 242 const ui::SelectFileDialog::FileTypeInfo* file_types,
252 FileEntryPicker::FilesSelectedCallback files_selected_callback, 243 FileSystemDelegate::FilesSelectedCallback files_selected_callback,
253 base::OnceClosure file_selection_canceled_callback) { 244 base::OnceClosure file_selection_canceled_callback) {
245 const Extension* extension = extension_function->extension();
246 content::WebContents* web_contents = nullptr;
247
254 // TODO(asargent/benwells) - As a short term remediation for 248 // TODO(asargent/benwells) - As a short term remediation for
255 // crbug.com/179010 we're adding the ability for a whitelisted extension to 249 // crbug.com/179010 we're adding the ability for a whitelisted extension to
256 // use this API since chrome.fileBrowserHandler.selectFile is ChromeOS-only. 250 // use this API since chrome.fileBrowserHandler.selectFile is ChromeOS-only.
257 // Eventually we'd like a better solution and likely this code will go back 251 // Eventually we'd like a better solution and likely this code will go back
258 // to being platform-app only. 252 // to being platform-app only.
259 content::WebContents* const web_contents = 253 if (extension->is_platform_app()) {
260 extension_function->extension()->is_platform_app() 254 content::WebContents* candidate = content::WebContents::FromRenderFrameHost(
261 ? GetWebContentsForRenderFrameHost( 255 extension_function->render_frame_host());
262 extension_function->browser_context(), 256 // Make sure there is an app window associated with the web contents.
263 extension_function->render_frame_host()) 257 if (AppWindowRegistry::Get(extension_function->browser_context())
264 : extension_function->GetAssociatedWebContents(); 258 ->GetAppWindowForWebContents(candidate)) {
259 web_contents = candidate;
260 }
261 } else {
262 // TODO(michaelpg): As a workaround for crbug.com/736930, allow this to work
263 // from a background page by using the the extended GetAssociatedWebContents
264 // function provided by ChromeExtensionFunctionDetails. This is safe because
265 // only whitelisted extensions can access the chrome.fileSystem API;
266 // platform apps cannot open the file picker from a background page.
267 web_contents = ChromeExtensionFunctionDetails(extension_function.get())
268 .GetAssociatedWebContents();
269 }
270
265 if (!web_contents) 271 if (!web_contents)
266 return false; 272 return false;
267 273
268 // The file picker will hold a reference to the UIThreadExtensionFunction 274 // The file picker will hold a reference to the UIThreadExtensionFunction
269 // instance, preventing its destruction (and subsequent sending of the 275 // instance, preventing its destruction (and subsequent sending of the
270 // function response) until the user has selected a file or cancelled the 276 // function response) until the user has selected a file or cancelled the
271 // picker. At that point, the picker will delete itself, which will also free 277 // picker. At that point, the picker will delete itself, which will also free
272 // the function instance. 278 // the function instance.
273 new FileEntryPicker(web_contents, default_path, *file_types, type, 279 new FileEntryPicker(web_contents, default_path, *file_types, type,
274 std::move(files_selected_callback), 280 std::move(files_selected_callback),
275 std::move(file_selection_canceled_callback)); 281 std::move(file_selection_canceled_callback));
276 return true; 282 return true;
277 } 283 }
278 284
279 } // namespace 285 void ChromeFileSystemDelegate::ConfirmSensitiveDirectoryAccess(
280 286 bool has_write_permission,
281 namespace file_system_api { 287 const base::string16& app_name,
282 288 content::WebContents* web_contents,
283 base::FilePath GetLastChooseEntryDirectory(const ExtensionPrefs* prefs, 289 const base::Closure& on_accept,
284 const std::string& extension_id) { 290 const base::Closure& on_cancel) {
285 base::FilePath path; 291 CreateDirectoryAccessConfirmationDialog(has_write_permission, app_name,
286 std::string string_path; 292 web_contents, on_accept, on_cancel);
287 if (prefs->ReadPrefAsString(extension_id, kLastChooseEntryDirectory, 293 }
288 &string_path)) { 294
289 path = base::FilePath::FromUTF8Unsafe(string_path); 295 int ChromeFileSystemDelegate::GetDescriptionIdForAcceptType(
290 } 296 const std::string& accept_type) {
291 return path; 297 if (accept_type == "image/*")
292 } 298 return IDS_IMAGE_FILES;
293 299 if (accept_type == "audio/*")
294 void SetLastChooseEntryDirectory(ExtensionPrefs* prefs, 300 return IDS_AUDIO_FILES;
295 const std::string& extension_id, 301 if (accept_type == "video/*")
296 const base::FilePath& path) { 302 return IDS_VIDEO_FILES;
297 prefs->UpdateExtensionPref(extension_id, kLastChooseEntryDirectory, 303 return 0;
298 base::CreateFilePathValue(path));
299 } 304 }
300 305
301 #if defined(OS_CHROMEOS) 306 #if defined(OS_CHROMEOS)
302 void DispatchVolumeListChangeEvent(Profile* profile) { 307 bool ChromeFileSystemDelegate::IsGrantable(
303 DCHECK(profile); 308 content::BrowserContext* browser_context,
304 EventRouter* const event_router = EventRouter::Get(profile); 309 content::RenderFrameHost* render_frame_host,
305 if (!event_router) // Possible on shutdown. 310 const Extension& extension) {
306 return;
307
308 ExtensionRegistry* const registry = ExtensionRegistry::Get(profile);
309 if (!registry) // Possible on shutdown.
310 return;
311
312 ConsentProviderDelegate consent_provider_delegate(profile, nullptr);
313 ConsentProvider consent_provider(&consent_provider_delegate);
314 api::file_system::VolumeListChangedEvent event_args;
315 FillVolumeList(profile, &event_args.volumes);
316 for (const auto& extension : registry->enabled_extensions()) {
317 if (!consent_provider.IsGrantable(*extension.get()))
318 continue;
319 event_router->DispatchEventToExtension(
320 extension->id(),
321 base::MakeUnique<Event>(
322 events::FILE_SYSTEM_ON_VOLUME_LIST_CHANGED,
323 api::file_system::OnVolumeListChanged::kEventName,
324 api::file_system::OnVolumeListChanged::Create(event_args)));
325 }
326 }
327 #endif
328
329 } // namespace file_system_api
330
331 #if defined(OS_CHROMEOS)
332 using file_system_api::ConsentProvider;
333 #endif
334
335 ExtensionFunction::ResponseAction FileSystemGetDisplayPathFunction::Run() {
336 std::string filesystem_name;
337 std::string filesystem_path;
338 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
339 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
340
341 base::FilePath file_path;
342 std::string error;
343 if (!app_file_handler_util::ValidateFileEntryAndGetPath(
344 filesystem_name, filesystem_path,
345 render_frame_host()->GetProcess()->GetID(), &file_path, &error)) {
346 return RespondNow(Error(error));
347 }
348
349 file_path = path_util::PrettifyPath(file_path);
350 return RespondNow(
351 OneArgument(base::MakeUnique<base::Value>(file_path.value())));
352 }
353
354 FileSystemEntryFunction::FileSystemEntryFunction()
355 : multiple_(false), is_directory_(false) {}
356
357 void FileSystemEntryFunction::PrepareFilesForWritableApp(
358 const std::vector<base::FilePath>& paths) {
359 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
360 // TODO(cmihail): Path directory set should be initialized only with the
361 // paths that are actually directories, but for now we will consider
362 // all paths directories in case is_directory_ is true, otherwise
363 // all paths files, as this was the previous logic.
364 std::set<base::FilePath> path_directory_set_ =
365 is_directory_ ? std::set<base::FilePath>(paths.begin(), paths.end())
366 : std::set<base::FilePath>{};
367 app_file_handler_util::PrepareFilesForWritableApp(
368 paths, GetProfile(), path_directory_set_,
369 base::Bind(&FileSystemEntryFunction::RegisterFileSystemsAndSendResponse,
370 this, paths),
371 base::Bind(&FileSystemEntryFunction::HandleWritableFileError, this));
372 }
373
374 void FileSystemEntryFunction::RegisterFileSystemsAndSendResponse(
375 const std::vector<base::FilePath>& paths) {
376 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
377 if (!render_frame_host())
378 return;
379
380 std::unique_ptr<base::DictionaryValue> result = CreateResult();
381 for (const auto& path : paths)
382 AddEntryToResult(path, std::string(), result.get());
383 SetResult(std::move(result));
384 SendResponse(true);
385 }
386
387 std::unique_ptr<base::DictionaryValue> FileSystemEntryFunction::CreateResult() {
388 std::unique_ptr<base::DictionaryValue> result(new base::DictionaryValue());
389 result->Set("entries", base::MakeUnique<base::ListValue>());
390 result->SetBoolean("multiple", multiple_);
391 return result;
392 }
393
394 void FileSystemEntryFunction::AddEntryToResult(const base::FilePath& path,
395 const std::string& id_override,
396 base::DictionaryValue* result) {
397 GrantedFileEntry file_entry = app_file_handler_util::CreateFileEntry(
398 GetProfile(), extension(), render_frame_host()->GetProcess()->GetID(),
399 path, is_directory_);
400 base::ListValue* entries;
401 bool success = result->GetList("entries", &entries);
402 DCHECK(success);
403
404 std::unique_ptr<base::DictionaryValue> entry(new base::DictionaryValue());
405 entry->SetString("fileSystemId", file_entry.filesystem_id);
406 entry->SetString("baseName", file_entry.registered_name);
407 if (id_override.empty())
408 entry->SetString("id", file_entry.id);
409 else
410 entry->SetString("id", id_override);
411 entry->SetBoolean("isDirectory", is_directory_);
412 entries->Append(std::move(entry));
413 }
414
415 void FileSystemEntryFunction::HandleWritableFileError(
416 const base::FilePath& error_path) {
417 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
418 error_ = base::StringPrintf(kWritableFileErrorFormat,
419 error_path.BaseName().AsUTF8Unsafe().c_str());
420 SendResponse(false);
421 }
422
423 bool FileSystemGetWritableEntryFunction::RunAsync() {
424 std::string filesystem_name;
425 std::string filesystem_path;
426 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
427 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
428
429 if (!app_file_handler_util::HasFileSystemWritePermission(extension_.get())) {
430 error_ = kRequiresFileSystemWriteError;
431 return false;
432 }
433
434 if (!app_file_handler_util::ValidateFileEntryAndGetPath(
435 filesystem_name, filesystem_path,
436 render_frame_host()->GetProcess()->GetID(), &path_, &error_))
437 return false;
438
439 base::PostTaskWithTraitsAndReply(
440 FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
441 base::BindOnce(&FileSystemGetWritableEntryFunction::SetIsDirectoryAsync,
442 this),
443 base::BindOnce(
444 &FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse,
445 this));
446 return true;
447 }
448
449 void FileSystemGetWritableEntryFunction::CheckPermissionAndSendResponse() {
450 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
451 if (is_directory_ && !extension_->permissions_data()->HasAPIPermission(
452 APIPermission::kFileSystemDirectory)) {
453 error_ = kRequiresFileSystemDirectoryError;
454 SendResponse(false);
455 }
456 std::vector<base::FilePath> paths;
457 paths.push_back(path_);
458 PrepareFilesForWritableApp(paths);
459 }
460
461 void FileSystemGetWritableEntryFunction::SetIsDirectoryAsync() {
462 if (base::DirectoryExists(path_)) {
463 is_directory_ = true;
464 }
465 }
466
467 ExtensionFunction::ResponseAction FileSystemIsWritableEntryFunction::Run() {
468 std::string filesystem_name;
469 std::string filesystem_path;
470 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &filesystem_name));
471 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_path));
472
473 std::string filesystem_id;
474 if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id))
475 return RespondNow(Error(app_file_handler_util::kInvalidParameters));
476
477 content::ChildProcessSecurityPolicy* policy =
478 content::ChildProcessSecurityPolicy::GetInstance();
479 int renderer_id = render_frame_host()->GetProcess()->GetID();
480 bool is_writable = policy->CanReadWriteFileSystem(renderer_id, filesystem_id);
481
482 return RespondNow(OneArgument(base::MakeUnique<base::Value>(is_writable)));
483 }
484
485 void FileSystemChooseEntryFunction::ShowPicker(
486 const ui::SelectFileDialog::FileTypeInfo& file_type_info,
487 ui::SelectFileDialog::Type picker_type) {
488 if (g_skip_picker_for_test) {
489 std::vector<base::FilePath> test_paths;
490 if (g_use_suggested_path_for_test)
491 test_paths.push_back(initial_path_);
492 else if (g_path_to_be_picked_for_test)
493 test_paths.push_back(*g_path_to_be_picked_for_test);
494 else if (g_paths_to_be_picked_for_test)
495 test_paths = *g_paths_to_be_picked_for_test;
496
497 content::BrowserThread::PostTask(
498 content::BrowserThread::UI, FROM_HERE,
499 test_paths.size() > 0
500 ? base::BindOnce(&FileSystemChooseEntryFunction::FilesSelected,
501 this, test_paths)
502 : base::BindOnce(
503 &FileSystemChooseEntryFunction::FileSelectionCanceled, this));
504 return;
505 }
506
507 // The callbacks passed to the dialog will retain references to this
508 // UIThreadExtenisonFunction, preventing its destruction (and subsequent
509 // sending of the function response) until the user has selected a file or
510 // cancelled the picker.
511 if (!ShowSelectFileDialog(
512 this, picker_type, initial_path_, &file_type_info,
513 base::BindOnce(&FileSystemChooseEntryFunction::FilesSelected, this),
514 base::BindOnce(&FileSystemChooseEntryFunction::FileSelectionCanceled,
515 this))) {
516 error_ = kInvalidCallingPage;
517 SendResponse(false);
518 }
519 }
520
521 // static
522 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathForTest(
523 base::FilePath* path) {
524 g_skip_picker_for_test = true;
525 g_use_suggested_path_for_test = false;
526 g_path_to_be_picked_for_test = path;
527 g_paths_to_be_picked_for_test = NULL;
528 }
529
530 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysSelectPathsForTest(
531 std::vector<base::FilePath>* paths) {
532 g_skip_picker_for_test = true;
533 g_use_suggested_path_for_test = false;
534 g_paths_to_be_picked_for_test = paths;
535 }
536
537 // static
538 void FileSystemChooseEntryFunction::SkipPickerAndSelectSuggestedPathForTest() {
539 g_skip_picker_for_test = true;
540 g_use_suggested_path_for_test = true;
541 g_path_to_be_picked_for_test = NULL;
542 g_paths_to_be_picked_for_test = NULL;
543 }
544
545 // static
546 void FileSystemChooseEntryFunction::SkipPickerAndAlwaysCancelForTest() {
547 g_skip_picker_for_test = true;
548 g_use_suggested_path_for_test = false;
549 g_path_to_be_picked_for_test = NULL;
550 g_paths_to_be_picked_for_test = NULL;
551 }
552
553 // static
554 void FileSystemChooseEntryFunction::StopSkippingPickerForTest() {
555 g_skip_picker_for_test = false;
556 }
557
558 // static
559 void FileSystemChooseEntryFunction::SkipDirectoryConfirmationForTest() {
560 g_skip_directory_confirmation_for_test = true;
561 g_allow_directory_access_for_test = true;
562 }
563
564 // static
565 void FileSystemChooseEntryFunction::AutoCancelDirectoryConfirmationForTest() {
566 g_skip_directory_confirmation_for_test = true;
567 g_allow_directory_access_for_test = false;
568 }
569
570 // static
571 void FileSystemChooseEntryFunction::StopSkippingDirectoryConfirmationForTest() {
572 g_skip_directory_confirmation_for_test = false;
573 }
574
575 // static
576 void FileSystemChooseEntryFunction::RegisterTempExternalFileSystemForTest(
577 const std::string& name,
578 const base::FilePath& path) {
579 // For testing on Chrome OS, where to deal with remote and local paths
580 // smoothly, all accessed paths need to be registered in the list of
581 // external mount points.
582 storage::ExternalMountPoints::GetSystemInstance()->RegisterFileSystem(
583 name, storage::kFileSystemTypeNativeLocal,
584 storage::FileSystemMountOption(), path);
585 }
586
587 void FileSystemChooseEntryFunction::FilesSelected(
588 const std::vector<base::FilePath>& paths) {
589 DCHECK(!paths.empty());
590 base::FilePath last_choose_directory;
591 if (is_directory_) {
592 last_choose_directory = paths[0];
593 } else {
594 last_choose_directory = paths[0].DirName();
595 }
596 file_system_api::SetLastChooseEntryDirectory(
597 ExtensionPrefs::Get(GetProfile()), extension()->id(),
598 last_choose_directory);
599 if (is_directory_) {
600 // Get the WebContents for the app window to be the parent window of the
601 // confirmation dialog if necessary.
602 content::WebContents* const web_contents =
603 GetWebContentsForRenderFrameHost(GetProfile(), render_frame_host());
604 if (!web_contents) {
605 error_ = kInvalidCallingPage;
606 SendResponse(false);
607 return;
608 }
609
610 DCHECK_EQ(paths.size(), 1u);
611 bool non_native_path = false;
612 #if defined(OS_CHROMEOS)
613 non_native_path =
614 file_manager::util::IsUnderNonNativeLocalPath(GetProfile(), paths[0]);
615 #endif
616
617 base::PostTaskWithTraits(
618 FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
619 base::BindOnce(
620 &FileSystemChooseEntryFunction::ConfirmDirectoryAccessAsync, this,
621 non_native_path, paths, web_contents));
622 return;
623 }
624
625 OnDirectoryAccessConfirmed(paths);
626 }
627
628 void FileSystemChooseEntryFunction::FileSelectionCanceled() {
629 error_ = kUserCancelled;
630 SendResponse(false);
631 }
632
633 void FileSystemChooseEntryFunction::ConfirmDirectoryAccessAsync(
634 bool non_native_path,
635 const std::vector<base::FilePath>& paths,
636 content::WebContents* web_contents) {
637 const base::FilePath check_path =
638 non_native_path ? paths[0] : base::MakeAbsoluteFilePath(paths[0]);
639 if (check_path.empty()) {
640 content::BrowserThread::PostTask(
641 content::BrowserThread::UI, FROM_HERE,
642 base::BindOnce(&FileSystemChooseEntryFunction::FileSelectionCanceled,
643 this));
644 return;
645 }
646
647 for (size_t i = 0; i < arraysize(kGraylistedPaths); i++) {
648 base::FilePath graylisted_path;
649 if (PathService::Get(kGraylistedPaths[i], &graylisted_path) &&
650 (check_path == graylisted_path ||
651 check_path.IsParent(graylisted_path))) {
652 if (g_skip_directory_confirmation_for_test) {
653 if (g_allow_directory_access_for_test) {
654 break;
655 } else {
656 content::BrowserThread::PostTask(
657 content::BrowserThread::UI, FROM_HERE,
658 base::BindOnce(
659 &FileSystemChooseEntryFunction::FileSelectionCanceled, this));
660 }
661 return;
662 }
663
664 content::BrowserThread::PostTask(
665 content::BrowserThread::UI, FROM_HERE,
666 base::BindOnce(
667 CreateDirectoryAccessConfirmationDialog,
668 app_file_handler_util::HasFileSystemWritePermission(
669 extension_.get()),
670 base::UTF8ToUTF16(extension_->name()), web_contents,
671 base::Bind(
672 &FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed,
673 this, paths),
674 base::Bind(&FileSystemChooseEntryFunction::FileSelectionCanceled,
675 this)));
676 return;
677 }
678 }
679
680 content::BrowserThread::PostTask(
681 content::BrowserThread::UI, FROM_HERE,
682 base::BindOnce(&FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed,
683 this, paths));
684 }
685
686 void FileSystemChooseEntryFunction::OnDirectoryAccessConfirmed(
687 const std::vector<base::FilePath>& paths) {
688 if (app_file_handler_util::HasFileSystemWritePermission(extension_.get())) {
689 PrepareFilesForWritableApp(paths);
690 return;
691 }
692
693 // Don't need to check the file, it's for reading.
694 RegisterFileSystemsAndSendResponse(paths);
695 }
696
697 void FileSystemChooseEntryFunction::BuildFileTypeInfo(
698 ui::SelectFileDialog::FileTypeInfo* file_type_info,
699 const base::FilePath::StringType& suggested_extension,
700 const AcceptOptions* accepts,
701 const bool* acceptsAllTypes) {
702 file_type_info->include_all_files = true;
703 if (acceptsAllTypes)
704 file_type_info->include_all_files = *acceptsAllTypes;
705
706 bool need_suggestion =
707 !file_type_info->include_all_files && !suggested_extension.empty();
708
709 if (accepts) {
710 for (const file_system::AcceptOption& option : *accepts) {
711 base::string16 description;
712 std::vector<base::FilePath::StringType> extensions;
713
714 if (!GetFileTypesFromAcceptOption(option, &extensions, &description))
715 continue; // No extensions were found.
716
717 file_type_info->extensions.push_back(extensions);
718 file_type_info->extension_description_overrides.push_back(description);
719
720 // If we still need to find suggested_extension, hunt for it inside the
721 // extensions returned from GetFileTypesFromAcceptOption.
722 if (need_suggestion &&
723 base::ContainsValue(extensions, suggested_extension)) {
724 need_suggestion = false;
725 }
726 }
727 }
728
729 // If there's nothing in our accepted extension list or we couldn't find the
730 // suggested extension required, then default to accepting all types.
731 if (file_type_info->extensions.empty() || need_suggestion)
732 file_type_info->include_all_files = true;
733 }
734
735 void FileSystemChooseEntryFunction::BuildSuggestion(
736 const std::string* opt_name,
737 base::FilePath* suggested_name,
738 base::FilePath::StringType* suggested_extension) {
739 if (opt_name) {
740 *suggested_name = base::FilePath::FromUTF8Unsafe(*opt_name);
741
742 // Don't allow any path components; shorten to the base name. This should
743 // result in a relative path, but in some cases may not. Clear the
744 // suggestion for safety if this is the case.
745 *suggested_name = suggested_name->BaseName();
746 if (suggested_name->IsAbsolute())
747 *suggested_name = base::FilePath();
748
749 *suggested_extension = suggested_name->Extension();
750 if (!suggested_extension->empty())
751 suggested_extension->erase(suggested_extension->begin()); // drop the .
752 }
753 }
754
755 void FileSystemChooseEntryFunction::SetInitialPathAndShowPicker(
756 const base::FilePath& previous_path,
757 const base::FilePath& suggested_name,
758 const ui::SelectFileDialog::FileTypeInfo& file_type_info,
759 ui::SelectFileDialog::Type picker_type,
760 bool is_previous_path_directory) {
761 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
762 if (is_previous_path_directory) {
763 initial_path_ = previous_path.Append(suggested_name);
764 } else {
765 base::FilePath documents_dir;
766 if (PathService::Get(chrome::DIR_USER_DOCUMENTS, &documents_dir)) {
767 initial_path_ = documents_dir.Append(suggested_name);
768 } else {
769 initial_path_ = suggested_name;
770 }
771 }
772 ShowPicker(file_type_info, picker_type);
773 }
774
775 bool FileSystemChooseEntryFunction::RunAsync() {
776 std::unique_ptr<ChooseEntry::Params> params(
777 ChooseEntry::Params::Create(*args_));
778 EXTENSION_FUNCTION_VALIDATE(params.get());
779
780 base::FilePath suggested_name;
781 ui::SelectFileDialog::FileTypeInfo file_type_info;
782 ui::SelectFileDialog::Type picker_type =
783 ui::SelectFileDialog::SELECT_OPEN_FILE;
784
785 file_system::ChooseEntryOptions* options = params->options.get();
786 if (options) {
787 multiple_ = options->accepts_multiple && *options->accepts_multiple;
788 if (multiple_)
789 picker_type = ui::SelectFileDialog::SELECT_OPEN_MULTI_FILE;
790
791 if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENWRITABLEFILE &&
792 !app_file_handler_util::HasFileSystemWritePermission(
793 extension_.get())) {
794 error_ = kRequiresFileSystemWriteError;
795 return false;
796 } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_SAVEFILE) {
797 if (!app_file_handler_util::HasFileSystemWritePermission(
798 extension_.get())) {
799 error_ = kRequiresFileSystemWriteError;
800 return false;
801 }
802 if (multiple_) {
803 error_ = kMultipleUnsupportedError;
804 return false;
805 }
806 picker_type = ui::SelectFileDialog::SELECT_SAVEAS_FILE;
807 } else if (options->type == file_system::CHOOSE_ENTRY_TYPE_OPENDIRECTORY) {
808 is_directory_ = true;
809 if (!extension_->permissions_data()->HasAPIPermission(
810 APIPermission::kFileSystemDirectory)) {
811 error_ = kRequiresFileSystemDirectoryError;
812 return false;
813 }
814 if (multiple_) {
815 error_ = kMultipleUnsupportedError;
816 return false;
817 }
818 picker_type = ui::SelectFileDialog::SELECT_FOLDER;
819 }
820
821 base::FilePath::StringType suggested_extension;
822 BuildSuggestion(options->suggested_name.get(), &suggested_name,
823 &suggested_extension);
824
825 BuildFileTypeInfo(&file_type_info, suggested_extension,
826 options->accepts.get(), options->accepts_all_types.get());
827 }
828
829 file_type_info.allowed_paths = ui::SelectFileDialog::FileTypeInfo::ANY_PATH;
830
831 base::FilePath previous_path = file_system_api::GetLastChooseEntryDirectory(
832 ExtensionPrefs::Get(GetProfile()), extension()->id());
833
834 if (previous_path.empty()) {
835 SetInitialPathAndShowPicker(previous_path, suggested_name, file_type_info,
836 picker_type, false);
837 return true;
838 }
839
840 base::Callback<void(bool)> set_initial_path_callback = base::Bind(
841 &FileSystemChooseEntryFunction::SetInitialPathAndShowPicker, this,
842 previous_path, suggested_name, file_type_info, picker_type);
843
844 // Check whether the |previous_path| is a non-native directory.
845 #if defined(OS_CHROMEOS)
846 if (file_manager::util::IsUnderNonNativeLocalPath(GetProfile(),
847 previous_path)) {
848 file_manager::util::IsNonNativeLocalPathDirectory(
849 GetProfile(), previous_path, set_initial_path_callback);
850 return true;
851 }
852 #endif
853 base::PostTaskWithTraitsAndReplyWithResult(
854 FROM_HERE, {base::MayBlock(), base::TaskPriority::BACKGROUND},
855 base::Bind(&base::DirectoryExists, previous_path),
856 set_initial_path_callback);
857
858 return true;
859 }
860
861 bool FileSystemRetainEntryFunction::RunAsync() {
862 std::string entry_id;
863 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
864 SavedFilesService* saved_files_service = SavedFilesService::Get(GetProfile());
865 // Add the file to the retain list if it is not already on there.
866 if (!saved_files_service->IsRegistered(extension_->id(), entry_id)) {
867 std::string filesystem_name;
868 std::string filesystem_path;
869 base::FilePath path;
870 EXTENSION_FUNCTION_VALIDATE(args_->GetString(1, &filesystem_name));
871 EXTENSION_FUNCTION_VALIDATE(args_->GetString(2, &filesystem_path));
872 if (!app_file_handler_util::ValidateFileEntryAndGetPath(
873 filesystem_name, filesystem_path,
874 render_frame_host()->GetProcess()->GetID(), &path, &error_)) {
875 return false;
876 }
877
878 std::string filesystem_id;
879 if (!storage::CrackIsolatedFileSystemName(filesystem_name, &filesystem_id))
880 return false;
881
882 const GURL site = util::GetSiteForExtensionId(extension_id(), GetProfile());
883 storage::FileSystemContext* const context =
884 content::BrowserContext::GetStoragePartitionForSite(GetProfile(), site)
885 ->GetFileSystemContext();
886 const storage::FileSystemURL url = context->CreateCrackedFileSystemURL(
887 site, storage::kFileSystemTypeIsolated,
888 IsolatedContext::GetInstance()
889 ->CreateVirtualRootPath(filesystem_id)
890 .Append(base::FilePath::FromUTF8Unsafe(filesystem_path)));
891
892 content::BrowserThread::PostTask(
893 content::BrowserThread::IO, FROM_HERE,
894 base::BindOnce(
895 base::IgnoreResult(
896 &storage::FileSystemOperationRunner::GetMetadata),
897 context->operation_runner()->AsWeakPtr(), url,
898 storage::FileSystemOperation::GET_METADATA_FIELD_IS_DIRECTORY,
899 base::Bind(
900 &PassFileInfoToUIThread,
901 base::Bind(&FileSystemRetainEntryFunction::RetainFileEntry,
902 this, entry_id, path))));
903 return true;
904 }
905
906 saved_files_service->EnqueueFileEntry(extension_->id(), entry_id);
907 SendResponse(true);
908 return true;
909 }
910
911 void FileSystemRetainEntryFunction::RetainFileEntry(
912 const std::string& entry_id,
913 const base::FilePath& path,
914 std::unique_ptr<base::File::Info> file_info) {
915 if (!file_info) {
916 SendResponse(false);
917 return;
918 }
919
920 SavedFilesService* saved_files_service = SavedFilesService::Get(GetProfile());
921 saved_files_service->RegisterFileEntry(extension_->id(), entry_id, path,
922 file_info->is_directory);
923 saved_files_service->EnqueueFileEntry(extension_->id(), entry_id);
924 SendResponse(true);
925 }
926
927 ExtensionFunction::ResponseAction FileSystemIsRestorableFunction::Run() {
928 std::string entry_id;
929 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
930 return RespondNow(OneArgument(base::MakeUnique<base::Value>(
931 SavedFilesService::Get(Profile::FromBrowserContext(browser_context()))
932 ->IsRegistered(extension_->id(), entry_id))));
933 }
934
935 bool FileSystemRestoreEntryFunction::RunAsync() {
936 std::string entry_id;
937 bool needs_new_entry;
938 EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &entry_id));
939 EXTENSION_FUNCTION_VALIDATE(args_->GetBoolean(1, &needs_new_entry));
940 const SavedFileEntry* file_entry =
941 SavedFilesService::Get(GetProfile())
942 ->GetFileEntry(extension_->id(), entry_id);
943 if (!file_entry) {
944 error_ = kUnknownIdError;
945 return false;
946 }
947
948 SavedFilesService::Get(GetProfile())
949 ->EnqueueFileEntry(extension_->id(), entry_id);
950
951 // Only create a new file entry if the renderer requests one.
952 // |needs_new_entry| will be false if the renderer already has an Entry for
953 // |entry_id|.
954 if (needs_new_entry) {
955 is_directory_ = file_entry->is_directory;
956 std::unique_ptr<base::DictionaryValue> result = CreateResult();
957 AddEntryToResult(file_entry->path, file_entry->id, result.get());
958 SetResult(std::move(result));
959 }
960 SendResponse(true);
961 return true;
962 }
963
964 ExtensionFunction::ResponseAction FileSystemObserveDirectoryFunction::Run() {
965 NOTIMPLEMENTED();
966 return RespondNow(Error(kUnknownIdError));
967 }
968
969 ExtensionFunction::ResponseAction FileSystemUnobserveEntryFunction::Run() {
970 NOTIMPLEMENTED();
971 return RespondNow(Error(kUnknownIdError));
972 }
973
974 ExtensionFunction::ResponseAction FileSystemGetObservedEntriesFunction::Run() {
975 NOTIMPLEMENTED();
976 return RespondNow(Error(kUnknownIdError));
977 }
978
979 #if !defined(OS_CHROMEOS)
980 ExtensionFunction::ResponseAction FileSystemRequestFileSystemFunction::Run() {
981 using api::file_system::RequestFileSystem::Params;
982 const std::unique_ptr<Params> params(Params::Create(*args_));
983 EXTENSION_FUNCTION_VALIDATE(params);
984
985 NOTIMPLEMENTED();
986 return RespondNow(Error(kNotSupportedOnCurrentPlatformError));
987 }
988
989 ExtensionFunction::ResponseAction FileSystemGetVolumeListFunction::Run() {
990 NOTIMPLEMENTED();
991 return RespondNow(Error(kNotSupportedOnCurrentPlatformError));
992 }
993 #else
994
995 FileSystemRequestFileSystemFunction::FileSystemRequestFileSystemFunction()
996 : chrome_details_(this) {}
997
998 FileSystemRequestFileSystemFunction::~FileSystemRequestFileSystemFunction() {}
999
1000 ExtensionFunction::ResponseAction FileSystemRequestFileSystemFunction::Run() {
1001 using api::file_system::RequestFileSystem::Params;
1002 const std::unique_ptr<Params> params(Params::Create(*args_));
1003 EXTENSION_FUNCTION_VALIDATE(params);
1004
1005 // Only kiosk apps in kiosk sessions can use this API. 311 // Only kiosk apps in kiosk sessions can use this API.
1006 // Additionally it is enabled for whitelisted component extensions and apps. 312 // Additionally it is enabled for whitelisted component extensions and apps.
1007 file_system_api::ConsentProviderDelegate consent_provider_delegate( 313 ConsentProviderDelegate consent_provider_delegate(
1008 chrome_details_.GetProfile(), render_frame_host()); 314 Profile::FromBrowserContext(browser_context));
1009 file_system_api::ConsentProvider consent_provider(&consent_provider_delegate); 315 return ConsentProvider(&consent_provider_delegate).IsGrantable(extension);
1010 316 }
1011 if (!consent_provider.IsGrantable(*extension())) 317
1012 return RespondNow(Error(kNotSupportedOnNonKioskSessionError)); 318 void ChromeFileSystemDelegate::RequestFileSystem(
319 content::BrowserContext* browser_context,
320 scoped_refptr<UIThreadExtensionFunction> requester,
321 const Extension& extension,
322 std::string volume_id,
323 bool writable,
324 const FileSystemCallback& success_callback,
325 const ErrorCallback& error_callback) {
326 ConsentProviderDelegate consent_provider_delegate(
327 Profile::FromBrowserContext(browser_context));
328 ConsentProvider consent_provider(&consent_provider_delegate);
329
330 if (!consent_provider.IsGrantable(extension)) {
331 error_callback.Run(kNotSupportedOnNonKioskSessionError);
332 return;
333 }
1013 334
1014 using file_manager::VolumeManager; 335 using file_manager::VolumeManager;
1015 using file_manager::Volume; 336 using file_manager::Volume;
1016 VolumeManager* const volume_manager = 337 VolumeManager* const volume_manager = VolumeManager::Get(browser_context);
1017 VolumeManager::Get(chrome_details_.GetProfile());
1018 DCHECK(volume_manager); 338 DCHECK(volume_manager);
1019 339
1020 const bool writable =
1021 params->options.writable.get() && *params->options.writable.get();
1022 if (writable && 340 if (writable &&
1023 !app_file_handler_util::HasFileSystemWritePermission(extension_.get())) { 341 !app_file_handler_util::HasFileSystemWritePermission(&extension)) {
1024 return RespondNow(Error(kRequiresFileSystemWriteError)); 342 error_callback.Run(kRequiresFileSystemWriteError);
343 return;
1025 } 344 }
1026 345
1027 base::WeakPtr<file_manager::Volume> volume = 346 base::WeakPtr<file_manager::Volume> volume =
1028 volume_manager->FindVolumeById(params->options.volume_id); 347 volume_manager->FindVolumeById(volume_id);
1029 if (!volume.get()) 348 if (!volume.get()) {
1030 return RespondNow(Error(kVolumeNotFoundError)); 349 error_callback.Run(kVolumeNotFoundError);
350 return;
351 }
1031 352
1032 const GURL site = 353 const GURL site =
1033 util::GetSiteForExtensionId(extension_id(), chrome_details_.GetProfile()); 354 util::GetSiteForExtensionId(extension.id(), browser_context);
1034 scoped_refptr<storage::FileSystemContext> file_system_context = 355 scoped_refptr<storage::FileSystemContext> file_system_context =
1035 content::BrowserContext::GetStoragePartitionForSite( 356 content::BrowserContext::GetStoragePartitionForSite(browser_context, site)
1036 chrome_details_.GetProfile(), site)
1037 ->GetFileSystemContext(); 357 ->GetFileSystemContext();
1038 storage::ExternalFileSystemBackend* const backend = 358 storage::ExternalFileSystemBackend* const backend =
1039 file_system_context->external_backend(); 359 file_system_context->external_backend();
1040 DCHECK(backend);
1041
1042 base::FilePath virtual_path;
1043 if (!backend->GetVirtualPath(volume->mount_path(), &virtual_path))
1044 return RespondNow(Error(kSecurityError));
1045
1046 if (writable && (volume->is_read_only()))
1047 return RespondNow(Error(kSecurityError));
1048
1049 consent_provider.RequestConsent(
1050 *extension(), volume, writable,
1051 base::Bind(&FileSystemRequestFileSystemFunction::OnConsentReceived, this,
1052 volume, writable));
1053 return RespondLater();
1054 }
1055
1056 void FileSystemRequestFileSystemFunction::OnConsentReceived(
1057 const base::WeakPtr<file_manager::Volume>& volume,
1058 bool writable,
1059 ConsentProvider::Consent result) {
1060 using file_manager::VolumeManager;
1061 using file_manager::Volume;
1062
1063 // Render frame host can be gone before this callback method is executed.
1064 if (!render_frame_host()) {
1065 Respond(Error(""));
1066 return;
1067 }
1068
1069 switch (result) {
1070 case ConsentProvider::CONSENT_REJECTED:
1071 Respond(Error(kSecurityError));
1072 return;
1073
1074 case ConsentProvider::CONSENT_IMPOSSIBLE:
1075 Respond(Error(kConsentImpossible));
1076 return;
1077
1078 case ConsentProvider::CONSENT_GRANTED:
1079 break;
1080 }
1081
1082 if (!volume.get()) {
1083 Respond(Error(kVolumeNotFoundError));
1084 return;
1085 }
1086
1087 const GURL site =
1088 util::GetSiteForExtensionId(extension_id(), chrome_details_.GetProfile());
1089 scoped_refptr<storage::FileSystemContext> file_system_context =
1090 content::BrowserContext::GetStoragePartitionForSite(
1091 chrome_details_.GetProfile(), site)
1092 ->GetFileSystemContext();
1093 storage::ExternalFileSystemBackend* const backend =
1094 file_system_context->external_backend();
1095 DCHECK(backend); 360 DCHECK(backend);
1096 361
1097 base::FilePath virtual_path; 362 base::FilePath virtual_path;
1098 if (!backend->GetVirtualPath(volume->mount_path(), &virtual_path)) { 363 if (!backend->GetVirtualPath(volume->mount_path(), &virtual_path)) {
1099 Respond(Error(kSecurityError)); 364 error_callback.Run(kSecurityError);
1100 return; 365 return;
1101 } 366 }
1102 367
1103 storage::IsolatedContext* const isolated_context = 368 if (writable && (volume->is_read_only())) {
1104 storage::IsolatedContext::GetInstance(); 369 error_callback.Run(kSecurityError);
1105 DCHECK(isolated_context);
1106
1107 const storage::FileSystemURL original_url =
1108 file_system_context->CreateCrackedFileSystemURL(
1109 GURL(std::string(kExtensionScheme) + url::kStandardSchemeSeparator +
1110 extension_id()),
1111 storage::kFileSystemTypeExternal, virtual_path);
1112
1113 // Set a fixed register name, as the automatic one would leak the mount point
1114 // directory.
1115 std::string register_name = "fs";
1116 const std::string file_system_id =
1117 isolated_context->RegisterFileSystemForPath(
1118 storage::kFileSystemTypeNativeForPlatformApp,
1119 std::string() /* file_system_id */, original_url.path(),
1120 &register_name);
1121 if (file_system_id.empty()) {
1122 Respond(Error(kSecurityError));
1123 return; 370 return;
1124 } 371 }
1125 372
1126 backend->GrantFileAccessToExtension(extension_->id(), virtual_path); 373 const ConsentProvider::ConsentCallback& callback = base::Bind(
374 &OnConsentReceived, browser_context, requester, success_callback,
375 error_callback, extension.id(), volume, writable);
1127 376
1128 // Grant file permissions to the renderer hosting component. 377 consent_provider.RequestConsent(extension, requester->render_frame_host(),
1129 content::ChildProcessSecurityPolicy* policy = 378 volume, writable, callback);
1130 content::ChildProcessSecurityPolicy::GetInstance();
1131 DCHECK(policy);
1132
1133 // Read-only permisisons.
1134 policy->GrantReadFile(render_frame_host()->GetProcess()->GetID(),
1135 volume->mount_path());
1136 policy->GrantReadFileSystem(render_frame_host()->GetProcess()->GetID(),
1137 file_system_id);
1138
1139 // Additional write permissions.
1140 if (writable) {
1141 policy->GrantCreateReadWriteFile(render_frame_host()->GetProcess()->GetID(),
1142 volume->mount_path());
1143 policy->GrantCopyInto(render_frame_host()->GetProcess()->GetID(),
1144 volume->mount_path());
1145 policy->GrantWriteFileSystem(render_frame_host()->GetProcess()->GetID(),
1146 file_system_id);
1147 policy->GrantDeleteFromFileSystem(
1148 render_frame_host()->GetProcess()->GetID(), file_system_id);
1149 policy->GrantCreateFileForFileSystem(
1150 render_frame_host()->GetProcess()->GetID(), file_system_id);
1151 }
1152
1153 std::unique_ptr<base::DictionaryValue> dict(new base::DictionaryValue());
1154 dict->SetString("file_system_id", file_system_id);
1155 dict->SetString("file_system_path", register_name);
1156
1157 Respond(OneArgument(std::move(dict)));
1158 } 379 }
1159 380
1160 FileSystemGetVolumeListFunction::FileSystemGetVolumeListFunction() 381 void ChromeFileSystemDelegate::GetVolumeList(
1161 : chrome_details_(this) {} 382 content::BrowserContext* browser_context,
383 const VolumeListCallback& success_callback,
384 const ErrorCallback& error_callback) {
385 std::vector<file_system::Volume> result_volume_list;
386 FillVolumeList(browser_context, &result_volume_list);
1162 387
1163 FileSystemGetVolumeListFunction::~FileSystemGetVolumeListFunction() {} 388 success_callback.Run(result_volume_list);
389 }
1164 390
1165 ExtensionFunction::ResponseAction FileSystemGetVolumeListFunction::Run() { 391 #endif // defined(OS_CHROMEOS)
1166 // Only kiosk apps in kiosk sessions can use this API.
1167 // Additionally it is enabled for whitelisted component extensions and apps.
1168 file_system_api::ConsentProviderDelegate consent_provider_delegate(
1169 chrome_details_.GetProfile(), render_frame_host());
1170 file_system_api::ConsentProvider consent_provider(&consent_provider_delegate);
1171 392
1172 if (!consent_provider.IsGrantable(*extension())) 393 SavedFilesServiceInterface* ChromeFileSystemDelegate::GetSavedFilesService(
1173 return RespondNow(Error(kNotSupportedOnNonKioskSessionError)); 394 content::BrowserContext* browser_context) {
1174 std::vector<api::file_system::Volume> result_volume_list; 395 return apps::SavedFilesService::Get(browser_context);
1175 FillVolumeList(chrome_details_.GetProfile(), &result_volume_list);
1176
1177 return RespondNow(ArgumentList(
1178 api::file_system::GetVolumeList::Results::Create(result_volume_list)));
1179 } 396 }
1180 #endif
1181 397
1182 } // namespace extensions 398 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698