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

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"
9 #include "base/crypto/symmetric_key.h"
10 #include "base/hmac.h"
7 #include "base/json/json_writer.h" 11 #include "base/json/json_writer.h"
12 #include "base/memory/singleton.h"
13 #include "base/stringprintf.h"
8 #include "base/task.h" 14 #include "base/task.h"
9 #include "base/values.h" 15 #include "base/values.h"
10 #include "chrome/browser/profiles/profile.h" 16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/extensions/extension_function_dispatcher.h"
18 #include "chrome/browser/extensions/extension_service.h"
19 #include "chrome/browser/tab_contents/context_menu_utils.h"
20 #include "chrome/browser/ui/webui/extension_icon_source.h"
21 #include "chrome/common/chrome_switches.h"
11 #include "chrome/common/extensions/extension.h" 22 #include "chrome/common/extensions/extension.h"
12 #include "content/browser/browser_thread.h" 23 #include "content/browser/browser_thread.h"
24 #include "content/browser/child_process_security_policy.h"
25 #include "content/browser/renderer_host/render_process_host.h"
26 #include "content/browser/renderer_host/render_view_host.h"
27 #include "content/browser/tab_contents/tab_contents.h"
28 #include "webkit/fileapi/file_system_context.h"
29 #include "webkit/fileapi/file_system_mount_point_provider.h"
13 #include "webkit/fileapi/file_system_operation.h" 30 #include "webkit/fileapi/file_system_operation.h"
31 #include "webkit/fileapi/file_system_path_manager.h"
14 #include "webkit/fileapi/file_system_types.h" 32 #include "webkit/fileapi/file_system_types.h"
33 #include "webkit/glue/context_menu.h"
34
35 #define SHA1_SIZE_IN_BITS 160
36
37 const char kContextTaskIdSchema[] = "context-task";
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 }
186
187 // Generates hashes used for filesystem: urls that are sent to 3rd party
188 // extension. Hashes generated from extension id and file url.
189 // They are valid only during the lifetime of the browser instance.
190 class FileHashGenerator {
191 public:
192 ~FileHashGenerator() {}
193 static FileHashGenerator* GetInstance() {
194 return Singleton<FileHashGenerator>::get();
195 }
196 // Generate hash for given url and extension combination.
197 std::string GenerateFileHash(const std::string& file_url,
198 const std::string& extension_id) {
199 std::string data(file_url);
200 data = data.append(extension_id);
201 scoped_ptr<unsigned char> digest(new unsigned char[SHA1_SIZE_IN_BITS/8]);
202 if (!hmac_.Sign(data, digest.get(), SHA1_SIZE_IN_BITS/8))
203 return std::string();
204 std::string output;
205 if (!base::Base64Encode(std::string(reinterpret_cast<char*>(digest.get()),
206 SHA1_SIZE_IN_BITS/8),
207 &output))
208 return std::string();
209 return output;
210 }
211
212 private:
213 friend struct DefaultSingletonTraits<FileHashGenerator>;
214
215 FileHashGenerator() : hmac_(base::HMAC::SHA1) {
216 scoped_ptr<base::SymmetricKey> sym_key(
217 base::SymmetricKey::GenerateRandomKey(base::SymmetricKey::AES,
218 256));
abarth-chromium 2011/04/06 02:17:32 Should 256 be a named constant? Why are we using
zel 2011/04/06 05:25:17 Added constant kAESKeyLengthInBits. symetric_key_
219 std::string raw_key;
220 sym_key->GetRawKey(&raw_key);
221 hmac_.Init(raw_key);
222 }
223
224 base::HMAC hmac_;
225 DISALLOW_COPY_AND_ASSIGN(FileHashGenerator);
226 };
15 227
16 class LocalFileSystemCallbackDispatcher 228 class LocalFileSystemCallbackDispatcher
17 : public fileapi::FileSystemCallbackDispatcher { 229 : public fileapi::FileSystemCallbackDispatcher {
18 public: 230 public:
19 explicit LocalFileSystemCallbackDispatcher( 231 explicit LocalFileSystemCallbackDispatcher(
20 RequestLocalFileSystemFunction* function) : function_(function) { 232 RequestLocalFileSystemFunctionBase* function,
233 Profile* profile,
234 int child_id,
235 const GURL& source_url,
236 const std::string& file_url)
237 : function_(function),
238 profile_(profile),
239 child_id_(child_id),
240 source_url_(source_url),
241 file_url_(file_url) {
21 DCHECK(function_); 242 DCHECK(function_);
22 } 243 }
23 // fileapi::FileSystemCallbackDispatcher overrides. 244 // fileapi::FileSystemCallbackDispatcher overrides.
24 virtual void DidSucceed() OVERRIDE { 245 virtual void DidSucceed() OVERRIDE {
25 NOTREACHED(); 246 NOTREACHED();
26 } 247 }
27 virtual void DidReadMetadata(const base::PlatformFileInfo& info, 248 virtual void DidReadMetadata(const base::PlatformFileInfo& info,
28 const FilePath& unused) OVERRIDE { 249 const FilePath& unused) OVERRIDE {
29 NOTREACHED(); 250 NOTREACHED();
30 } 251 }
31 virtual void DidReadDirectory( 252 virtual void DidReadDirectory(
32 const std::vector<base::FileUtilProxy::Entry>& entries, 253 const std::vector<base::FileUtilProxy::Entry>& entries,
33 bool has_more) OVERRIDE { 254 bool has_more) OVERRIDE {
34 NOTREACHED(); 255 NOTREACHED();
35 } 256 }
36 virtual void DidWrite(int64 bytes, bool complete) OVERRIDE { 257 virtual void DidWrite(int64 bytes, bool complete) OVERRIDE {
37 NOTREACHED(); 258 NOTREACHED();
38 } 259 }
39 virtual void DidOpenFileSystem(const std::string& name, 260 virtual void DidOpenFileSystem(const std::string& name,
40 const FilePath& path) OVERRIDE { 261 const FilePath& path) OVERRIDE {
262 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
263 // Set up file permission access.
264 if (file_url_.length()) {
265 if (!SetupFileAccessPermissions()) {
266 DidFail(base::PLATFORM_FILE_ERROR_SECURITY);
267 return;
268 }
269 } else if (!SetupFileSystemAccessPermissions()) {
270 DidFail(base::PLATFORM_FILE_ERROR_SECURITY);
271 return;
272 }
273
41 BrowserThread::PostTask( 274 BrowserThread::PostTask(
42 BrowserThread::UI, FROM_HERE, 275 BrowserThread::UI, FROM_HERE,
43 NewRunnableMethod(function_, 276 NewRunnableMethod(function_,
44 &RequestLocalFileSystemFunction::RespondSuccessOnUIThread, 277 &RequestLocalFileSystemFunctionBase::RespondSuccessOnUIThread,
45 name, 278 name,
46 path)); 279 path,
280 file_url_));
47 } 281 }
48 virtual void DidFail(base::PlatformFileError error_code) OVERRIDE { 282 virtual void DidFail(base::PlatformFileError error_code) OVERRIDE {
49 BrowserThread::PostTask( 283 BrowserThread::PostTask(
50 BrowserThread::UI, FROM_HERE, 284 BrowserThread::UI, FROM_HERE,
51 NewRunnableMethod(function_, 285 NewRunnableMethod(function_,
52 &RequestLocalFileSystemFunction::RespondFailedOnUIThread, 286 &RequestLocalFileSystemFunctionBase::RespondFailedOnUIThread,
53 error_code)); 287 error_code));
54 } 288 }
55 private: 289 private:
56 RequestLocalFileSystemFunction* function_; 290
291 const Extension* GetExtension() {
292 std::string extension_id = source_url_.GetOrigin().host();
293 ExtensionService* service = profile_->GetExtensionService();
294 if (!service)
295 return NULL;
296 return service->GetExtensionById(extension_id,
297 false); // include_disabled
298 }
299
300 // Checks file url to make sure this particular extension has access to it.
301 // For non-component extensions, hash code of the file url will be verified.
302 bool VerifyFileUrl(const Extension* extension, std::string* base_file_url) {
303 // Component extensions should be able to access any filesystem url exposed
304 // by the provider.
305 if (extension->location() == Extension::COMPONENT
306 #ifndef NDEBUG
307 || CommandLine::ForCurrentProcess()->HasSwitch(
308 switches::kExposePrivateExtensionApi)
309 #endif
310 ) {
311 *base_file_url = file_url_;
312 return true;
313 }
314
315 // For non-component extensions, we need to check file url hash.
316 std::string::size_type pos = file_url_.find('#');
abarth-chromium 2011/04/06 02:17:32 You're storing something magical in the fragment o
zel 2011/04/06 05:25:17 I tried GURL. It does not like filesystem: and doe
317 if (pos == std::string::npos)
318 return false;
319
320 std::string url_hash = file_url_.substr(pos + 1);
321 std::string file_url = file_url_.substr(0, pos);
322 std::string extension_id = source_url_.GetOrigin().host();
323 std::string expected_hash = FileHashGenerator::GetInstance()->
324 GenerateFileHash(file_url, extension_id);
325 if (expected_hash != url_hash)
326 return false;
327 *base_file_url = file_url;
328 return true;
329 }
330
331 // Checks legitimacy of file url and grants RO access permissions for that
332 // file to the target renderer process.
333 bool SetupFileAccessPermissions() {
334 GURL file_origin_url;
335 FilePath virtual_path;
336 fileapi::FileSystemType type;
337 fileapi::FileSystemPathManager* path_manager =
338 profile_->GetFileSystemContext()->path_manager();
339
340 const Extension* extension = GetExtension();
341 if (!extension)
342 return false;
343
344 std::string base_file_url;
345 if (!VerifyFileUrl(extension, &base_file_url))
346 return false;
347
348 if (!path_manager->CrackFileSystemPath(FilePath(base_file_url),
349 &file_origin_url,
350 &type,
351 &virtual_path)) {
352 return false;
353 }
354 // Make sure this url really used by the right caller extension.
355 if (source_url_.GetOrigin() != file_origin_url) {
356 DidFail(base::PLATFORM_FILE_ERROR_SECURITY);
357 return false;
358 }
359 FilePath root_path = path_manager->GetFileSystemRootPathOnFileThread(
360 file_origin_url,
361 fileapi::kFileSystemTypeLocal,
362 FilePath(virtual_path),
363 false); // create
364 FilePath finalFilePath = root_path.Append(virtual_path);\
365
366 // TODO(zelidrag): Add explicit R/W + R/O permissions for non-component
367 // extensions.
368
369 // Grant R/O access permission to non-component extension and R/W to
370 // component extensions.
371 ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile(
372 child_id_, finalFilePath,
373 extension->location() != Extension::COMPONENT ?
374 kReadOnlyFilePermissions : kReadWriteFilePermissions);
375 return true;
376 }
377
378 // Grants file system access permissions to file browser component.
379 bool SetupFileSystemAccessPermissions() {
380 const Extension* extension = GetExtension();
381 if (!extension)
382 return false;
383
384 // Make sure that only component extension can access the entire
385 // local file system.
386 if (extension->location() != Extension::COMPONENT
387 #ifndef NDEBUG
388 && !CommandLine::ForCurrentProcess()->HasSwitch(
389 switches::kExposePrivateExtensionApi)
390 #endif
391 ) {
392 NOTREACHED() << "Private method access by non-component extension "
393 << extension->id();
394 return false;
395 }
396
397 fileapi::FileSystemPathManager* path_manager =
398 profile_->GetFileSystemContext()->path_manager();
399 fileapi::FileSystemMountPointProvider* provider =
400 path_manager->local_provider();
401 if (!provider)
402 return false;
403
404 // Grant R/W file permissions to the renderer hosting component
405 // extension for all paths exposed by our local file system provider.
406 std::vector<FilePath> root_dirs = provider->GetRootDirectories();
407 for (std::vector<FilePath>::iterator iter = root_dirs.begin();
408 iter != root_dirs.end();
409 ++iter) {
410 ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile(
411 child_id_, *iter, kReadWriteFilePermissions);
412 }
413 return true;
414 }
415
416 RequestLocalFileSystemFunctionBase* function_;
417 Profile* profile_;
418 // Renderer process id.
419 int child_id_;
420 // Extension source URL.
421 GURL source_url_;
422 std::string file_url_;
57 DISALLOW_COPY_AND_ASSIGN(LocalFileSystemCallbackDispatcher); 423 DISALLOW_COPY_AND_ASSIGN(LocalFileSystemCallbackDispatcher);
58 }; 424 };
59 425
60 RequestLocalFileSystemFunction::RequestLocalFileSystemFunction() { 426 void RequestLocalFileSystemFunctionBase::RequestOnFileThread(
61 } 427 const GURL& source_url, const std::string& file_url) {
62
63 RequestLocalFileSystemFunction::~RequestLocalFileSystemFunction() {
64 }
65
66 bool RequestLocalFileSystemFunction::RunImpl() {
67 fileapi::FileSystemOperation* operation = 428 fileapi::FileSystemOperation* operation =
68 new fileapi::FileSystemOperation( 429 new fileapi::FileSystemOperation(
69 new LocalFileSystemCallbackDispatcher(this), 430 new LocalFileSystemCallbackDispatcher(
431 this,
432 profile(),
433 dispatcher()->render_view_host()->process()->id(),
434 source_url,
435 file_url),
70 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), 436 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
71 profile()->GetFileSystemContext(), 437 profile()->GetFileSystemContext(),
72 NULL); 438 NULL);
73 GURL origin_url = source_url().GetOrigin(); 439 GURL origin_url = source_url.GetOrigin();
74 operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeLocal, 440 operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeLocal,
75 false); // create 441 false); // create
442 }
443
444 bool RequestLocalFileSystemFunctionBase::RunImpl() {
445 std::string file_url;
446 if (args_->GetSize())
447 args_->GetString(0, &file_url);
448 BrowserThread::PostTask(
449 BrowserThread::FILE, FROM_HERE,
450 NewRunnableMethod(this,
451 &RequestLocalFileSystemFunctionBase::RequestOnFileThread,
452 source_url_,
453 file_url));
76 // Will finish asynchronously. 454 // Will finish asynchronously.
77 return true; 455 return true;
78 } 456 }
79 457
80 void RequestLocalFileSystemFunction::RespondSuccessOnUIThread( 458 void RequestLocalFileSystemFunctionBase::RespondSuccessOnUIThread(
81 const std::string& name, const FilePath& path) { 459 const std::string& name, const FilePath& path,
460 const std::string& file_url) {
82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 461 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
83 result_.reset(new DictionaryValue()); 462 result_.reset(new DictionaryValue());
84 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get()); 463 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get());
85 dict->SetString("name", name); 464 dict->SetString("name", name);
86 dict->SetString("path", path.value()); 465 dict->SetString("path", path.value());
87 dict->SetInteger("error", base::PLATFORM_FILE_OK); 466 dict->SetInteger("error", base::PLATFORM_FILE_OK);
467 if (file_url.size())
468 dict->SetString("fileUrl", file_url);
88 SendResponse(true); 469 SendResponse(true);
89 } 470 }
90 471
91 void RequestLocalFileSystemFunction::RespondFailedOnUIThread( 472 void RequestLocalFileSystemFunctionBase::RespondFailedOnUIThread(
92 base::PlatformFileError error_code) { 473 base::PlatformFileError error_code) {
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 474 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
94 result_.reset(new DictionaryValue()); 475 result_.reset(new DictionaryValue());
95 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get()); 476 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get());
96 dict->SetInteger("error", static_cast<int>(error_code)); 477 dict->SetInteger("error", static_cast<int>(error_code));
478 SendResponse(false);
479 }
480
481 bool GetFileTasksFileBrowserFunction::RunImpl() {
482 ListValue* files_list = NULL;
483 if (!args_->GetList(0, &files_list))
484 return false;
485
486 result_.reset(new ListValue());
487 ListValue* result_list = reinterpret_cast<ListValue*>(result_.get());
488
489 ExtensionMenuItem::List common_tasks;
490 if (!FindCommonTasks(profile_, source_url_, files_list, &common_tasks))
491 return false;
492
493 ExtensionService* service = profile_->GetExtensionService();
494 for (ExtensionMenuItem::List::iterator iter = common_tasks.begin();
495 iter != common_tasks.end();
496 ++iter) {
497 if ((*iter)->type() != ExtensionMenuItem::NORMAL)
498 continue;
499 const std::string extension_id = (*iter)->extension_id();
500 const Extension* extension = service->GetExtensionById(extension_id, false);
501 if (!extension) {
502 LOG(WARNING) << "Disabled extension" << extension_id;
503 continue;
504 }
505 DictionaryValue* task = new DictionaryValue();
506 task->SetString("taskId", MakeTaskID(kContextTaskIdSchema,
507 extension_id.c_str(),
508 (*iter)->id().uid));
509 task->SetString("title", (*iter)->title());
510 GURL icon =
511 ExtensionIconSource::GetIconURL(extension,
512 Extension::EXTENSION_ICON_SMALLISH,
513 ExtensionIconSet::MATCH_BIGGER,
514 false); // grayscale
515 task->SetString("iconUrl", icon.spec());
516 result_list->Append(task);
517 }
518
519 // TODO(zelidrag, serya): Add intent content tasks to result_list once we
520 // implement that API.
97 SendResponse(true); 521 SendResponse(true);
98 } 522 return true;
99 523 }
524
525 bool ExecuteTasksFileBrowserFunction::RunImpl() {
526 // First param is task id that was to the extension with getFileTasks call.
527 std::string task_id;
528 if (!args_->GetString(0, &task_id) || !task_id.size())
529 return false;
530
531 // The second param is the list of files that need to be executed with this
532 // task.
533 ListValue* files_list = NULL;
534 if (!args_->GetList(1, &files_list))
535 return false;
536
537 if (!files_list->GetSize())
538 return true;
539
540 std::string task_type;
541 std::string target_extension_id;
542 std::string action_id;
543 if (!CrackTaskIdentifier(task_id, &task_type, &target_extension_id,
544 &action_id)) {
545 return false;
546 }
547
548 if (task_type == kContextTaskIdSchema) {
549 ExecuteContextMenuTasks(target_extension_id, action_id, files_list);
550 } else {
551 LOG(WARNING) << "Unsupported task type of: " << task_type;
552 // TODO(zelidrag, serya): Add intent content tasks here once we implement
553 // that API.
554 return false;
555 }
556 SendResponse(true);
557 return true;
558 }
559
560 std::string ExecuteTasksFileBrowserFunction::MakeSafeFileUrl(
561 const std::string& origin_file_url, const std::string& extension_id) {
562 // Replace extension part of the url with one from the target.
563 std::string::size_type pos = origin_file_url.find("/local/");
abarth-chromium 2011/04/06 02:17:32 What about a URL like the following: filesystem:h
zel 2011/04/06 05:25:17 I used GURL as much as I could. This class is not
564 if (pos == std::string::npos)
565 return std::string();
566 std::string file_url = base::StringPrintf(
567 "filesystem:chrome-extension://%s/%s",
568 extension_id.c_str(),
569 origin_file_url.substr(pos + 1).c_str());
570 std::string hash =
571 FileHashGenerator::GetInstance()->GenerateFileHash(file_url,
572 extension_id);
573 file_url = file_url.append("#");
abarth-chromium 2011/04/06 02:17:32 What if the file_url already has a fragment? You
zel 2011/04/06 05:25:17 Same as above.
574 return file_url.append(hash);
575 }
576
577 bool ExecuteTasksFileBrowserFunction::ExecuteContextMenuTasks(
578 const std::string& extension_id, const std::string& action_id,
579 ListValue* files_list) {
580 ExtensionMenuManager* manager =
581 profile_->GetExtensionService()->menu_manager();
582 for (size_t i = 0; i < files_list->GetSize(); i++) {
583 std::string origin_file_url;
584 if (!files_list->GetString(i, &origin_file_url)) {
585 result_.reset(new FundamentalValue(false));
586 SendResponse(true);
587 return false;
588 }
589 std::string file_url = MakeSafeFileUrl(origin_file_url, extension_id);
590 if (!file_url.size()) {
591 result_.reset(new FundamentalValue(false));
592 SendResponse(true);
593 return false;
594 }
595 ContextMenuParams params;
596 CreateContextMenuParams(source_url_, file_url, &params);
597 ExtensionMenuItem::Id menuItemId(profile_, extension_id,
598 atoi(action_id.c_str()));
599 manager->ExecuteCommand(profile_,
600 NULL, // tab_contents, not needed in args.
601 params,
602 menuItemId);
603 }
604 result_.reset(new FundamentalValue(true));
605 SendResponse(true);
606 return true;
607 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698