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

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

Powered by Google App Engine
This is Rietveld 408576698