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

Side by Side Diff: chrome/browser/extensions/extension_file_browser_private_api.cc

Issue 6749021: Added new fileBrowserPrivate and fileHandler extension APIs (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 8 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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/extension_file_browser_private_api.h" 5 #include "chrome/browser/extensions/extension_file_browser_private_api.h"
6 6
7 #include "base/base64.h"
8 #include "base/command_line.h"
7 #include "base/json/json_writer.h" 9 #include "base/json/json_writer.h"
10 #include "base/memory/singleton.h"
11 #include "base/stringprintf.h"
8 #include "base/task.h" 12 #include "base/task.h"
9 #include "base/values.h" 13 #include "base/values.h"
10 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/extensions/extension_function_dispatcher.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/tab_contents/context_menu_utils.h"
18 #include "chrome/browser/ui/webui/extension_icon_source.h"
19 #include "chrome/common/chrome_switches.h"
11 #include "chrome/common/extensions/extension.h" 20 #include "chrome/common/extensions/extension.h"
12 #include "content/browser/browser_thread.h" 21 #include "content/browser/browser_thread.h"
22 #include "content/browser/child_process_security_policy.h"
23 #include "content/browser/renderer_host/render_process_host.h"
24 #include "content/browser/renderer_host/render_view_host.h"
25 #include "content/browser/tab_contents/tab_contents.h"
26 #include "webkit/fileapi/file_system_context.h"
27 #include "webkit/fileapi/file_system_mount_point_provider.h"
13 #include "webkit/fileapi/file_system_operation.h" 28 #include "webkit/fileapi/file_system_operation.h"
29 #include "webkit/fileapi/file_system_path_manager.h"
14 #include "webkit/fileapi/file_system_types.h" 30 #include "webkit/fileapi/file_system_types.h"
31 #include "webkit/glue/context_menu.h"
32
33 const char kContextTaskIdSchema[] = "context-task";
34
35 // Error messages.
36 const char kFileError[] = "File error %d";
37 const char kInvalidFileUrl[] = "Invalid file URL";
abarth-chromium 2011/04/07 23:09:20 Does these error messages need to be localized?
zel 2011/04/07 23:11:00 From what I've seen in other API examples, we didn
38
39 const int kReadOnlyFilePermissions = base::PLATFORM_FILE_OPEN |
40 base::PLATFORM_FILE_READ |
41 base::PLATFORM_FILE_EXCLUSIVE_READ |
42 base::PLATFORM_FILE_ASYNC;
43
44 const int kReadWriteFilePermissions = base::PLATFORM_FILE_OPEN |
45 base::PLATFORM_FILE_CREATE |
46 base::PLATFORM_FILE_OPEN_ALWAYS |
47 base::PLATFORM_FILE_CREATE_ALWAYS |
48 base::PLATFORM_FILE_READ |
49 base::PLATFORM_FILE_WRITE |
50 base::PLATFORM_FILE_EXCLUSIVE_READ |
51 base::PLATFORM_FILE_EXCLUSIVE_WRITE |
52 base::PLATFORM_FILE_ASYNC |
53 base::PLATFORM_FILE_TRUNCATE |
54 base::PLATFORM_FILE_WRITE_ATTRIBUTES;
55
56 bool GetContextMenuItems(Profile* profile,
57 const ContextMenuParams& params,
58 ExtensionMenuItem::List* results) {
59 ExtensionService* service = profile->GetExtensionService();
60 if (!service)
61 return false; // In unit-tests, we may not have an ExtensionService.
62
63 // Get a list of extension id's that have context menu items, and sort it by
64 // the extension's name.
65 ExtensionMenuManager* menu_manager = service->menu_manager();
66 std::set<std::string> ids = menu_manager->ExtensionIds();
67 std::vector<std::pair<std::string, std::string> > sorted_ids;
68 for (std::set<std::string>::iterator i = ids.begin(); i != ids.end(); ++i) {
69 const Extension* extension = service->GetExtensionById(*i, false);
70 if (extension)
71 sorted_ids.push_back(
72 std::pair<std::string, std::string>(extension->name(), *i));
73 }
74
75 if (sorted_ids.empty())
76 return true;
77
78 std::vector<std::pair<std::string, std::string> >::const_iterator i;
79 for (i = sorted_ids.begin(); i != sorted_ids.end(); ++i) {
80 const std::string& extension_id = i->second;
81 const Extension* extension = service->GetExtensionById(extension_id, false);
82 bool can_cross_incognito = service->CanCrossIncognito(extension);
83 const ExtensionMenuItem::List* all_items =
84 menu_manager->MenuItems(extension_id);
85 ExtensionMenuItem::List relevant_items =
86 ContextMenuUtils::GetRelevantExtensionItems(*all_items,
87 params,
88 profile,
89 can_cross_incognito);
90 results->insert(results->end(), relevant_items.begin(),
91 relevant_items.end());
92 }
93 return true;
94 }
95
96 void CreateContextMenuParams(const GURL& source_url,
97 const std::string& file_url,
98 ContextMenuParams* params) {
99 DCHECK(params);
100 params->is_image_blocked = false;
101 params->spellcheck_enabled = false;
102 params->is_editable = false;
103 params->media_flags = 0;
104 params->edit_flags = 0;
105 params->media_type = WebKit::WebContextMenuData::MediaTypeFile;
106 params->src_url = GURL(file_url);
107 params->page_url = source_url;
108 }
109
110 // Given the list of selected files, returns array of context menu tasks
111 // that are sahred
112 bool FindCommonTasks(Profile* profile,
113 const GURL& source_url,
114 ListValue* files_list,
115 ExtensionMenuItem::List* common_tasks) {
116 common_tasks->clear();
117 for (size_t i = 0; i < files_list->GetSize(); ++i) {
118 std::string file_url;
119 if (!files_list->GetString(i, &file_url))
120 return false;
121
122 ContextMenuParams params;
123 CreateContextMenuParams(source_url, file_url, &params);
124
125 ExtensionMenuItem::List file_actions;
126 if (!GetContextMenuItems(profile, params, &file_actions))
127 return false;
128 // If there is nothing to do for one file, the intersection of tasks for all
129 // files will be empty at the end.
130 if (!file_actions.size()) {
131 common_tasks->clear();
132 return true;
133 }
134 // For the very first file, just copy elements.
135 if (i == 0) {
136 common_tasks->insert(common_tasks->begin(),
137 file_actions.begin(),
138 file_actions.end());
139 std::sort(common_tasks->begin(), common_tasks->end());
140 } else if (common_tasks->size()) {
141 // For all additional files, find intersection between the accumulated
142 // and file specific set.
143 std::sort(file_actions.begin(), file_actions.end());
144 ExtensionMenuItem::List intersection(common_tasks->size());
145 ExtensionMenuItem::List::iterator intersection_end =
146 std::set_intersection(common_tasks->begin(),
147 common_tasks->end(),
148 file_actions.begin(),
149 file_actions.end(),
150 intersection.begin());
151 common_tasks->clear();
152 common_tasks->insert(common_tasks->begin(),
153 intersection.begin(),
154 intersection_end);
155 std::sort(common_tasks->begin(), common_tasks->end());
156 }
157 }
158 return true;
159 }
160
161 // Breaks down task_id that is used between getFileTasks() and executeTask() on
162 // its building blocks. task_id field the following structure:
163 // <task-type>:<extension-id>/<task-action-id>
164 // Currently, the only supported task-type is of 'context'.
165 bool CrackTaskIdentifier(const std::string& task_id,
166 std::string* task_type,
167 std::string* target_extension_id,
168 std::string* action_id) {
169 std::string::size_type pos_col = task_id.find(':');
170 if (pos_col == std::string::npos)
171 return false;
172 *task_type = task_id.substr(0, pos_col);
173 std::string::size_type pos_slash = task_id.find('/');
174 if (pos_slash == std::string::npos)
175 return false;
176 *target_extension_id = task_id.substr(pos_col + 1, pos_slash - pos_col - 1);
177 *action_id = task_id.substr(pos_slash + 1);
178 return true;
179 }
180
181 std::string MakeTaskID(const char* task_schema,
182 const char* extension_id,
183 int action_id) {
184 return base::StringPrintf("%s:%s/%d", task_schema, extension_id, action_id);
185 }
15 186
16 class LocalFileSystemCallbackDispatcher 187 class LocalFileSystemCallbackDispatcher
17 : public fileapi::FileSystemCallbackDispatcher { 188 : public fileapi::FileSystemCallbackDispatcher {
18 public: 189 public:
19 explicit LocalFileSystemCallbackDispatcher( 190 explicit LocalFileSystemCallbackDispatcher(
20 RequestLocalFileSystemFunction* function) : function_(function) { 191 RequestLocalFileSystemFunctionBase* function,
192 Profile* profile,
193 int child_id,
194 const GURL& source_url,
195 const GURL& file_url)
196 : function_(function),
197 profile_(profile),
198 child_id_(child_id),
199 source_url_(source_url),
200 file_url_(file_url) {
21 DCHECK(function_); 201 DCHECK(function_);
22 } 202 }
23 // fileapi::FileSystemCallbackDispatcher overrides. 203 // fileapi::FileSystemCallbackDispatcher overrides.
24 virtual void DidSucceed() OVERRIDE { 204 virtual void DidSucceed() OVERRIDE {
25 NOTREACHED(); 205 NOTREACHED();
26 } 206 }
27 virtual void DidReadMetadata(const base::PlatformFileInfo& info, 207 virtual void DidReadMetadata(const base::PlatformFileInfo& info,
28 const FilePath& unused) OVERRIDE { 208 const FilePath& unused) OVERRIDE {
29 NOTREACHED(); 209 NOTREACHED();
30 } 210 }
31 virtual void DidReadDirectory( 211 virtual void DidReadDirectory(
32 const std::vector<base::FileUtilProxy::Entry>& entries, 212 const std::vector<base::FileUtilProxy::Entry>& entries,
33 bool has_more) OVERRIDE { 213 bool has_more) OVERRIDE {
34 NOTREACHED(); 214 NOTREACHED();
35 } 215 }
36 virtual void DidWrite(int64 bytes, bool complete) OVERRIDE { 216 virtual void DidWrite(int64 bytes, bool complete) OVERRIDE {
37 NOTREACHED(); 217 NOTREACHED();
38 } 218 }
39 virtual void DidOpenFileSystem(const std::string& name, 219 virtual void DidOpenFileSystem(const std::string& name,
40 const FilePath& path) OVERRIDE { 220 const FilePath& path) OVERRIDE {
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
222 // Set up file permission access.
223 if (!file_url_.is_empty()) {
224 if (!SetupFileAccessPermissions()) {
225 DidFail(base::PLATFORM_FILE_ERROR_SECURITY);
226 return;
227 }
228 } else if (!SetupFileSystemAccessPermissions()) {
229 DidFail(base::PLATFORM_FILE_ERROR_SECURITY);
230 return;
231 }
232
41 BrowserThread::PostTask( 233 BrowserThread::PostTask(
42 BrowserThread::UI, FROM_HERE, 234 BrowserThread::UI, FROM_HERE,
43 NewRunnableMethod(function_, 235 NewRunnableMethod(function_,
44 &RequestLocalFileSystemFunction::RespondSuccessOnUIThread, 236 &RequestLocalFileSystemFunctionBase::RespondSuccessOnUIThread,
45 name, 237 name,
46 path)); 238 path,
239 file_url_));
47 } 240 }
48 virtual void DidFail(base::PlatformFileError error_code) OVERRIDE { 241 virtual void DidFail(base::PlatformFileError error_code) OVERRIDE {
49 BrowserThread::PostTask( 242 BrowserThread::PostTask(
50 BrowserThread::UI, FROM_HERE, 243 BrowserThread::UI, FROM_HERE,
51 NewRunnableMethod(function_, 244 NewRunnableMethod(function_,
52 &RequestLocalFileSystemFunction::RespondFailedOnUIThread, 245 &RequestLocalFileSystemFunctionBase::RespondFailedOnUIThread,
53 error_code)); 246 error_code));
54 } 247 }
55 private: 248 private:
56 RequestLocalFileSystemFunction* function_; 249
250 const Extension* GetExtension() {
251 std::string extension_id = source_url_.GetOrigin().host();
252 ExtensionService* service = profile_->GetExtensionService();
253 if (!service)
254 return NULL;
255 return service->GetExtensionById(extension_id,
256 false); // include_disabled
257 }
258
259 // Checks legitimacy of file url and grants RO access permissions for that
260 // file to the target renderer process.
261 bool SetupFileAccessPermissions() {
262 GURL file_origin_url;
263 FilePath virtual_path;
264 fileapi::FileSystemType type;
265 fileapi::FileSystemPathManager* path_manager =
266 profile_->GetFileSystemContext()->path_manager();
267
268 const Extension* extension = GetExtension();
269 if (!extension)
270 return false;
271
272 if (!path_manager->CrackFileSystemPath(FilePath(file_url_.spec()),
273 &file_origin_url,
274 &type,
275 &virtual_path)) {
276 return false;
277 }
278
279 if (type != fileapi::kFileSystemTypeLocal)
280 return false;
281
282 // Make sure this url really used by the right caller extension.
ericu 2011/04/08 02:12:14 s/really/is really being/ ?
zel 2011/04/08 04:56:48 Done.
283 if (source_url_.GetOrigin() != file_origin_url) {
284 DidFail(base::PLATFORM_FILE_ERROR_SECURITY);
285 return false;
286 }
287 FilePath root_path = path_manager->GetFileSystemRootPathOnFileThread(
288 file_origin_url,
289 fileapi::kFileSystemTypeLocal,
290 FilePath(virtual_path),
ericu 2011/04/08 02:12:14 FilePath constructor is redundant.
zel 2011/04/08 04:56:48 Done.
291 false); // create
292 FilePath finalFilePath = root_path.Append(virtual_path);
293
294 // TODO(zelidrag): Add explicit R/W + R/O permissions for non-component
295 // extensions.
296
297 // Grant R/O access permission to non-component extension and R/W to
298 // component extensions.
299 ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile(
300 child_id_, finalFilePath,
301 extension->location() != Extension::COMPONENT ?
302 kReadOnlyFilePermissions : kReadWriteFilePermissions);
303 return true;
304 }
305
306 // Grants file system access permissions to file browser component.
307 bool SetupFileSystemAccessPermissions() {
308 const Extension* extension = GetExtension();
309 if (!extension)
310 return false;
311
312 // Make sure that only component extension can access the entire
313 // local file system.
314 if (extension->location() != Extension::COMPONENT
315 #ifndef NDEBUG
316 && !CommandLine::ForCurrentProcess()->HasSwitch(
317 switches::kExposePrivateExtensionApi)
318 #endif
319 ) {
320 NOTREACHED() << "Private method access by non-component extension "
321 << extension->id();
322 return false;
323 }
324
325 fileapi::FileSystemPathManager* path_manager =
326 profile_->GetFileSystemContext()->path_manager();
327 fileapi::FileSystemMountPointProvider* provider =
328 path_manager->local_provider();
329 if (!provider)
330 return false;
331
332 // Grant R/W file permissions to the renderer hosting component
333 // extension for all paths exposed by our local file system provider.
334 std::vector<FilePath> root_dirs = provider->GetRootDirectories();
335 for (std::vector<FilePath>::iterator iter = root_dirs.begin();
336 iter != root_dirs.end();
337 ++iter) {
338 ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile(
339 child_id_, *iter, kReadWriteFilePermissions);
340 }
341 return true;
342 }
343
344 RequestLocalFileSystemFunctionBase* function_;
345 Profile* profile_;
346 // Renderer process id.
347 int child_id_;
348 // Extension source URL.
349 GURL source_url_;
350 GURL file_url_;
57 DISALLOW_COPY_AND_ASSIGN(LocalFileSystemCallbackDispatcher); 351 DISALLOW_COPY_AND_ASSIGN(LocalFileSystemCallbackDispatcher);
58 }; 352 };
59 353
60 RequestLocalFileSystemFunction::RequestLocalFileSystemFunction() { 354 void RequestLocalFileSystemFunctionBase::RequestOnFileThread(
61 } 355 const GURL& source_url, const GURL& file_url) {
62
63 RequestLocalFileSystemFunction::~RequestLocalFileSystemFunction() {
64 }
65
66 bool RequestLocalFileSystemFunction::RunImpl() {
67 fileapi::FileSystemOperation* operation = 356 fileapi::FileSystemOperation* operation =
68 new fileapi::FileSystemOperation( 357 new fileapi::FileSystemOperation(
69 new LocalFileSystemCallbackDispatcher(this), 358 new LocalFileSystemCallbackDispatcher(
359 this,
360 profile(),
361 dispatcher()->render_view_host()->process()->id(),
362 source_url,
363 file_url),
70 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), 364 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
71 profile()->GetFileSystemContext(), 365 profile()->GetFileSystemContext(),
72 NULL); 366 NULL);
73 GURL origin_url = source_url().GetOrigin(); 367 GURL origin_url = source_url.GetOrigin();
74 operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeLocal, 368 operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeLocal,
75 false); // create 369 false); // create
370 }
371
372 bool RequestLocalFileSystemFunctionBase::RunImpl() {
373 std::string file_url;
374 if (args_->GetSize())
375 args_->GetString(0, &file_url);
376 BrowserThread::PostTask(
377 BrowserThread::FILE, FROM_HERE,
378 NewRunnableMethod(this,
379 &RequestLocalFileSystemFunctionBase::RequestOnFileThread,
380 source_url_,
381 GURL(file_url)));
76 // Will finish asynchronously. 382 // Will finish asynchronously.
77 return true; 383 return true;
78 } 384 }
79 385
80 void RequestLocalFileSystemFunction::RespondSuccessOnUIThread( 386 void RequestLocalFileSystemFunctionBase::RespondSuccessOnUIThread(
81 const std::string& name, const FilePath& path) { 387 const std::string& name, const FilePath& path,
388 const GURL& file_url) {
82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 389 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
83 result_.reset(new DictionaryValue()); 390 result_.reset(new DictionaryValue());
84 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get()); 391 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get());
85 dict->SetString("name", name); 392 dict->SetString("name", name);
86 dict->SetString("path", path.value()); 393 dict->SetString("path", path.value());
87 dict->SetInteger("error", base::PLATFORM_FILE_OK); 394 dict->SetInteger("error", base::PLATFORM_FILE_OK);
395 if (!file_url.is_empty() && file_url.is_valid())
396 dict->SetString("fileUrl", file_url.spec());
88 SendResponse(true); 397 SendResponse(true);
89 } 398 }
90 399
91 void RequestLocalFileSystemFunction::RespondFailedOnUIThread( 400 void RequestLocalFileSystemFunctionBase::RespondFailedOnUIThread(
92 base::PlatformFileError error_code) { 401 base::PlatformFileError error_code) {
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 402 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
94 result_.reset(new DictionaryValue()); 403 error_ = base::StringPrintf(kFileError, static_cast<int>(error_code));
95 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get()); 404 SendResponse(false);
96 dict->SetInteger("error", static_cast<int>(error_code)); 405 }
406
407 bool GetFileTasksFileBrowserFunction::RunImpl() {
408 ListValue* files_list = NULL;
409 if (!args_->GetList(0, &files_list))
410 return false;
411
412 result_.reset(new ListValue());
413 ListValue* result_list = reinterpret_cast<ListValue*>(result_.get());
414
415 ExtensionMenuItem::List common_tasks;
416 if (!FindCommonTasks(profile_, source_url_, files_list, &common_tasks))
417 return false;
418
419 ExtensionService* service = profile_->GetExtensionService();
420 for (ExtensionMenuItem::List::iterator iter = common_tasks.begin();
421 iter != common_tasks.end();
422 ++iter) {
423 if ((*iter)->type() != ExtensionMenuItem::NORMAL)
424 continue;
425 const std::string extension_id = (*iter)->extension_id();
426 const Extension* extension = service->GetExtensionById(extension_id, false);
427 if (!extension) {
428 LOG(WARNING) << "Disabled extension" << extension_id;
429 continue;
430 }
431 DictionaryValue* task = new DictionaryValue();
432 task->SetString("taskId", MakeTaskID(kContextTaskIdSchema,
433 extension_id.c_str(),
434 (*iter)->id().uid));
435 task->SetString("title", (*iter)->title());
436 GURL icon =
437 ExtensionIconSource::GetIconURL(extension,
438 Extension::EXTENSION_ICON_SMALLISH,
439 ExtensionIconSet::MATCH_BIGGER,
440 false); // grayscale
441 task->SetString("iconUrl", icon.spec());
442 result_list->Append(task);
443 }
444
445 // TODO(zelidrag, serya): Add intent content tasks to result_list once we
446 // implement that API.
97 SendResponse(true); 447 SendResponse(true);
98 } 448 return true;
99 449 }
450
451 bool ExecuteTasksFileBrowserFunction::RunImpl() {
452 // First param is task id that was to the extension with getFileTasks call.
453 std::string task_id;
454 if (!args_->GetString(0, &task_id) || !task_id.size())
455 return false;
456
457 // The second param is the list of files that need to be executed with this
458 // task.
459 ListValue* files_list = NULL;
460 if (!args_->GetList(1, &files_list))
461 return false;
462
463 if (!files_list->GetSize())
464 return true;
465
466 std::string task_type;
467 std::string target_extension_id;
468 std::string action_id;
469 if (!CrackTaskIdentifier(task_id, &task_type, &target_extension_id,
470 &action_id)) {
471 return false;
472 }
473
474 if (task_type == kContextTaskIdSchema) {
475 ExecuteContextMenuTasks(target_extension_id, action_id, files_list);
476 } else {
477 LOG(WARNING) << "Unsupported task type of: " << task_type;
478 // TODO(zelidrag, serya): Add intent content tasks here once we implement
479 // that API.
480 return false;
481 }
482 SendResponse(true);
483 return true;
484 }
485
486 bool ExecuteTasksFileBrowserFunction::GrantLocalFileSystemAccess(
487 const GURL& origin_file_url,
488 const std::string& extension_id,
489 GURL* target_file_url) {
490 GURL file_origin_url;
491 FilePath virtual_path;
492 fileapi::FileSystemType type;
493 fileapi::FileSystemPathManager* path_manager =
494 profile_->GetFileSystemContext()->path_manager();
495 // Breakdown file URL.
496 if (!path_manager->CrackFileSystemPath(FilePath(origin_file_url.spec()),
497 &file_origin_url,
498 &type,
499 &virtual_path)) {
500 return false;
501 }
502 // Grant access to this particular file to target extension.
503 GURL target_origin_url(base::StringPrintf(
504 "chrome-extension://%s/", extension_id.data()));
505 fileapi::FileSystemMountPointProvider* provider =
506 path_manager->local_provider();
507 if (!provider)
508 return false;
509 provider->GrantAccess(target_origin_url, virtual_path);
510 *target_file_url = GURL(base::StringPrintf("filesystem:%slocal/%s",
511 target_origin_url.spec().data(),
512 virtual_path.value().data()));
513 return true;
514 }
515
516 bool ExecuteTasksFileBrowserFunction::ExecuteContextMenuTasks(
517 const std::string& handler_extension_id, const std::string& action_id,
518 ListValue* files_list) {
519 ExtensionMenuManager* manager =
520 profile_->GetExtensionService()->menu_manager();
521 for (size_t i = 0; i < files_list->GetSize(); i++) {
522 std::string origin_file_url;
523 if (!files_list->GetString(i, &origin_file_url)) {
524 error_ = kInvalidFileUrl;
525 SendResponse(false);
526 return false;
527 }
528 GURL handler_file_url;
529 if (!GrantLocalFileSystemAccess(GURL(origin_file_url),
530 handler_extension_id,
531 &handler_file_url)) {
532 error_ = kInvalidFileUrl;
533 SendResponse(false);
534 return false;
535 }
536 ContextMenuParams params;
537 CreateContextMenuParams(source_url_, handler_file_url.spec(), &params);
538 ExtensionMenuItem::Id menuItemId(profile_, handler_extension_id,
539 atoi(action_id.c_str()));
540 manager->ExecuteCommand(profile_,
541 NULL, // tab_contents, not needed in args.
542 params,
543 menuItemId);
544 }
545 result_.reset(new FundamentalValue(true));
546 SendResponse(true);
547 return true;
548 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698