OLD | NEW |
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/crypto/symmetric_key.h" |
| 9 #include "base/hmac.h" |
7 #include "base/json/json_writer.h" | 10 #include "base/json/json_writer.h" |
| 11 #include "base/memory/singleton.h" |
| 12 #include "base/stringprintf.h" |
8 #include "base/task.h" | 13 #include "base/task.h" |
9 #include "base/values.h" | 14 #include "base/values.h" |
10 #include "chrome/browser/profiles/profile.h" | 15 #include "chrome/browser/profiles/profile.h" |
| 16 #include "chrome/browser/extensions/extension_function_dispatcher.h" |
| 17 #include "chrome/browser/extensions/extension_service.h" |
| 18 #include "chrome/browser/tab_contents/context_menu_utils.h" |
| 19 #include "chrome/browser/ui/webui/extension_icon_source.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" |
13 #include "webkit/fileapi/file_system_operation.h" | 27 #include "webkit/fileapi/file_system_operation.h" |
| 28 #include "webkit/fileapi/file_system_path_manager.h" |
14 #include "webkit/fileapi/file_system_types.h" | 29 #include "webkit/fileapi/file_system_types.h" |
| 30 #include "webkit/glue/context_menu.h" |
| 31 |
| 32 #define SHA1_SIZE_IN_BITS 160 |
| 33 |
| 34 const char kContextTaskIdSchema[] = "context-task"; |
| 35 |
| 36 bool GetContextMenuItems(Profile* profile, |
| 37 const ContextMenuParams& params, |
| 38 ExtensionMenuItem::List* results) { |
| 39 ExtensionService* service = profile->GetExtensionService(); |
| 40 if (!service) |
| 41 return false; // In unit-tests, we may not have an ExtensionService. |
| 42 |
| 43 // Get a list of extension id's that have context menu items, and sort it by |
| 44 // the extension's name. |
| 45 ExtensionMenuManager* menu_manager = service->menu_manager(); |
| 46 std::set<std::string> ids = menu_manager->ExtensionIds(); |
| 47 std::vector<std::pair<std::string, std::string> > sorted_ids; |
| 48 for (std::set<std::string>::iterator i = ids.begin(); i != ids.end(); ++i) { |
| 49 const Extension* extension = service->GetExtensionById(*i, false); |
| 50 if (extension) |
| 51 sorted_ids.push_back( |
| 52 std::pair<std::string, std::string>(extension->name(), *i)); |
| 53 } |
| 54 |
| 55 if (sorted_ids.empty()) |
| 56 return true; |
| 57 |
| 58 std::vector<std::pair<std::string, std::string> >::const_iterator i; |
| 59 for (i = sorted_ids.begin(); i != sorted_ids.end(); ++i) { |
| 60 const std::string& extension_id = i->second; |
| 61 const Extension* extension = service->GetExtensionById(extension_id, false); |
| 62 bool can_cross_incognito = service->CanCrossIncognito(extension); |
| 63 const ExtensionMenuItem::List* all_items = |
| 64 menu_manager->MenuItems(extension_id); |
| 65 ExtensionMenuItem::List relevant_items = |
| 66 ContextMenuUtils::GetRelevantExtensionItems(*all_items, |
| 67 params, |
| 68 profile, |
| 69 can_cross_incognito); |
| 70 results->insert(results->end(), relevant_items.begin(), |
| 71 relevant_items.end()); |
| 72 } |
| 73 return true; |
| 74 } |
| 75 |
| 76 void CreateContextMenuParams(const GURL& source_url, |
| 77 const std::string& file_url, |
| 78 ContextMenuParams* params) { |
| 79 DCHECK(params); |
| 80 params->is_image_blocked = false; |
| 81 params->spellcheck_enabled = false; |
| 82 params->is_editable = false; |
| 83 params->media_flags = 0; |
| 84 params->edit_flags = 0; |
| 85 params->media_type = WebKit::WebContextMenuData::MediaTypeFile; |
| 86 params->src_url = GURL(file_url); |
| 87 params->page_url = source_url; |
| 88 } |
| 89 |
| 90 // Given the list of selected files, returns array of context menu tasks |
| 91 // that are sahred |
| 92 bool FindCommonTasks(Profile* profile, |
| 93 const GURL& source_url, |
| 94 ListValue* files_list, |
| 95 ExtensionMenuItem::List* common_tasks) { |
| 96 common_tasks->clear(); |
| 97 for (size_t i = 0; i < files_list->GetSize(); ++i) { |
| 98 std::string file_url; |
| 99 if (!files_list->GetString(i, &file_url)) |
| 100 return false; |
| 101 |
| 102 ContextMenuParams params; |
| 103 CreateContextMenuParams(source_url, file_url, ¶ms); |
| 104 |
| 105 ExtensionMenuItem::List file_actions; |
| 106 if (!GetContextMenuItems(profile, params, &file_actions)) |
| 107 return false; |
| 108 // If there is nothing to do for one file, the intersection of tasks for all |
| 109 // files will be empty at the end. |
| 110 if (!file_actions.size()) { |
| 111 common_tasks->clear(); |
| 112 return true; |
| 113 } |
| 114 // For the very first file, just copy elements. |
| 115 if (i == 0) { |
| 116 common_tasks->insert(common_tasks->begin(), |
| 117 file_actions.begin(), |
| 118 file_actions.end()); |
| 119 std::sort(common_tasks->begin(), common_tasks->end()); |
| 120 } else if (common_tasks->size()) { |
| 121 // For all additional files, find intersection between the accumulated |
| 122 // and file specific set. |
| 123 std::sort(file_actions.begin(), file_actions.end()); |
| 124 ExtensionMenuItem::List intersection(common_tasks->size()); |
| 125 ExtensionMenuItem::List::iterator intersection_end = |
| 126 std::set_intersection(common_tasks->begin(), |
| 127 common_tasks->end(), |
| 128 file_actions.begin(), |
| 129 file_actions.end(), |
| 130 intersection.begin()); |
| 131 common_tasks->clear(); |
| 132 common_tasks->insert(common_tasks->begin(), |
| 133 intersection.begin(), |
| 134 intersection_end); |
| 135 std::sort(common_tasks->begin(), common_tasks->end()); |
| 136 } |
| 137 } |
| 138 return true; |
| 139 } |
| 140 |
| 141 // Breaks down task_id that is used between getFileTasks() and executeTask() on |
| 142 // its building blocks. task_id field the following structure: |
| 143 // <task-type>:<extension-id>/<task-action-id> |
| 144 // Currently, the only supported task-type is of 'context'. |
| 145 bool CrackTaskIdentifier(const std::string& task_id, |
| 146 std::string* task_type, |
| 147 std::string* target_extension_id, |
| 148 std::string* action_id) { |
| 149 std::string::size_type pos_col = task_id.find(':'); |
| 150 if (pos_col == std::string::npos) |
| 151 return false; |
| 152 *task_type = task_id.substr(0, pos_col); |
| 153 std::string::size_type pos_slash = task_id.find('/'); |
| 154 if (pos_slash == std::string::npos) |
| 155 return false; |
| 156 *target_extension_id = task_id.substr(pos_col + 1, pos_slash - pos_col - 1); |
| 157 *action_id = task_id.substr(pos_slash + 1); |
| 158 return true; |
| 159 } |
| 160 |
| 161 std::string MakeTaskID(const char* task_schema, |
| 162 const char* extension_id, |
| 163 int action_id) { |
| 164 return base::StringPrintf("%s:%s/%d", task_schema, extension_id, action_id); |
| 165 } |
| 166 |
| 167 // Generates hashes used for filesystem: urls that are sent to 3rd party |
| 168 // extension. Hashes generated from extension id and file url. |
| 169 // They are valid only during the lifetime of the browser instance. |
| 170 class FileHashGenerator { |
| 171 public: |
| 172 ~FileHashGenerator() {} |
| 173 static FileHashGenerator* GetInstance() { |
| 174 return Singleton<FileHashGenerator>::get(); |
| 175 } |
| 176 // Generate hash for given url and extension combination. |
| 177 std::string GenerateFileHash(const std::string& file_url, |
| 178 const std::string& extension_id) { |
| 179 std::string data(file_url); |
| 180 data = data.append(extension_id); |
| 181 scoped_ptr<unsigned char> digest(new unsigned char[SHA1_SIZE_IN_BITS/8]); |
| 182 if (!hmac_.Sign(data, digest.get(), SHA1_SIZE_IN_BITS/8)) |
| 183 return std::string(); |
| 184 std::string output; |
| 185 if (!base::Base64Encode(std::string(reinterpret_cast<char*>(digest.get()), |
| 186 SHA1_SIZE_IN_BITS/8), |
| 187 &output)) |
| 188 return std::string(); |
| 189 return output; |
| 190 } |
| 191 |
| 192 private: |
| 193 friend struct DefaultSingletonTraits<FileHashGenerator>; |
| 194 |
| 195 FileHashGenerator() : hmac_(base::HMAC::SHA1) { |
| 196 scoped_ptr<base::SymmetricKey> sym_key( |
| 197 base::SymmetricKey::GenerateRandomKey(base::SymmetricKey::AES, |
| 198 SHA1_SIZE_IN_BITS)); |
| 199 std::string raw_key; |
| 200 sym_key->GetRawKey(&raw_key); |
| 201 hmac_.Init(raw_key); |
| 202 } |
| 203 |
| 204 base::HMAC hmac_; |
| 205 DISALLOW_COPY_AND_ASSIGN(FileHashGenerator); |
| 206 }; |
15 | 207 |
16 class LocalFileSystemCallbackDispatcher | 208 class LocalFileSystemCallbackDispatcher |
17 : public fileapi::FileSystemCallbackDispatcher { | 209 : public fileapi::FileSystemCallbackDispatcher { |
18 public: | 210 public: |
19 explicit LocalFileSystemCallbackDispatcher( | 211 explicit LocalFileSystemCallbackDispatcher( |
20 RequestLocalFileSystemFunction* function) : function_(function) { | 212 RequestLocalFileSystemFunctionBase* function, |
| 213 Profile* profile, |
| 214 int child_id, |
| 215 const GURL& source_url, |
| 216 const std::string& file_url) |
| 217 : function_(function), |
| 218 profile_(profile), |
| 219 child_id_(child_id), |
| 220 source_url_(source_url), |
| 221 file_url_(file_url) { |
21 DCHECK(function_); | 222 DCHECK(function_); |
22 } | 223 } |
23 // fileapi::FileSystemCallbackDispatcher overrides. | 224 // fileapi::FileSystemCallbackDispatcher overrides. |
24 virtual void DidSucceed() OVERRIDE { | 225 virtual void DidSucceed() OVERRIDE { |
25 NOTREACHED(); | 226 NOTREACHED(); |
26 } | 227 } |
27 virtual void DidReadMetadata(const base::PlatformFileInfo& info, | 228 virtual void DidReadMetadata(const base::PlatformFileInfo& info, |
28 const FilePath& unused) OVERRIDE { | 229 const FilePath& unused) OVERRIDE { |
29 NOTREACHED(); | 230 NOTREACHED(); |
30 } | 231 } |
31 virtual void DidReadDirectory( | 232 virtual void DidReadDirectory( |
32 const std::vector<base::FileUtilProxy::Entry>& entries, | 233 const std::vector<base::FileUtilProxy::Entry>& entries, |
33 bool has_more) OVERRIDE { | 234 bool has_more) OVERRIDE { |
34 NOTREACHED(); | 235 NOTREACHED(); |
35 } | 236 } |
36 virtual void DidWrite(int64 bytes, bool complete) OVERRIDE { | 237 virtual void DidWrite(int64 bytes, bool complete) OVERRIDE { |
37 NOTREACHED(); | 238 NOTREACHED(); |
38 } | 239 } |
39 virtual void DidOpenFileSystem(const std::string& name, | 240 virtual void DidOpenFileSystem(const std::string& name, |
40 const FilePath& path) OVERRIDE { | 241 const FilePath& path) OVERRIDE { |
| 242 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 243 // Set up file permission access. |
| 244 if (file_url_.length()) { |
| 245 if (!SetupFileAccessPermissions()) { |
| 246 DidFail(base::PLATFORM_FILE_ERROR_SECURITY); |
| 247 return; |
| 248 } |
| 249 } |
| 250 |
41 BrowserThread::PostTask( | 251 BrowserThread::PostTask( |
42 BrowserThread::UI, FROM_HERE, | 252 BrowserThread::UI, FROM_HERE, |
43 NewRunnableMethod(function_, | 253 NewRunnableMethod(function_, |
44 &RequestLocalFileSystemFunction::RespondSuccessOnUIThread, | 254 &RequestLocalFileSystemFunctionBase::RespondSuccessOnUIThread, |
45 name, | 255 name, |
46 path)); | 256 path, |
| 257 file_url_)); |
47 } | 258 } |
48 virtual void DidFail(base::PlatformFileError error_code) OVERRIDE { | 259 virtual void DidFail(base::PlatformFileError error_code) OVERRIDE { |
49 BrowserThread::PostTask( | 260 BrowserThread::PostTask( |
50 BrowserThread::UI, FROM_HERE, | 261 BrowserThread::UI, FROM_HERE, |
51 NewRunnableMethod(function_, | 262 NewRunnableMethod(function_, |
52 &RequestLocalFileSystemFunction::RespondFailedOnUIThread, | 263 &RequestLocalFileSystemFunctionBase::RespondFailedOnUIThread, |
53 error_code)); | 264 error_code)); |
54 } | 265 } |
55 private: | 266 private: |
56 RequestLocalFileSystemFunction* function_; | 267 |
| 268 // Checks legitimacy of file url and grants access permissions. |
| 269 bool SetupFileAccessPermissions() { |
| 270 GURL file_origin_url; |
| 271 FilePath virtual_path; |
| 272 fileapi::FileSystemType type; |
| 273 fileapi::FileSystemPathManager* path_manager = |
| 274 profile_->GetFileSystemContext()->path_manager(); |
| 275 |
| 276 // Check file url hash first. |
| 277 std::string::size_type pos = file_url_.find('#'); |
| 278 if (pos == std::string::npos) |
| 279 return false; |
| 280 std::string url_hash = file_url_.substr(pos + 1); |
| 281 std::string file_url = file_url_.substr(0, pos); |
| 282 std::string extension_id = source_url_.GetOrigin().host(); |
| 283 std::string expected_hash = FileHashGenerator::GetInstance()-> |
| 284 GenerateFileHash(file_url, extension_id); |
| 285 if (expected_hash != url_hash) |
| 286 return false; |
| 287 |
| 288 if (!path_manager->CrackFileSystemPath(FilePath(file_url), |
| 289 &file_origin_url, |
| 290 &type, |
| 291 &virtual_path)) { |
| 292 return false; |
| 293 } |
| 294 // Make sure this url really used by the right caller extension. |
| 295 if (source_url_.GetOrigin() != file_origin_url) { |
| 296 DidFail(base::PLATFORM_FILE_ERROR_SECURITY); |
| 297 return false; |
| 298 } |
| 299 FilePath root_path = path_manager->GetFileSystemRootPathOnFileThread( |
| 300 file_origin_url, |
| 301 fileapi::kFileSystemTypeLocal, |
| 302 FilePath(virtual_path), |
| 303 false); // create |
| 304 FilePath finalFilePath = root_path.Append(virtual_path); |
| 305 // Grant read access permission to this file. |
| 306 ChildProcessSecurityPolicy::GetInstance()->GrantReadFile(child_id_, |
| 307 finalFilePath); |
| 308 return true; |
| 309 } |
| 310 |
| 311 RequestLocalFileSystemFunctionBase* function_; |
| 312 Profile* profile_; |
| 313 // Renderer process id. |
| 314 int child_id_; |
| 315 // Extension source URL. |
| 316 GURL source_url_; |
| 317 std::string file_url_; |
57 DISALLOW_COPY_AND_ASSIGN(LocalFileSystemCallbackDispatcher); | 318 DISALLOW_COPY_AND_ASSIGN(LocalFileSystemCallbackDispatcher); |
58 }; | 319 }; |
59 | 320 |
60 RequestLocalFileSystemFunction::RequestLocalFileSystemFunction() { | 321 void RequestLocalFileSystemFunctionBase::RequestOnFileThread( |
61 } | 322 const GURL& source_url, const std::string& file_url) { |
62 | |
63 RequestLocalFileSystemFunction::~RequestLocalFileSystemFunction() { | |
64 } | |
65 | |
66 bool RequestLocalFileSystemFunction::RunImpl() { | |
67 fileapi::FileSystemOperation* operation = | 323 fileapi::FileSystemOperation* operation = |
68 new fileapi::FileSystemOperation( | 324 new fileapi::FileSystemOperation( |
69 new LocalFileSystemCallbackDispatcher(this), | 325 new LocalFileSystemCallbackDispatcher( |
| 326 this, |
| 327 profile(), |
| 328 dispatcher()->render_view_host()->process()->id(), |
| 329 source_url, |
| 330 file_url), |
70 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), | 331 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), |
71 profile()->GetFileSystemContext(), | 332 profile()->GetFileSystemContext(), |
72 NULL); | 333 NULL); |
73 GURL origin_url = source_url().GetOrigin(); | 334 GURL origin_url = source_url.GetOrigin(); |
74 operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeLocal, | 335 operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeLocal, |
75 false); // create | 336 false); // create |
| 337 } |
| 338 |
| 339 bool RequestLocalFileSystemFunctionBase::RunImpl() { |
| 340 std::string file_url; |
| 341 if (args_->GetSize()) |
| 342 args_->GetString(0, &file_url); |
| 343 BrowserThread::PostTask( |
| 344 BrowserThread::FILE, FROM_HERE, |
| 345 NewRunnableMethod(this, |
| 346 &RequestLocalFileSystemFunctionBase::RequestOnFileThread, |
| 347 source_url_, |
| 348 file_url)); |
76 // Will finish asynchronously. | 349 // Will finish asynchronously. |
77 return true; | 350 return true; |
78 } | 351 } |
79 | 352 |
80 void RequestLocalFileSystemFunction::RespondSuccessOnUIThread( | 353 void RequestLocalFileSystemFunctionBase::RespondSuccessOnUIThread( |
81 const std::string& name, const FilePath& path) { | 354 const std::string& name, const FilePath& path, |
| 355 const std::string& file_url) { |
82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 356 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
83 result_.reset(new DictionaryValue()); | 357 result_.reset(new DictionaryValue()); |
84 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get()); | 358 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get()); |
85 dict->SetString("name", name); | 359 dict->SetString("name", name); |
86 dict->SetString("path", path.value()); | 360 dict->SetString("path", path.value()); |
87 dict->SetInteger("error", base::PLATFORM_FILE_OK); | 361 dict->SetInteger("error", base::PLATFORM_FILE_OK); |
| 362 if (file_url.size()) |
| 363 dict->SetString("fileUrl", file_url); |
88 SendResponse(true); | 364 SendResponse(true); |
89 } | 365 } |
90 | 366 |
91 void RequestLocalFileSystemFunction::RespondFailedOnUIThread( | 367 void RequestLocalFileSystemFunctionBase::RespondFailedOnUIThread( |
92 base::PlatformFileError error_code) { | 368 base::PlatformFileError error_code) { |
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
94 result_.reset(new DictionaryValue()); | 370 result_.reset(new DictionaryValue()); |
95 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get()); | 371 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get()); |
96 dict->SetInteger("error", static_cast<int>(error_code)); | 372 dict->SetInteger("error", static_cast<int>(error_code)); |
97 SendResponse(true); | 373 SendResponse(true); |
98 } | 374 } |
99 | 375 |
| 376 bool GetFileTasksFileBrowserFunction::RunImpl() { |
| 377 ListValue* files_list = NULL; |
| 378 if (!args_->GetList(0, &files_list)) |
| 379 return false; |
| 380 |
| 381 result_.reset(new ListValue()); |
| 382 ListValue* result_list = reinterpret_cast<ListValue*>(result_.get()); |
| 383 |
| 384 ExtensionMenuItem::List common_tasks; |
| 385 if (!FindCommonTasks(profile_, source_url_, files_list, &common_tasks)) |
| 386 return false; |
| 387 |
| 388 ExtensionService* service = profile_->GetExtensionService(); |
| 389 for (ExtensionMenuItem::List::iterator iter = common_tasks.begin(); |
| 390 iter != common_tasks.end(); |
| 391 ++iter) { |
| 392 if ((*iter)->type() != ExtensionMenuItem::NORMAL) |
| 393 continue; |
| 394 const std::string extension_id = (*iter)->extension_id(); |
| 395 const Extension* extension = service->GetExtensionById(extension_id, false); |
| 396 if (!extension) { |
| 397 LOG(WARNING) << "Disabled extension" << extension_id; |
| 398 continue; |
| 399 } |
| 400 DictionaryValue* task = new DictionaryValue(); |
| 401 task->SetString("taskId", MakeTaskID(kContextTaskIdSchema, |
| 402 extension_id.c_str(), |
| 403 (*iter)->id().uid)); |
| 404 task->SetString("title", (*iter)->title()); |
| 405 GURL icon = |
| 406 ExtensionIconSource::GetIconURL(extension, |
| 407 Extension::EXTENSION_ICON_SMALLISH, |
| 408 ExtensionIconSet::MATCH_BIGGER, |
| 409 false); // grayscale |
| 410 task->SetString("iconUrl", icon.spec()); |
| 411 result_list->Append(task); |
| 412 } |
| 413 |
| 414 // TODO(zelidrag, serya): Add intent content tasks to result_list once we |
| 415 // implement that API. |
| 416 SendResponse(true); |
| 417 return true; |
| 418 } |
| 419 |
| 420 bool ExecuteTasksFileBrowserFunction::RunImpl() { |
| 421 // First param is task id that was to the extension with getFileTasks call. |
| 422 std::string task_id; |
| 423 if (!args_->GetString(0, &task_id) || !task_id.size()) |
| 424 return false; |
| 425 |
| 426 // The second param is the list of files that need to be executed with this |
| 427 // task. |
| 428 ListValue* files_list = NULL; |
| 429 if (!args_->GetList(1, &files_list)) |
| 430 return false; |
| 431 |
| 432 if (!files_list->GetSize()) |
| 433 return true; |
| 434 |
| 435 std::string task_type; |
| 436 std::string target_extension_id; |
| 437 std::string action_id; |
| 438 if (!CrackTaskIdentifier(task_id, &task_type, &target_extension_id, |
| 439 &action_id)) { |
| 440 return false; |
| 441 } |
| 442 |
| 443 if (task_type == kContextTaskIdSchema) { |
| 444 ExecuteContextMenuTasks(target_extension_id, action_id, files_list); |
| 445 } else { |
| 446 LOG(WARNING) << "Unsupported task type of: " << task_type; |
| 447 // TODO(zelidrag, serya): Add intent content tasks here once we implement |
| 448 // that API. |
| 449 return false; |
| 450 } |
| 451 SendResponse(true); |
| 452 return true; |
| 453 } |
| 454 |
| 455 std::string ExecuteTasksFileBrowserFunction::MakeSafeFileUrl( |
| 456 const std::string& origin_file_url, const std::string& extension_id) { |
| 457 // Replace extension part of the url with one from the target. |
| 458 std::string::size_type pos = origin_file_url.find("/local/"); |
| 459 if (pos == std::string::npos) |
| 460 return std::string(); |
| 461 std::string file_url = base::StringPrintf( |
| 462 "filesystem:chrome-extension://%s/%s", |
| 463 extension_id.c_str(), |
| 464 origin_file_url.substr(pos + 1).c_str()); |
| 465 std::string hash = |
| 466 FileHashGenerator::GetInstance()->GenerateFileHash(file_url, |
| 467 extension_id); |
| 468 file_url = file_url.append("#"); |
| 469 return file_url.append(hash); |
| 470 } |
| 471 |
| 472 bool ExecuteTasksFileBrowserFunction::ExecuteContextMenuTasks( |
| 473 const std::string& extension_id, const std::string& action_id, |
| 474 ListValue* files_list) { |
| 475 ExtensionMenuManager* manager = |
| 476 profile_->GetExtensionService()->menu_manager(); |
| 477 for (size_t i = 0; i < files_list->GetSize(); i++) { |
| 478 std::string origin_file_url; |
| 479 if (!files_list->GetString(i, &origin_file_url)) { |
| 480 result_.reset(new FundamentalValue(false)); |
| 481 SendResponse(true); |
| 482 return false; |
| 483 } |
| 484 std::string file_url = MakeSafeFileUrl(origin_file_url, extension_id); |
| 485 if (!file_url.size()) { |
| 486 result_.reset(new FundamentalValue(false)); |
| 487 SendResponse(true); |
| 488 return false; |
| 489 } |
| 490 ContextMenuParams params; |
| 491 CreateContextMenuParams(source_url_, file_url, ¶ms); |
| 492 ExtensionMenuItem::Id menuItemId(profile_, extension_id, |
| 493 atoi(action_id.c_str())); |
| 494 manager->ExecuteCommand(profile_, |
| 495 NULL, // tab_contents, not needed in args. |
| 496 params, |
| 497 menuItemId); |
| 498 } |
| 499 result_.reset(new FundamentalValue(true)); |
| 500 SendResponse(true); |
| 501 return true; |
| 502 } |
OLD | NEW |