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/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, ¶ms); | |
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, ¶ms); | |
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 } | |
OLD | NEW |