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