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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/extensions/extension_file_browser_private_api.h"
6
7 #include "base/base64.h"
8 #include "base/command_line.h"
9 #include "base/json/json_writer.h"
10 #include "base/logging.h"
11 #include "base/memory/singleton.h"
12 #include "base/stringprintf.h"
13 #include "base/string_util.h"
14 #include "base/task.h"
15 #include "base/time.h"
16 #include "base/values.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/extensions/extension_event_router.h"
19 #include "chrome/browser/extensions/extension_function_dispatcher.h"
20 #include "chrome/browser/extensions/extension_process_manager.h"
21 #include "chrome/browser/extensions/extension_service.h"
22 #include "chrome/browser/extensions/extension_tabs_module.h"
23 #include "chrome/browser/extensions/file_manager_util.h"
24 #include "chrome/browser/prefs/pref_service.h"
25 #include "chrome/browser/prefs/scoped_user_pref_update.h"
26 #include "chrome/browser/ui/browser.h"
27 #include "chrome/browser/ui/views/html_dialog_view.h"
28 #include "chrome/browser/ui/webui/extension_icon_source.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/extensions/extension.h"
31 #include "chrome/common/extensions/file_browser_handler.h"
32 #include "chrome/common/pref_names.h"
33 #include "content/browser/browser_thread.h"
34 #include "content/browser/child_process_security_policy.h"
35 #include "content/browser/renderer_host/render_process_host.h"
36 #include "content/browser/renderer_host/render_view_host.h"
37 #include "content/browser/tab_contents/tab_contents.h"
38 #include "googleurl/src/gurl.h"
39 #include "grit/generated_resources.h"
40 #include "webkit/fileapi/file_system_context.h"
41 #include "webkit/fileapi/file_system_mount_point_provider.h"
42 #include "webkit/fileapi/file_system_operation.h"
43 #include "webkit/fileapi/file_system_operation_context.h"
44 #include "webkit/fileapi/file_system_path_manager.h"
45 #include "webkit/fileapi/file_system_types.h"
46 #include "webkit/fileapi/file_system_util.h"
47 #include "webkit/fileapi/file_system_file_util.h"
48 #include "webkit/fileapi/local_file_system_file_util.h"
49 #include "ui/base/l10n/l10n_util.h"
50
51 // Error messages.
52 const char kFileError[] = "File error %d";
53 const char kInvalidFileUrl[] = "Invalid file URL";
54
55 // Internal task ids.
56 const char kEnqueueTaskId[] = "enqueue";
57
58 const int kReadOnlyFilePermissions = base::PLATFORM_FILE_OPEN |
59 base::PLATFORM_FILE_READ |
60 base::PLATFORM_FILE_EXCLUSIVE_READ |
61 base::PLATFORM_FILE_ASYNC;
62
63 const int kReadWriteFilePermissions = base::PLATFORM_FILE_OPEN |
64 base::PLATFORM_FILE_CREATE |
65 base::PLATFORM_FILE_OPEN_ALWAYS |
66 base::PLATFORM_FILE_CREATE_ALWAYS |
67 base::PLATFORM_FILE_READ |
68 base::PLATFORM_FILE_WRITE |
69 base::PLATFORM_FILE_EXCLUSIVE_READ |
70 base::PLATFORM_FILE_EXCLUSIVE_WRITE |
71 base::PLATFORM_FILE_ASYNC |
72 base::PLATFORM_FILE_TRUNCATE |
73 base::PLATFORM_FILE_WRITE_ATTRIBUTES;
74
75 typedef std::pair<int, const FileBrowserHandler* > LastUsedHandler;
76 typedef std::vector<LastUsedHandler> LastUsedHandlerList;
77
78 typedef std::vector<const FileBrowserHandler*> ActionList;
79
80
81 // Breaks down task_id that is used between getFileTasks() and executeTask() on
82 // its building blocks. task_id field the following structure:
83 // <extension-id>|<task-action-id>
84 // Currently, the only supported task-type is of 'context'.
CrackTaskIdentifier(const std::string & task_id,std::string * target_extension_id,std::string * action_id)85 bool CrackTaskIdentifier(const std::string& task_id,
86 std::string* target_extension_id,
87 std::string* action_id) {
88 std::vector<std::string> result;
89 int count = Tokenize(task_id, std::string("|"), &result);
90 if (count != 2)
91 return false;
92 *target_extension_id = result[0];
93 *action_id = result[1];
94 return true;
95 }
96
MakeTaskID(const char * extension_id,const char * action_id)97 std::string MakeTaskID(const char* extension_id,
98 const char* action_id) {
99 return base::StringPrintf("%s|%s", extension_id, action_id);
100 }
101
GetFileBrowserHandlers(Profile * profile,const GURL & selected_file_url,ActionList * results)102 bool GetFileBrowserHandlers(Profile* profile,
103 const GURL& selected_file_url,
104 ActionList* results) {
105 ExtensionService* service = profile->GetExtensionService();
106 if (!service)
107 return false; // In unit-tests, we may not have an ExtensionService.
108
109 for (ExtensionList::const_iterator iter = service->extensions()->begin();
110 iter != service->extensions()->end();
111 ++iter) {
112 const Extension* extension = iter->get();
113 if (!extension->file_browser_handlers())
114 continue;
115
116 for (Extension::FileBrowserHandlerList::const_iterator action_iter =
117 extension->file_browser_handlers()->begin();
118 action_iter != extension->file_browser_handlers()->end();
119 ++action_iter) {
120 const FileBrowserHandler* action = action_iter->get();
121 if (!action->MatchesURL(selected_file_url))
122 continue;
123
124 results->push_back(action_iter->get());
125 }
126 }
127 return true;
128 }
129
SortByLastUsedTimestampDesc(const LastUsedHandler & a,const LastUsedHandler & b)130 bool SortByLastUsedTimestampDesc(const LastUsedHandler& a,
131 const LastUsedHandler& b) {
132 return a.first > b.first;
133 }
134
135 // TODO(zelidrag): Wire this with ICU to make this sort I18N happy.
SortByTaskName(const LastUsedHandler & a,const LastUsedHandler & b)136 bool SortByTaskName(const LastUsedHandler& a, const LastUsedHandler& b) {
137 return base::strcasecmp(a.second->title().data(),
138 b.second->title().data()) > 0;
139 }
140
141 // Given the list of selected files, returns array of context menu tasks
142 // that are shared
FindCommonTasks(Profile * profile,ListValue * files_list,LastUsedHandlerList * named_action_list)143 bool FindCommonTasks(Profile* profile,
144 ListValue* files_list,
145 LastUsedHandlerList* named_action_list) {
146 named_action_list->clear();
147 ActionList common_tasks;
148 for (size_t i = 0; i < files_list->GetSize(); ++i) {
149 std::string file_url;
150 if (!files_list->GetString(i, &file_url))
151 return false;
152
153 ActionList file_actions;
154 if (!GetFileBrowserHandlers(profile, GURL(file_url), &file_actions))
155 return false;
156 // If there is nothing to do for one file, the intersection of tasks for all
157 // files will be empty at the end.
158 if (!file_actions.size()) {
159 common_tasks.clear();
160 return true;
161 }
162 // For the very first file, just copy elements.
163 if (i == 0) {
164 common_tasks.insert(common_tasks.begin(),
165 file_actions.begin(),
166 file_actions.end());
167 std::sort(common_tasks.begin(), common_tasks.end());
168 } else if (common_tasks.size()) {
169 // For all additional files, find intersection between the accumulated
170 // and file specific set.
171 std::sort(file_actions.begin(), file_actions.end());
172 ActionList intersection(common_tasks.size());
173 ActionList::iterator intersection_end =
174 std::set_intersection(common_tasks.begin(),
175 common_tasks.end(),
176 file_actions.begin(),
177 file_actions.end(),
178 intersection.begin());
179 common_tasks.clear();
180 common_tasks.insert(common_tasks.begin(),
181 intersection.begin(),
182 intersection_end);
183 std::sort(common_tasks.begin(), common_tasks.end());
184 }
185 }
186
187 const DictionaryValue* prefs_tasks =
188 profile->GetPrefs()->GetDictionary(prefs::kLastUsedFileBrowserHandlers);
189 for (ActionList::const_iterator iter = common_tasks.begin();
190 iter != common_tasks.end(); ++iter) {
191 // Get timestamp of when this task was used last time.
192 int last_used_timestamp = 0;
193 prefs_tasks->GetInteger(MakeTaskID((*iter)->extension_id().data(),
194 (*iter)->id().data()),
195 &last_used_timestamp);
196 named_action_list->push_back(LastUsedHandler(last_used_timestamp, *iter));
197 }
198 // Sort by the last used descending.
199 std::sort(named_action_list->begin(), named_action_list->end(),
200 SortByLastUsedTimestampDesc);
201 if (named_action_list->size() > 1) {
202 // Sort the rest by name.
203 std::sort(named_action_list->begin() + 1, named_action_list->end(),
204 SortByTaskName);
205 }
206 return true;
207 }
208
209 // Update file handler usage stats.
UpdateFileHandlerUsageStats(Profile * profile,const std::string & task_id)210 void UpdateFileHandlerUsageStats(Profile* profile, const std::string& task_id) {
211 if (!profile || !profile->GetPrefs())
212 return;
213 DictionaryPrefUpdate prefs_usage_update(profile->GetPrefs(),
214 prefs::kLastUsedFileBrowserHandlers);
215 prefs_usage_update->SetWithoutPathExpansion(task_id,
216 new FundamentalValue(
217 static_cast<int>(base::Time::Now().ToInternalValue()/
218 base::Time::kMicrosecondsPerSecond)));
219 }
220
221
222 class LocalFileSystemCallbackDispatcher
223 : public fileapi::FileSystemCallbackDispatcher {
224 public:
LocalFileSystemCallbackDispatcher(RequestLocalFileSystemFunction * function,Profile * profile,int child_id,scoped_refptr<const Extension> extension)225 explicit LocalFileSystemCallbackDispatcher(
226 RequestLocalFileSystemFunction* function,
227 Profile* profile,
228 int child_id,
229 scoped_refptr<const Extension> extension)
230 : function_(function),
231 profile_(profile),
232 child_id_(child_id),
233 extension_(extension) {
234 DCHECK(function_);
235 }
236
237 // fileapi::FileSystemCallbackDispatcher overrides.
DidSucceed()238 virtual void DidSucceed() OVERRIDE {
239 NOTREACHED();
240 }
241
DidGetLocalPath(const FilePath & local_path)242 virtual void DidGetLocalPath(const FilePath& local_path) {
243 NOTREACHED();
244 }
245
DidReadMetadata(const base::PlatformFileInfo & info,const FilePath & unused)246 virtual void DidReadMetadata(const base::PlatformFileInfo& info,
247 const FilePath& unused) OVERRIDE {
248 NOTREACHED();
249 }
250
DidReadDirectory(const std::vector<base::FileUtilProxy::Entry> & entries,bool has_more)251 virtual void DidReadDirectory(
252 const std::vector<base::FileUtilProxy::Entry>& entries,
253 bool has_more) OVERRIDE {
254 NOTREACHED();
255 }
256
DidWrite(int64 bytes,bool complete)257 virtual void DidWrite(int64 bytes, bool complete) OVERRIDE {
258 NOTREACHED();
259 }
260
DidOpenFileSystem(const std::string & name,const GURL & root_path)261 virtual void DidOpenFileSystem(const std::string& name,
262 const GURL& root_path) OVERRIDE {
263 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
264 // Set up file permission access.
265 if (!SetupFileSystemAccessPermissions()) {
266 DidFail(base::PLATFORM_FILE_ERROR_SECURITY);
267 return;
268 }
269
270 BrowserThread::PostTask(
271 BrowserThread::UI, FROM_HERE,
272 NewRunnableMethod(function_,
273 &RequestLocalFileSystemFunction::RespondSuccessOnUIThread,
274 name,
275 root_path));
276 }
277
DidFail(base::PlatformFileError error_code)278 virtual void DidFail(base::PlatformFileError error_code) OVERRIDE {
279 BrowserThread::PostTask(
280 BrowserThread::UI, FROM_HERE,
281 NewRunnableMethod(function_,
282 &RequestLocalFileSystemFunction::RespondFailedOnUIThread,
283 error_code));
284 }
285
286 private:
287
288 // Grants file system access permissions to file browser component.
SetupFileSystemAccessPermissions()289 bool SetupFileSystemAccessPermissions() {
290 if (!extension_.get())
291 return false;
292
293 // Make sure that only component extension can access the entire
294 // local file system.
295 if (extension_->location() != Extension::COMPONENT
296 #ifndef NDEBUG
297 && !CommandLine::ForCurrentProcess()->HasSwitch(
298 switches::kExposePrivateExtensionApi)
299 #endif
300 ) {
301 NOTREACHED() << "Private method access by non-component extension "
302 << extension_->id();
303 return false;
304 }
305
306 fileapi::FileSystemPathManager* path_manager =
307 profile_->GetFileSystemContext()->path_manager();
308 fileapi::ExternalFileSystemMountPointProvider* provider =
309 path_manager->external_provider();
310 if (!provider)
311 return false;
312
313 // Grant full access to File API from this component extension.
314 provider->GrantFullAccessToExtension(extension_->id());
315
316 // Grant R/W file permissions to the renderer hosting component
317 // extension for all paths exposed by our local file system provider.
318 std::vector<FilePath> root_dirs = provider->GetRootDirectories();
319 for (std::vector<FilePath>::iterator iter = root_dirs.begin();
320 iter != root_dirs.end();
321 ++iter) {
322 ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile(
323 child_id_, *iter, kReadWriteFilePermissions);
324 }
325 return true;
326 }
327
328 RequestLocalFileSystemFunction* function_;
329 Profile* profile_;
330 // Renderer process id.
331 int child_id_;
332 // Extension source URL.
333 scoped_refptr<const Extension> extension_;
334 DISALLOW_COPY_AND_ASSIGN(LocalFileSystemCallbackDispatcher);
335 };
336
RequestOnFileThread(const GURL & source_url,int child_id)337 void RequestLocalFileSystemFunction::RequestOnFileThread(
338 const GURL& source_url, int child_id) {
339 fileapi::FileSystemOperation* operation =
340 new fileapi::FileSystemOperation(
341 new LocalFileSystemCallbackDispatcher(
342 this,
343 profile(),
344 child_id,
345 GetExtension()),
346 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
347 profile()->GetFileSystemContext(),
348 NULL);
349 GURL origin_url = source_url.GetOrigin();
350 operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeExternal,
351 false); // create
352 }
353
RunImpl()354 bool RequestLocalFileSystemFunction::RunImpl() {
355 if (!dispatcher() || !dispatcher()->render_view_host() ||
356 !dispatcher()->render_view_host()->process())
357 return false;
358
359 BrowserThread::PostTask(
360 BrowserThread::FILE, FROM_HERE,
361 NewRunnableMethod(this,
362 &RequestLocalFileSystemFunction::RequestOnFileThread,
363 source_url_,
364 dispatcher()->render_view_host()->process()->id()));
365 // Will finish asynchronously.
366 return true;
367 }
368
RespondSuccessOnUIThread(const std::string & name,const GURL & root_path)369 void RequestLocalFileSystemFunction::RespondSuccessOnUIThread(
370 const std::string& name, const GURL& root_path) {
371 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
372 result_.reset(new DictionaryValue());
373 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get());
374 dict->SetString("name", name);
375 dict->SetString("path", root_path.spec());
376 dict->SetInteger("error", base::PLATFORM_FILE_OK);
377 SendResponse(true);
378 }
379
RespondFailedOnUIThread(base::PlatformFileError error_code)380 void RequestLocalFileSystemFunction::RespondFailedOnUIThread(
381 base::PlatformFileError error_code) {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
383 error_ = base::StringPrintf(kFileError, static_cast<int>(error_code));
384 SendResponse(false);
385 }
386
RunImpl()387 bool GetFileTasksFileBrowserFunction::RunImpl() {
388 ListValue* files_list = NULL;
389 if (!args_->GetList(0, &files_list))
390 return false;
391
392 ListValue* result_list = new ListValue();
393 result_.reset(result_list);
394
395 LastUsedHandlerList common_tasks;
396 if (!FindCommonTasks(profile_, files_list, &common_tasks))
397 return false;
398
399 ExtensionService* service = profile_->GetExtensionService();
400 for (LastUsedHandlerList::const_iterator iter = common_tasks.begin();
401 iter != common_tasks.end();
402 ++iter) {
403 const std::string extension_id = iter->second->extension_id();
404 const Extension* extension = service->GetExtensionById(extension_id, false);
405 CHECK(extension);
406 DictionaryValue* task = new DictionaryValue();
407 task->SetString("taskId", MakeTaskID(extension_id.data(),
408 iter->second->id().data()));
409 task->SetString("title", iter->second->title());
410 // TODO(zelidrag): Figure out how to expose icon URL that task defined in
411 // manifest instead of the default extension icon.
412 GURL icon =
413 ExtensionIconSource::GetIconURL(extension,
414 Extension::EXTENSION_ICON_BITTY,
415 ExtensionIconSet::MATCH_BIGGER,
416 false); // grayscale
417 task->SetString("iconUrl", icon.spec());
418 result_list->Append(task);
419 }
420
421 // TODO(zelidrag, serya): Add intent content tasks to result_list once we
422 // implement that API.
423 SendResponse(true);
424 return true;
425 }
426
427 class ExecuteTasksFileSystemCallbackDispatcher
428 : public fileapi::FileSystemCallbackDispatcher {
429 public:
ExecuteTasksFileSystemCallbackDispatcher(ExecuteTasksFileBrowserFunction * function,Profile * profile,int child_id,const GURL & source_url,scoped_refptr<const Extension> extension,const std::string task_id,const std::vector<GURL> & file_urls)430 explicit ExecuteTasksFileSystemCallbackDispatcher(
431 ExecuteTasksFileBrowserFunction* function,
432 Profile* profile,
433 int child_id,
434 const GURL& source_url,
435 scoped_refptr<const Extension> extension,
436 const std::string task_id,
437 const std::vector<GURL>& file_urls)
438 : function_(function),
439 profile_(profile),
440 source_url_(source_url),
441 extension_(extension),
442 task_id_(task_id),
443 origin_file_urls_(file_urls) {
444 DCHECK(function_);
445 }
446
447 // fileapi::FileSystemCallbackDispatcher overrides.
DidSucceed()448 virtual void DidSucceed() OVERRIDE {
449 NOTREACHED();
450 }
451
DidGetLocalPath(const FilePath & local_path)452 virtual void DidGetLocalPath(const FilePath& local_path) {
453 NOTREACHED();
454 }
455
DidReadMetadata(const base::PlatformFileInfo & info,const FilePath & unused)456 virtual void DidReadMetadata(const base::PlatformFileInfo& info,
457 const FilePath& unused) OVERRIDE {
458 NOTREACHED();
459 }
460
DidReadDirectory(const std::vector<base::FileUtilProxy::Entry> & entries,bool has_more)461 virtual void DidReadDirectory(
462 const std::vector<base::FileUtilProxy::Entry>& entries,
463 bool has_more) OVERRIDE {
464 NOTREACHED();
465 }
466
DidWrite(int64 bytes,bool complete)467 virtual void DidWrite(int64 bytes, bool complete) OVERRIDE {
468 NOTREACHED();
469 }
470
DidOpenFileSystem(const std::string & file_system_name,const GURL & file_system_root)471 virtual void DidOpenFileSystem(const std::string& file_system_name,
472 const GURL& file_system_root) OVERRIDE {
473 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
474 ExecuteTasksFileBrowserFunction::FileDefinitionList file_list;
475 for (std::vector<GURL>::iterator iter = origin_file_urls_.begin();
476 iter != origin_file_urls_.end();
477 ++iter) {
478 // Set up file permission access.
479 ExecuteTasksFileBrowserFunction::FileDefinition file;
480 if (!SetupFileAccessPermissions(*iter, &file.target_file_url,
481 &file.virtual_path, &file.is_directory)) {
482 continue;
483 }
484 file_list.push_back(file);
485 }
486 if (file_list.empty()) {
487 BrowserThread::PostTask(
488 BrowserThread::UI, FROM_HERE,
489 NewRunnableMethod(function_,
490 &ExecuteTasksFileBrowserFunction::ExecuteFailedOnUIThread));
491 return;
492 }
493
494 BrowserThread::PostTask(
495 BrowserThread::UI, FROM_HERE,
496 NewRunnableMethod(function_,
497 &ExecuteTasksFileBrowserFunction::ExecuteFileActionsOnUIThread,
498 task_id_,
499 file_system_name,
500 file_system_root,
501 file_list));
502 }
503
DidFail(base::PlatformFileError error_code)504 virtual void DidFail(base::PlatformFileError error_code) OVERRIDE {
505 LOG(WARNING) << "Local file system cant be resolved";
506 BrowserThread::PostTask(
507 BrowserThread::UI, FROM_HERE,
508 NewRunnableMethod(function_,
509 &ExecuteTasksFileBrowserFunction::ExecuteFailedOnUIThread));
510 }
511
512 private:
513 // Checks legitimacy of file url and grants file RO access permissions from
514 // handler (target) extension and its renderer process.
SetupFileAccessPermissions(const GURL & origin_file_url,GURL * target_file_url,FilePath * file_path,bool * is_directory)515 bool SetupFileAccessPermissions(const GURL& origin_file_url,
516 GURL* target_file_url, FilePath* file_path, bool* is_directory) {
517
518 if (!extension_.get())
519 return false;
520
521 GURL file_origin_url;
522 FilePath virtual_path;
523 fileapi::FileSystemType type;
524 if (!CrackFileSystemURL(origin_file_url, &file_origin_url, &type,
525 &virtual_path)) {
526 return false;
527 }
528
529 if (type != fileapi::kFileSystemTypeExternal)
530 return false;
531
532 fileapi::FileSystemPathManager* path_manager =
533 profile_->GetFileSystemContext()->path_manager();
534 if (!path_manager->IsAccessAllowed(file_origin_url,
535 type,
536 virtual_path)) {
537 return false;
538 }
539
540 // Make sure this url really being used by the right caller extension.
541 if (source_url_.GetOrigin() != file_origin_url) {
542 DidFail(base::PLATFORM_FILE_ERROR_SECURITY);
543 return false;
544 }
545
546 FilePath root_path =
547 path_manager->ValidateFileSystemRootAndGetPathOnFileThread(
548 file_origin_url,
549 fileapi::kFileSystemTypeExternal,
550 virtual_path,
551 false); // create
552 FilePath final_file_path = root_path.Append(virtual_path);
553
554 // Check if this file system entry exists first.
555 base::PlatformFileInfo file_info;
556 FilePath platform_path;
557 fileapi::FileSystemOperationContext file_system_operation_context(
558 profile_->GetFileSystemContext(),
559 fileapi::LocalFileSystemFileUtil::GetInstance());
560 if (base::PLATFORM_FILE_OK !=
561 fileapi::FileSystemFileUtil::GetInstance()->GetFileInfo(
562 &file_system_operation_context, final_file_path, &file_info,
563 &platform_path)) {
564 return false;
565 }
566
567 fileapi::ExternalFileSystemMountPointProvider* external_provider =
568 path_manager->external_provider();
569 if (!external_provider)
570 return false;
571
572 // TODO(zelidrag): Let's just prevent all symlinks for now. We don't want a
573 // USB drive content to point to something in the rest of the file system.
574 // Ideally, we should permit symlinks within the boundary of the same
575 // virtual mount point.
576 if (file_info.is_symbolic_link)
577 return false;
578
579 // Get task details.
580 std::string target_extension_id;
581 std::string action_id;
582 if (!CrackTaskIdentifier(task_id_, &target_extension_id,
583 &action_id)) {
584 return false;
585 }
586
587 // TODO(zelidrag): Add explicit R/W + R/O permissions for non-component
588 // extensions.
589
590 // Get target extension's process.
591 RenderProcessHost* target_host =
592 profile_->GetExtensionProcessManager()->GetExtensionProcess(
593 target_extension_id);
594 if (!target_host)
595 return false;
596
597 // Grant R/O access permission to non-component extension and R/W to
598 // component extensions.
599 ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile(
600 target_host->id(), final_file_path,
601 extension_->location() != Extension::COMPONENT ?
602 kReadOnlyFilePermissions : kReadWriteFilePermissions);
603
604 // Grant access to this particular file to target extension. This will
605 // ensure that the target extension can access only this FS entry and
606 // prevent from traversing FS hierarchy upward.
607 external_provider->GrantFileAccessToExtension(target_extension_id,
608 virtual_path);
609
610 // Output values.
611 GURL target_origin_url(Extension::GetBaseURLFromExtensionId(
612 target_extension_id));
613 GURL base_url = fileapi::GetFileSystemRootURI(target_origin_url,
614 fileapi::kFileSystemTypeExternal);
615 *target_file_url = GURL(base_url.spec() + virtual_path.value());
616 FilePath root(FILE_PATH_LITERAL("/"));
617 *file_path = root.Append(virtual_path);
618 *is_directory = file_info.is_directory;
619 return true;
620 }
621
622 ExecuteTasksFileBrowserFunction* function_;
623 Profile* profile_;
624 // Extension source URL.
625 GURL source_url_;
626 scoped_refptr<const Extension> extension_;
627 std::string task_id_;
628 std::vector<GURL> origin_file_urls_;
629 DISALLOW_COPY_AND_ASSIGN(ExecuteTasksFileSystemCallbackDispatcher);
630 };
631
RunImpl()632 bool ExecuteTasksFileBrowserFunction::RunImpl() {
633 // First param is task id that was to the extension with getFileTasks call.
634 std::string task_id;
635 if (!args_->GetString(0, &task_id) || !task_id.size())
636 return false;
637
638 // The second param is the list of files that need to be executed with this
639 // task.
640 ListValue* files_list = NULL;
641 if (!args_->GetList(1, &files_list))
642 return false;
643
644 if (!files_list->GetSize())
645 return true;
646
647 return InitiateFileTaskExecution(task_id, files_list);
648 }
649
InitiateFileTaskExecution(const std::string & task_id,ListValue * files_list)650 bool ExecuteTasksFileBrowserFunction::InitiateFileTaskExecution(
651 const std::string& task_id, ListValue* files_list) {
652 std::vector<GURL> file_urls;
653 for (size_t i = 0; i < files_list->GetSize(); i++) {
654 std::string origin_file_url;
655 if (!files_list->GetString(i, &origin_file_url)) {
656 error_ = kInvalidFileUrl;
657 return false;
658 }
659 file_urls.push_back(GURL(origin_file_url));
660 }
661 // Get local file system instance on file thread.
662 BrowserThread::PostTask(
663 BrowserThread::FILE, FROM_HERE,
664 NewRunnableMethod(this,
665 &ExecuteTasksFileBrowserFunction::RequestFileEntryOnFileThread,
666 source_url_,
667 task_id,
668 file_urls));
669 result_.reset(new FundamentalValue(true));
670 return true;
671 }
672
RequestFileEntryOnFileThread(const GURL & source_url,const std::string & task_id,const std::vector<GURL> & file_urls)673 void ExecuteTasksFileBrowserFunction::RequestFileEntryOnFileThread(
674 const GURL& source_url, const std::string& task_id,
675 const std::vector<GURL>& file_urls) {
676 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
677 fileapi::FileSystemOperation* operation =
678 new fileapi::FileSystemOperation(
679 new ExecuteTasksFileSystemCallbackDispatcher(
680 this,
681 profile(),
682 dispatcher()->render_view_host()->process()->id(),
683 source_url,
684 GetExtension(),
685 task_id,
686 file_urls),
687 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE),
688 profile()->GetFileSystemContext(),
689 NULL);
690 GURL origin_url = source_url.GetOrigin();
691 operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeExternal,
692 false); // create
693 }
694
ExecuteFailedOnUIThread()695 void ExecuteTasksFileBrowserFunction::ExecuteFailedOnUIThread() {
696 SendResponse(false);
697 }
698
699
ExecuteFileActionsOnUIThread(const std::string & task_id,const std::string & file_system_name,const GURL & file_system_root,const FileDefinitionList & file_list)700 void ExecuteTasksFileBrowserFunction::ExecuteFileActionsOnUIThread(
701 const std::string& task_id,
702 const std::string& file_system_name,
703 const GURL& file_system_root,
704 const FileDefinitionList& file_list) {
705 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
706 ExtensionService* service = profile_->GetExtensionService();
707 if (!service)
708 return;
709 // Get task details.
710 std::string handler_extension_id;
711 std::string action_id;
712 if (!CrackTaskIdentifier(task_id, &handler_extension_id,
713 &action_id)) {
714 LOG(WARNING) << "Invalid task " << task_id;
715 SendResponse(false);
716 return;
717 }
718
719 const Extension* extension = service->GetExtensionById(handler_extension_id,
720 false);
721 if (!extension) {
722 SendResponse(false);
723 return;
724 }
725
726 ExtensionEventRouter* event_router = profile_->GetExtensionEventRouter();
727 if (!event_router) {
728 SendResponse(false);
729 return;
730 }
731
732 scoped_ptr<ListValue> event_args(new ListValue());
733 event_args->Append(Value::CreateStringValue(action_id));
734 DictionaryValue* details = new DictionaryValue();
735 event_args->Append(details);
736 // Get file definitions. These will be replaced with Entry instances by
737 // chromeHidden.Event.dispatchJSON() method from even_binding.js.
738 ListValue* files_urls = new ListValue();
739 details->Set("entries", files_urls);
740 for (FileDefinitionList::const_iterator iter = file_list.begin();
741 iter != file_list.end();
742 ++iter) {
743 DictionaryValue* file_def = new DictionaryValue();
744 files_urls->Append(file_def);
745 file_def->SetString("fileSystemName", file_system_name);
746 file_def->SetString("fileSystemRoot", file_system_root.spec());
747 file_def->SetString("fileFullPath", iter->virtual_path.value());
748 file_def->SetBoolean("fileIsDirectory", iter->is_directory);
749 }
750 // Get tab id.
751 Browser* browser = GetCurrentBrowser();
752 if (browser) {
753 TabContents* contents = browser->GetSelectedTabContents();
754 if (contents)
755 details->SetInteger("tab_id", ExtensionTabUtil::GetTabId(contents));
756 }
757
758 UpdateFileHandlerUsageStats(profile_, task_id);
759
760 std::string json_args;
761 base::JSONWriter::Write(event_args.get(), false, &json_args);
762 event_router->DispatchEventToExtension(
763 handler_extension_id, std::string("fileBrowserHandler.onExecute"),
764 json_args, profile_,
765 GURL());
766 SendResponse(true);
767 }
768
FileDialogFunction()769 FileDialogFunction::FileDialogFunction() {
770 }
771
~FileDialogFunction()772 FileDialogFunction::~FileDialogFunction() {
773 }
774
775 // static
776 FileDialogFunction::Callback
777 FileDialogFunction::Callback::null_(NULL, NULL, NULL);
778
779 // static
780 FileDialogFunction::Callback::Map FileDialogFunction::Callback::map_;
781
782 // static
Add(int32 tab_id,SelectFileDialog::Listener * listener,HtmlDialogView * dialog,void * params)783 void FileDialogFunction::Callback::Add(int32 tab_id,
784 SelectFileDialog::Listener* listener,
785 HtmlDialogView* dialog,
786 void* params) {
787 if (map_.find(tab_id) == map_.end()) {
788 map_.insert(std::make_pair(tab_id, Callback(listener, dialog, params)));
789 } else {
790 DLOG_ASSERT("FileDialogFunction::AddCallback tab_id already present");
791 }
792 }
793
794 // static
Remove(int32 tab_id)795 void FileDialogFunction::Callback::Remove(int32 tab_id) {
796 map_.erase(tab_id);
797 }
798
799 // static
800 const FileDialogFunction::Callback&
Find(int32 tab_id)801 FileDialogFunction::Callback::Find(int32 tab_id) {
802 Callback::Map::const_iterator it = map_.find(tab_id);
803 return (it == map_.end()) ? null_ : it->second;
804 }
805
806
GetTabId() const807 int32 FileDialogFunction::GetTabId() const {
808 return dispatcher()->delegate()->associated_tab_contents()->
809 controller().session_id().id();
810 }
811
GetCallback() const812 const FileDialogFunction::Callback& FileDialogFunction::GetCallback() const {
813 if (!dispatcher() || !dispatcher()->delegate() ||
814 !dispatcher()->delegate()->associated_tab_contents()) {
815 return Callback::null();
816 }
817 return Callback::Find(GetTabId());
818 }
819
CloseDialog(HtmlDialogView * dialog)820 void FileDialogFunction::CloseDialog(HtmlDialogView* dialog) {
821 DCHECK(dialog);
822 TabContents* contents = dispatcher()->delegate()->associated_tab_contents();
823 if (contents)
824 dialog->CloseContents(contents);
825 }
826
827 // GetFileSystemRootPathOnFileThread can only be called from the file thread,
828 // so here we are. This function takes a vector of virtual paths, converts
829 // them to local paths and calls GetLocalPathsResponseOnUIThread with the
830 // result vector, on the UI thread.
GetLocalPathsOnFileThread(const UrlList & file_urls,const std::string & task_id)831 void FileDialogFunction::GetLocalPathsOnFileThread(const UrlList& file_urls,
832 const std::string& task_id) {
833 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
834 FilePathList selected_files;
835
836 // FilePath(virtual_path) doesn't work on win, so limit this to ChromeOS.
837 #if defined(OS_CHROMEOS)
838 GURL origin_url = source_url().GetOrigin();
839 fileapi::FileSystemPathManager* path_manager =
840 profile()->GetFileSystemContext()->path_manager();
841
842 size_t len = file_urls.size();
843 selected_files.reserve(len);
844 for (size_t i = 0; i < len; ++i) {
845 const GURL& file_url = file_urls[i];
846 GURL file_origin_url;
847 FilePath virtual_path;
848 fileapi::FileSystemType type;
849 if (!CrackFileSystemURL(file_url, &file_origin_url, &type,
850 &virtual_path)) {
851 continue;
852 }
853 if (type != fileapi::kFileSystemTypeExternal) {
854 NOTREACHED();
855 continue;
856 }
857 FilePath root = path_manager->ValidateFileSystemRootAndGetPathOnFileThread(
858 origin_url,
859 fileapi::kFileSystemTypeExternal,
860 FilePath(virtual_path),
861 false);
862 if (!root.empty()) {
863 selected_files.push_back(root.Append(virtual_path));
864 } else {
865 LOG(WARNING) << "GetLocalPathsOnFileThread failed "
866 << file_url.spec();
867 }
868 }
869 #endif
870
871 if (!selected_files.empty()) {
872 BrowserThread::PostTask(
873 BrowserThread::UI, FROM_HERE,
874 NewRunnableMethod(this,
875 &FileDialogFunction::GetLocalPathsResponseOnUIThread,
876 selected_files, task_id));
877 }
878 }
879
RunImpl()880 bool SelectFileFunction::RunImpl() {
881 if (args_->GetSize() != 2) {
882 return false;
883 }
884 std::string file_url;
885 args_->GetString(0, &file_url);
886 UrlList file_paths;
887 file_paths.push_back(GURL(file_url));
888
889 BrowserThread::PostTask(
890 BrowserThread::FILE, FROM_HERE,
891 NewRunnableMethod(this,
892 &SelectFileFunction::GetLocalPathsOnFileThread,
893 file_paths, std::string()));
894
895 return true;
896 }
897
GetLocalPathsResponseOnUIThread(const FilePathList & files,const std::string & task_id)898 void SelectFileFunction::GetLocalPathsResponseOnUIThread(
899 const FilePathList& files, const std::string& task_id) {
900 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
901 if (files.size() != 1) {
902 SendResponse(false);
903 return;
904 }
905 int index;
906 args_->GetInteger(1, &index);
907 const Callback& callback = GetCallback();
908 DCHECK(!callback.IsNull());
909 if (!callback.IsNull()) {
910 // Must do this before callback, as the callback may delete listeners
911 // waiting for window close.
912 CloseDialog(callback.dialog());
913 callback.listener()->FileSelected(files[0],
914 index,
915 callback.params());
916 }
917 SendResponse(true);
918 }
919
920
ViewFilesFunction()921 ViewFilesFunction::ViewFilesFunction() {
922 }
923
~ViewFilesFunction()924 ViewFilesFunction::~ViewFilesFunction() {
925 }
926
RunImpl()927 bool ViewFilesFunction::RunImpl() {
928 if (args_->GetSize() < 1) {
929 return false;
930 }
931
932 ListValue* path_list = NULL;
933 args_->GetList(0, &path_list);
934 DCHECK(path_list);
935
936 std::string internal_task_id;
937 args_->GetString(1, &internal_task_id);
938
939 std::string virtual_path;
940 size_t len = path_list->GetSize();
941 UrlList file_urls;
942 file_urls.reserve(len);
943 for (size_t i = 0; i < len; ++i) {
944 path_list->GetString(i, &virtual_path);
945 file_urls.push_back(GURL(virtual_path));
946 }
947
948 BrowserThread::PostTask(
949 BrowserThread::FILE, FROM_HERE,
950 NewRunnableMethod(this,
951 &ViewFilesFunction::GetLocalPathsOnFileThread,
952 file_urls, internal_task_id));
953
954 return true;
955 }
956
GetLocalPathsResponseOnUIThread(const FilePathList & files,const std::string & internal_task_id)957 void ViewFilesFunction::GetLocalPathsResponseOnUIThread(
958 const FilePathList& files, const std::string& internal_task_id) {
959 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
960 for (FilePathList::const_iterator iter = files.begin();
961 iter != files.end();
962 ++iter) {
963 FileManagerUtil::ViewItem(*iter, internal_task_id == kEnqueueTaskId);
964 }
965 UpdateFileHandlerUsageStats(profile_, internal_task_id);
966 SendResponse(true);
967 }
968
SelectFilesFunction()969 SelectFilesFunction::SelectFilesFunction() {
970 }
971
~SelectFilesFunction()972 SelectFilesFunction::~SelectFilesFunction() {
973 }
974
RunImpl()975 bool SelectFilesFunction::RunImpl() {
976 if (args_->GetSize() != 1) {
977 return false;
978 }
979
980 ListValue* path_list = NULL;
981 args_->GetList(0, &path_list);
982 DCHECK(path_list);
983
984 std::string virtual_path;
985 size_t len = path_list->GetSize();
986 UrlList file_urls;
987 file_urls.reserve(len);
988 for (size_t i = 0; i < len; ++i) {
989 path_list->GetString(i, &virtual_path);
990 file_urls.push_back(GURL(virtual_path));
991 }
992
993 BrowserThread::PostTask(
994 BrowserThread::FILE, FROM_HERE,
995 NewRunnableMethod(this,
996 &SelectFilesFunction::GetLocalPathsOnFileThread,
997 file_urls, std::string()));
998
999 return true;
1000 }
1001
GetLocalPathsResponseOnUIThread(const FilePathList & files,const std::string & internal_task_id)1002 void SelectFilesFunction::GetLocalPathsResponseOnUIThread(
1003 const FilePathList& files, const std::string& internal_task_id) {
1004 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1005
1006 const Callback& callback = GetCallback();
1007 DCHECK(!callback.IsNull());
1008 if (!callback.IsNull()) {
1009 // Must do this before callback, as the callback may delete listeners
1010 // waiting for window close.
1011 CloseDialog(callback.dialog());
1012 callback.listener()->MultiFilesSelected(files, callback.params());
1013 }
1014 SendResponse(true);
1015 }
1016
RunImpl()1017 bool CancelFileDialogFunction::RunImpl() {
1018 const Callback& callback = GetCallback();
1019 DCHECK(!callback.IsNull());
1020 if (!callback.IsNull()) {
1021 // Must do this before callback, as the callback may delete listeners
1022 // waiting for window close.
1023 CloseDialog(callback.dialog());
1024 callback.listener()->FileSelectionCanceled(callback.params());
1025 }
1026 SendResponse(true);
1027 return true;
1028 }
1029
RunImpl()1030 bool FileDialogStringsFunction::RunImpl() {
1031 result_.reset(new DictionaryValue());
1032 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get());
1033
1034 #define SET_STRING(ns, id) \
1035 dict->SetString(#id, l10n_util::GetStringUTF16(ns##_##id))
1036
1037 SET_STRING(IDS, LOCALE_FMT_DATE_SHORT);
1038 SET_STRING(IDS, LOCALE_MONTHS_SHORT);
1039 SET_STRING(IDS, LOCALE_DAYS_SHORT);
1040
1041 SET_STRING(IDS_FILE_BROWSER, BODY_FONT_FAMILY);
1042 SET_STRING(IDS_FILE_BROWSER, BODY_FONT_SIZE);
1043
1044 SET_STRING(IDS_FILE_BROWSER, ROOT_DIRECTORY_LABEL);
1045 SET_STRING(IDS_FILE_BROWSER, DOWNLOADS_DIRECTORY_LABEL);
1046 SET_STRING(IDS_FILE_BROWSER, MEDIA_DIRECTORY_LABEL);
1047 SET_STRING(IDS_FILE_BROWSER, NAME_COLUMN_LABEL);
1048 SET_STRING(IDS_FILE_BROWSER, SIZE_COLUMN_LABEL);
1049 SET_STRING(IDS_FILE_BROWSER, DATE_COLUMN_LABEL);
1050 SET_STRING(IDS_FILE_BROWSER, PREVIEW_COLUMN_LABEL);
1051
1052 SET_STRING(IDS_FILE_BROWSER, ERROR_CREATING_FOLDER);
1053 SET_STRING(IDS_FILE_BROWSER, ERROR_INVALID_FOLDER_CHARACTER);
1054 SET_STRING(IDS_FILE_BROWSER, ERROR_INVALID_FILE_CHARACTER);
1055 SET_STRING(IDS_FILE_BROWSER, NEW_FOLDER_PROMPT);
1056 SET_STRING(IDS_FILE_BROWSER, NEW_FOLDER_BUTTON_LABEL);
1057 SET_STRING(IDS_FILE_BROWSER, FILENAME_LABEL);
1058
1059 SET_STRING(IDS_FILE_BROWSER, EJECT_BUTTON);
1060 SET_STRING(IDS_FILE_BROWSER, IMAGE_DIMENSIONS);
1061 SET_STRING(IDS_FILE_BROWSER, VOLUME_LABEL);
1062 SET_STRING(IDS_FILE_BROWSER, READ_ONLY);
1063
1064 SET_STRING(IDS_FILE_BROWSER, ERROR_RENAMING);
1065 SET_STRING(IDS_FILE_BROWSER, RENAME_PROMPT);
1066 SET_STRING(IDS_FILE_BROWSER, RENAME_BUTTON_LABEL);
1067
1068 SET_STRING(IDS_FILE_BROWSER, ERROR_DELETING);
1069 SET_STRING(IDS_FILE_BROWSER, DELETE_BUTTON_LABEL);
1070
1071 SET_STRING(IDS_FILE_BROWSER, ERROR_MOVING);
1072 SET_STRING(IDS_FILE_BROWSER, MOVE_BUTTON_LABEL);
1073
1074 SET_STRING(IDS_FILE_BROWSER, ERROR_PASTING);
1075 SET_STRING(IDS_FILE_BROWSER, PASTE_BUTTON_LABEL);
1076
1077 SET_STRING(IDS_FILE_BROWSER, COPY_BUTTON_LABEL);
1078 SET_STRING(IDS_FILE_BROWSER, CUT_BUTTON_LABEL);
1079
1080 SET_STRING(IDS_FILE_BROWSER, DEVICE_TYPE_FLASH);
1081 SET_STRING(IDS_FILE_BROWSER, DEVICE_TYPE_HDD);
1082 SET_STRING(IDS_FILE_BROWSER, DEVICE_TYPE_OPTICAL);
1083 SET_STRING(IDS_FILE_BROWSER, DEVICE_TYPE_UNDEFINED);
1084
1085 SET_STRING(IDS_FILE_BROWSER, CANCEL_LABEL);
1086 SET_STRING(IDS_FILE_BROWSER, OPEN_LABEL);
1087 SET_STRING(IDS_FILE_BROWSER, SAVE_LABEL);
1088
1089 SET_STRING(IDS_FILE_BROWSER, SELECT_FOLDER_TITLE);
1090 SET_STRING(IDS_FILE_BROWSER, SELECT_OPEN_FILE_TITLE);
1091 SET_STRING(IDS_FILE_BROWSER, SELECT_OPEN_MULTI_FILE_TITLE);
1092 SET_STRING(IDS_FILE_BROWSER, SELECT_SAVEAS_FILE_TITLE);
1093
1094 SET_STRING(IDS_FILE_BROWSER, COMPUTING_SELECTION);
1095 SET_STRING(IDS_FILE_BROWSER, NOTHING_SELECTED);
1096 SET_STRING(IDS_FILE_BROWSER, ONE_FILE_SELECTED);
1097 SET_STRING(IDS_FILE_BROWSER, MANY_FILES_SELECTED);
1098
1099 // FILEBROWSER, without the underscore, is from the old school codebase.
1100 // TODO(rginda): Move these into IDS_FILE_BROWSER post M12.
1101 SET_STRING(IDS_FILEBROWSER, CONFIRM_DELETE);
1102
1103 SET_STRING(IDS_FILEBROWSER, ENQUEUE);
1104 #undef SET_STRING
1105
1106 // TODO(serya): Create a new string in .grd file for this one in M13.
1107 dict->SetString("PREVIEW_IMAGE",
1108 l10n_util::GetStringUTF16(IDS_CERT_MANAGER_VIEW_CERT_BUTTON));
1109 dict->SetString("PLAY_MEDIA",
1110 l10n_util::GetStringUTF16(IDS_CONTENT_CONTEXT_PLAY));
1111
1112 return true;
1113 }
1114