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" |
| 13 #include "base/string_util.h" |
9 #include "base/task.h" | 14 #include "base/task.h" |
10 #include "base/values.h" | 15 #include "base/values.h" |
11 #include "chrome/browser/profiles/profile.h" | 16 #include "chrome/browser/profiles/profile.h" |
| 17 #include "chrome/browser/extensions/extension_event_router.h" |
| 18 #include "chrome/browser/extensions/extension_function_dispatcher.h" |
| 19 #include "chrome/browser/extensions/extension_process_manager.h" |
| 20 #include "chrome/browser/extensions/extension_service.h" |
| 21 #include "chrome/browser/ui/webui/extension_icon_source.h" |
| 22 #include "chrome/common/chrome_switches.h" |
12 #include "chrome/common/extensions/extension.h" | 23 #include "chrome/common/extensions/extension.h" |
| 24 #include "chrome/common/extensions/file_browser_handler.h" |
13 #include "content/browser/browser_thread.h" | 25 #include "content/browser/browser_thread.h" |
| 26 #include "content/browser/child_process_security_policy.h" |
| 27 #include "content/browser/renderer_host/render_process_host.h" |
| 28 #include "content/browser/renderer_host/render_view_host.h" |
14 #include "content/browser/tab_contents/tab_contents.h" | 29 #include "content/browser/tab_contents/tab_contents.h" |
15 #include "googleurl/src/gurl.h" | 30 #include "googleurl/src/gurl.h" |
16 #include "grit/generated_resources.h" | 31 #include "grit/generated_resources.h" |
17 #include "webkit/fileapi/file_system_context.h" | 32 #include "webkit/fileapi/file_system_context.h" |
| 33 #include "webkit/fileapi/file_system_mount_point_provider.h" |
18 #include "webkit/fileapi/file_system_operation.h" | 34 #include "webkit/fileapi/file_system_operation.h" |
| 35 #include "webkit/fileapi/file_system_operation_context.h" |
19 #include "webkit/fileapi/file_system_path_manager.h" | 36 #include "webkit/fileapi/file_system_path_manager.h" |
20 #include "webkit/fileapi/file_system_types.h" | 37 #include "webkit/fileapi/file_system_types.h" |
| 38 #include "webkit/fileapi/file_system_util.h" |
| 39 #include "webkit/fileapi/file_system_file_util.h" |
| 40 #include "webkit/fileapi/local_file_system_file_util.h" |
21 #include "ui/base/l10n/l10n_util.h" | 41 #include "ui/base/l10n/l10n_util.h" |
22 | 42 |
| 43 // Error messages. |
| 44 const char kFileError[] = "File error %d"; |
| 45 const char kInvalidFileUrl[] = "Invalid file URL"; |
| 46 |
| 47 const int kReadOnlyFilePermissions = base::PLATFORM_FILE_OPEN | |
| 48 base::PLATFORM_FILE_READ | |
| 49 base::PLATFORM_FILE_EXCLUSIVE_READ | |
| 50 base::PLATFORM_FILE_ASYNC; |
| 51 |
| 52 const int kReadWriteFilePermissions = base::PLATFORM_FILE_OPEN | |
| 53 base::PLATFORM_FILE_CREATE | |
| 54 base::PLATFORM_FILE_OPEN_ALWAYS | |
| 55 base::PLATFORM_FILE_CREATE_ALWAYS | |
| 56 base::PLATFORM_FILE_READ | |
| 57 base::PLATFORM_FILE_WRITE | |
| 58 base::PLATFORM_FILE_EXCLUSIVE_READ | |
| 59 base::PLATFORM_FILE_EXCLUSIVE_WRITE | |
| 60 base::PLATFORM_FILE_ASYNC | |
| 61 base::PLATFORM_FILE_TRUNCATE | |
| 62 base::PLATFORM_FILE_WRITE_ATTRIBUTES; |
| 63 |
| 64 typedef std::vector< |
| 65 std::pair<std::string, const FileBrowserHandler* > > |
| 66 NamedHandlerList; |
| 67 |
| 68 typedef std::vector<const FileBrowserHandler*> ActionList; |
| 69 |
| 70 bool GetFileBrowserHandlers(Profile* profile, |
| 71 const GURL& selected_file_url, |
| 72 ActionList* results) { |
| 73 ExtensionService* service = profile->GetExtensionService(); |
| 74 if (!service) |
| 75 return false; // In unit-tests, we may not have an ExtensionService. |
| 76 |
| 77 for (ExtensionList::const_iterator iter = service->extensions()->begin(); |
| 78 iter != service->extensions()->end(); |
| 79 ++iter) { |
| 80 const Extension* extension = iter->get(); |
| 81 if (!extension->file_browser_handlers()) |
| 82 continue; |
| 83 |
| 84 for (Extension::FileBrowserHandlerList::const_iterator action_iter = |
| 85 extension->file_browser_handlers()->begin(); |
| 86 action_iter != extension->file_browser_handlers()->end(); |
| 87 ++action_iter) { |
| 88 const FileBrowserHandler* action = action_iter->get(); |
| 89 if (!action->MatchesURL(selected_file_url)) |
| 90 continue; |
| 91 |
| 92 results->push_back(action_iter->get()); |
| 93 } |
| 94 } |
| 95 return true; |
| 96 } |
| 97 |
| 98 // Given the list of selected files, returns array of context menu tasks |
| 99 // that are shared |
| 100 bool FindCommonTasks(Profile* profile, |
| 101 ListValue* files_list, |
| 102 NamedHandlerList* named_action_list) { |
| 103 named_action_list->clear(); |
| 104 ActionList common_tasks; |
| 105 for (size_t i = 0; i < files_list->GetSize(); ++i) { |
| 106 std::string file_url; |
| 107 if (!files_list->GetString(i, &file_url)) |
| 108 return false; |
| 109 |
| 110 ActionList file_actions; |
| 111 if (!GetFileBrowserHandlers(profile, GURL(file_url), &file_actions)) |
| 112 return false; |
| 113 // If there is nothing to do for one file, the intersection of tasks for all |
| 114 // files will be empty at the end. |
| 115 if (!file_actions.size()) { |
| 116 common_tasks.clear(); |
| 117 return true; |
| 118 } |
| 119 // For the very first file, just copy elements. |
| 120 if (i == 0) { |
| 121 common_tasks.insert(common_tasks.begin(), |
| 122 file_actions.begin(), |
| 123 file_actions.end()); |
| 124 std::sort(common_tasks.begin(), common_tasks.end()); |
| 125 } else if (common_tasks.size()) { |
| 126 // For all additional files, find intersection between the accumulated |
| 127 // and file specific set. |
| 128 std::sort(file_actions.begin(), file_actions.end()); |
| 129 ActionList intersection(common_tasks.size()); |
| 130 ActionList::iterator intersection_end = |
| 131 std::set_intersection(common_tasks.begin(), |
| 132 common_tasks.end(), |
| 133 file_actions.begin(), |
| 134 file_actions.end(), |
| 135 intersection.begin()); |
| 136 common_tasks.clear(); |
| 137 common_tasks.insert(common_tasks.begin(), |
| 138 intersection.begin(), |
| 139 intersection_end); |
| 140 std::sort(common_tasks.begin(), common_tasks.end()); |
| 141 } |
| 142 } |
| 143 |
| 144 // At the end, sort the results by task title. |
| 145 // TODO(zelidrag): Wire this with ICU to make this sort I18N happy. |
| 146 for (ActionList::const_iterator iter = common_tasks.begin(); |
| 147 iter != common_tasks.end(); ++iter) { |
| 148 named_action_list->push_back( |
| 149 std::pair<std::string, const FileBrowserHandler* >( |
| 150 (*iter)->title(), *iter)); |
| 151 } |
| 152 std::sort(named_action_list->begin(), named_action_list->end()); |
| 153 return true; |
| 154 } |
| 155 |
| 156 // Breaks down task_id that is used between getFileTasks() and executeTask() on |
| 157 // its building blocks. task_id field the following structure: |
| 158 // <task-type>:<extension-id>/<task-action-id> |
| 159 // Currently, the only supported task-type is of 'context'. |
| 160 bool CrackTaskIdentifier(const std::string& task_id, |
| 161 std::string* target_extension_id, |
| 162 std::string* action_id) { |
| 163 std::vector<std::string> result; |
| 164 int count = Tokenize(task_id, std::string("|"), &result); |
| 165 if (count != 2) |
| 166 return false; |
| 167 *target_extension_id = result[0]; |
| 168 *action_id = result[1]; |
| 169 return true; |
| 170 } |
| 171 |
| 172 std::string MakeTaskID(const char* extension_id, |
| 173 const char* action_id) { |
| 174 return base::StringPrintf("%s|%s", extension_id, action_id); |
| 175 } |
23 | 176 |
24 class LocalFileSystemCallbackDispatcher | 177 class LocalFileSystemCallbackDispatcher |
25 : public fileapi::FileSystemCallbackDispatcher { | 178 : public fileapi::FileSystemCallbackDispatcher { |
26 public: | 179 public: |
27 explicit LocalFileSystemCallbackDispatcher( | 180 explicit LocalFileSystemCallbackDispatcher( |
28 RequestLocalFileSystemFunction* function) : function_(function) { | 181 RequestLocalFileSystemFunction* function, |
| 182 Profile* profile, |
| 183 int child_id, |
| 184 scoped_refptr<const Extension> extension) |
| 185 : function_(function), |
| 186 profile_(profile), |
| 187 child_id_(child_id), |
| 188 extension_(extension) { |
29 DCHECK(function_); | 189 DCHECK(function_); |
30 } | 190 } |
| 191 |
31 // fileapi::FileSystemCallbackDispatcher overrides. | 192 // fileapi::FileSystemCallbackDispatcher overrides. |
32 virtual void DidSucceed() OVERRIDE { | 193 virtual void DidSucceed() OVERRIDE { |
33 NOTREACHED(); | 194 NOTREACHED(); |
34 } | 195 } |
| 196 |
35 virtual void DidReadMetadata(const base::PlatformFileInfo& info, | 197 virtual void DidReadMetadata(const base::PlatformFileInfo& info, |
36 const FilePath& unused) OVERRIDE { | 198 const FilePath& unused) OVERRIDE { |
37 NOTREACHED(); | 199 NOTREACHED(); |
38 } | 200 } |
| 201 |
39 virtual void DidReadDirectory( | 202 virtual void DidReadDirectory( |
40 const std::vector<base::FileUtilProxy::Entry>& entries, | 203 const std::vector<base::FileUtilProxy::Entry>& entries, |
41 bool has_more) OVERRIDE { | 204 bool has_more) OVERRIDE { |
42 NOTREACHED(); | 205 NOTREACHED(); |
43 } | 206 } |
| 207 |
44 virtual void DidWrite(int64 bytes, bool complete) OVERRIDE { | 208 virtual void DidWrite(int64 bytes, bool complete) OVERRIDE { |
45 NOTREACHED(); | 209 NOTREACHED(); |
46 } | 210 } |
| 211 |
47 virtual void DidOpenFileSystem(const std::string& name, | 212 virtual void DidOpenFileSystem(const std::string& name, |
48 const GURL& root) OVERRIDE { | 213 const GURL& root_path) OVERRIDE { |
| 214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 215 // Set up file permission access. |
| 216 if (!SetupFileSystemAccessPermissions()) { |
| 217 DidFail(base::PLATFORM_FILE_ERROR_SECURITY); |
| 218 return; |
| 219 } |
| 220 |
49 BrowserThread::PostTask( | 221 BrowserThread::PostTask( |
50 BrowserThread::UI, FROM_HERE, | 222 BrowserThread::UI, FROM_HERE, |
51 NewRunnableMethod(function_, | 223 NewRunnableMethod(function_, |
52 &RequestLocalFileSystemFunction::RespondSuccessOnUIThread, | 224 &RequestLocalFileSystemFunction::RespondSuccessOnUIThread, |
53 name, | 225 name, |
54 root)); | 226 root_path)); |
55 } | 227 } |
| 228 |
56 virtual void DidFail(base::PlatformFileError error_code) OVERRIDE { | 229 virtual void DidFail(base::PlatformFileError error_code) OVERRIDE { |
57 BrowserThread::PostTask( | 230 BrowserThread::PostTask( |
58 BrowserThread::UI, FROM_HERE, | 231 BrowserThread::UI, FROM_HERE, |
59 NewRunnableMethod(function_, | 232 NewRunnableMethod(function_, |
60 &RequestLocalFileSystemFunction::RespondFailedOnUIThread, | 233 &RequestLocalFileSystemFunction::RespondFailedOnUIThread, |
61 error_code)); | 234 error_code)); |
62 } | 235 } |
| 236 |
63 private: | 237 private: |
| 238 |
| 239 // Grants file system access permissions to file browser component. |
| 240 bool SetupFileSystemAccessPermissions() { |
| 241 if (!extension_.get()) |
| 242 return false; |
| 243 |
| 244 // Make sure that only component extension can access the entire |
| 245 // local file system. |
| 246 if (extension_->location() != Extension::COMPONENT |
| 247 #ifndef NDEBUG |
| 248 && !CommandLine::ForCurrentProcess()->HasSwitch( |
| 249 switches::kExposePrivateExtensionApi) |
| 250 #endif |
| 251 ) { |
| 252 NOTREACHED() << "Private method access by non-component extension " |
| 253 << extension_->id(); |
| 254 return false; |
| 255 } |
| 256 |
| 257 fileapi::FileSystemPathManager* path_manager = |
| 258 profile_->GetFileSystemContext()->path_manager(); |
| 259 fileapi::ExternalFileSystemMountPointProvider* provider = |
| 260 path_manager->external_provider(); |
| 261 if (!provider) |
| 262 return false; |
| 263 |
| 264 // Grant full access to File API from this component extension. |
| 265 provider->GrantFullAccessToExtension(extension_->id()); |
| 266 |
| 267 // Grant R/W file permissions to the renderer hosting component |
| 268 // extension for all paths exposed by our local file system provider. |
| 269 std::vector<FilePath> root_dirs = provider->GetRootDirectories(); |
| 270 for (std::vector<FilePath>::iterator iter = root_dirs.begin(); |
| 271 iter != root_dirs.end(); |
| 272 ++iter) { |
| 273 ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile( |
| 274 child_id_, *iter, kReadWriteFilePermissions); |
| 275 } |
| 276 return true; |
| 277 } |
| 278 |
64 RequestLocalFileSystemFunction* function_; | 279 RequestLocalFileSystemFunction* function_; |
| 280 Profile* profile_; |
| 281 // Renderer process id. |
| 282 int child_id_; |
| 283 // Extension source URL. |
| 284 scoped_refptr<const Extension> extension_; |
65 DISALLOW_COPY_AND_ASSIGN(LocalFileSystemCallbackDispatcher); | 285 DISALLOW_COPY_AND_ASSIGN(LocalFileSystemCallbackDispatcher); |
66 }; | 286 }; |
67 | 287 |
68 RequestLocalFileSystemFunction::RequestLocalFileSystemFunction() { | 288 void RequestLocalFileSystemFunction::RequestOnFileThread( |
69 } | 289 const GURL& source_url) { |
70 | 290 fileapi::FileSystemOperation* operation = |
71 RequestLocalFileSystemFunction::~RequestLocalFileSystemFunction() { | 291 new fileapi::FileSystemOperation( |
| 292 new LocalFileSystemCallbackDispatcher( |
| 293 this, |
| 294 profile(), |
| 295 dispatcher()->render_view_host()->process()->id(), |
| 296 GetExtension()), |
| 297 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), |
| 298 profile()->GetFileSystemContext(), |
| 299 NULL); |
| 300 GURL origin_url = source_url.GetOrigin(); |
| 301 operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeExternal, |
| 302 false); // create |
72 } | 303 } |
73 | 304 |
74 bool RequestLocalFileSystemFunction::RunImpl() { | 305 bool RequestLocalFileSystemFunction::RunImpl() { |
75 fileapi::FileSystemOperation* operation = | 306 BrowserThread::PostTask( |
76 new fileapi::FileSystemOperation( | 307 BrowserThread::FILE, FROM_HERE, |
77 new LocalFileSystemCallbackDispatcher(this), | 308 NewRunnableMethod(this, |
78 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), | 309 &RequestLocalFileSystemFunction::RequestOnFileThread, |
79 profile()->GetFileSystemContext(), | 310 source_url_)); |
80 NULL); | |
81 GURL origin_url = source_url().GetOrigin(); | |
82 operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeExternal, | |
83 false); // create | |
84 // Will finish asynchronously. | 311 // Will finish asynchronously. |
85 return true; | 312 return true; |
86 } | 313 } |
87 | 314 |
88 void RequestLocalFileSystemFunction::RespondSuccessOnUIThread( | 315 void RequestLocalFileSystemFunction::RespondSuccessOnUIThread( |
89 const std::string& name, const GURL& root) { | 316 const std::string& name, const GURL& root_path) { |
90 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
91 result_.reset(new DictionaryValue()); | 318 result_.reset(new DictionaryValue()); |
92 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get()); | 319 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get()); |
93 dict->SetString("name", name); | 320 dict->SetString("name", name); |
94 dict->SetString("path", root.spec()); | 321 dict->SetString("path", root_path.spec()); |
95 dict->SetInteger("error", base::PLATFORM_FILE_OK); | 322 dict->SetInteger("error", base::PLATFORM_FILE_OK); |
96 SendResponse(true); | 323 SendResponse(true); |
97 } | 324 } |
98 | 325 |
99 void RequestLocalFileSystemFunction::RespondFailedOnUIThread( | 326 void RequestLocalFileSystemFunction::RespondFailedOnUIThread( |
100 base::PlatformFileError error_code) { | 327 base::PlatformFileError error_code) { |
101 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 328 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
102 result_.reset(new DictionaryValue()); | 329 error_ = base::StringPrintf(kFileError, static_cast<int>(error_code)); |
103 DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get()); | 330 SendResponse(false); |
104 dict->SetInteger("error", static_cast<int>(error_code)); | 331 } |
| 332 |
| 333 bool GetFileTasksFileBrowserFunction::RunImpl() { |
| 334 ListValue* files_list = NULL; |
| 335 if (!args_->GetList(0, &files_list)) |
| 336 return false; |
| 337 |
| 338 ListValue* result_list = new ListValue(); |
| 339 result_.reset(result_list); |
| 340 |
| 341 NamedHandlerList common_tasks; |
| 342 if (!FindCommonTasks(profile_, files_list, &common_tasks)) |
| 343 return false; |
| 344 |
| 345 ExtensionService* service = profile_->GetExtensionService(); |
| 346 for (NamedHandlerList::iterator iter = common_tasks.begin(); |
| 347 iter != common_tasks.end(); |
| 348 ++iter) { |
| 349 const std::string extension_id = iter->second->extension_id(); |
| 350 const Extension* extension = service->GetExtensionById(extension_id, false); |
| 351 CHECK(extension); |
| 352 DictionaryValue* task = new DictionaryValue(); |
| 353 task->SetString("taskId", MakeTaskID(extension_id.data(), |
| 354 iter->second->id().data())); |
| 355 task->SetString("title", iter->second->title()); |
| 356 // TODO(zelidrag): Figure out how to expose icon URL that task defined in |
| 357 // manifest instead of the default extension icon. |
| 358 GURL icon = |
| 359 ExtensionIconSource::GetIconURL(extension, |
| 360 Extension::EXTENSION_ICON_SMALLISH, |
| 361 ExtensionIconSet::MATCH_BIGGER, |
| 362 false); // grayscale |
| 363 task->SetString("iconUrl", icon.spec()); |
| 364 result_list->Append(task); |
| 365 } |
| 366 |
| 367 // TODO(zelidrag, serya): Add intent content tasks to result_list once we |
| 368 // implement that API. |
105 SendResponse(true); | 369 SendResponse(true); |
| 370 return true; |
| 371 } |
| 372 |
| 373 class ExecuteTasksFileSystemCallbackDispatcher |
| 374 : public fileapi::FileSystemCallbackDispatcher { |
| 375 public: |
| 376 explicit ExecuteTasksFileSystemCallbackDispatcher( |
| 377 ExecuteTasksFileBrowserFunction* function, |
| 378 Profile* profile, |
| 379 int child_id, |
| 380 const GURL& source_url, |
| 381 scoped_refptr<const Extension> extension, |
| 382 const std::string task_id, |
| 383 const std::vector<GURL>& file_urls) |
| 384 : function_(function), |
| 385 profile_(profile), |
| 386 source_url_(source_url), |
| 387 extension_(extension), |
| 388 task_id_(task_id), |
| 389 origin_file_urls_(file_urls) { |
| 390 DCHECK(function_); |
| 391 } |
| 392 |
| 393 // fileapi::FileSystemCallbackDispatcher overrides. |
| 394 virtual void DidSucceed() OVERRIDE { |
| 395 NOTREACHED(); |
| 396 } |
| 397 |
| 398 virtual void DidReadMetadata(const base::PlatformFileInfo& info, |
| 399 const FilePath& unused) OVERRIDE { |
| 400 NOTREACHED(); |
| 401 } |
| 402 |
| 403 virtual void DidReadDirectory( |
| 404 const std::vector<base::FileUtilProxy::Entry>& entries, |
| 405 bool has_more) OVERRIDE { |
| 406 NOTREACHED(); |
| 407 } |
| 408 |
| 409 virtual void DidWrite(int64 bytes, bool complete) OVERRIDE { |
| 410 NOTREACHED(); |
| 411 } |
| 412 |
| 413 virtual void DidOpenFileSystem(const std::string& file_system_name, |
| 414 const GURL& file_system_root) OVERRIDE { |
| 415 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 416 ExecuteTasksFileBrowserFunction::FileDefinitionList file_list; |
| 417 for (std::vector<GURL>::iterator iter = origin_file_urls_.begin(); |
| 418 iter != origin_file_urls_.end(); |
| 419 ++iter) { |
| 420 // Set up file permission access. |
| 421 ExecuteTasksFileBrowserFunction::FileDefinition file; |
| 422 if (!SetupFileAccessPermissions(*iter, &file.target_file_url, |
| 423 &file.virtual_path, &file.is_directory)) { |
| 424 continue; |
| 425 } |
| 426 file_list.push_back(file); |
| 427 } |
| 428 if (file_list.empty()) |
| 429 return; |
| 430 |
| 431 BrowserThread::PostTask( |
| 432 BrowserThread::UI, FROM_HERE, |
| 433 NewRunnableMethod(function_, |
| 434 &ExecuteTasksFileBrowserFunction::ExecuteFileActionsOnUIThread, |
| 435 task_id_, |
| 436 file_system_name, |
| 437 file_system_root, |
| 438 file_list)); |
| 439 } |
| 440 |
| 441 virtual void DidFail(base::PlatformFileError error_code) OVERRIDE { |
| 442 LOG(WARNING) << "Local file system cant be resolved"; |
| 443 } |
| 444 |
| 445 private: |
| 446 // Checks legitimacy of file url and grants file RO access permissions from |
| 447 // handler (target) extension and its renderer process. |
| 448 bool SetupFileAccessPermissions(const GURL& origin_file_url, |
| 449 GURL* target_file_url, FilePath* file_path, bool* is_directory) { |
| 450 |
| 451 if (!extension_.get()) |
| 452 return false; |
| 453 |
| 454 GURL file_origin_url; |
| 455 FilePath virtual_path; |
| 456 fileapi::FileSystemType type; |
| 457 |
| 458 if (!CrackFileSystemURL(origin_file_url, &file_origin_url, &type, |
| 459 &virtual_path)) { |
| 460 return false; |
| 461 } |
| 462 |
| 463 if (type != fileapi::kFileSystemTypeExternal) |
| 464 return false; |
| 465 |
| 466 fileapi::FileSystemPathManager* path_manager = |
| 467 profile_->GetFileSystemContext()->path_manager(); |
| 468 if (!path_manager->IsAccessAllowed(file_origin_url, |
| 469 type, |
| 470 virtual_path)) { |
| 471 return false; |
| 472 } |
| 473 |
| 474 // Make sure this url really being used by the right caller extension. |
| 475 if (source_url_.GetOrigin() != file_origin_url) { |
| 476 DidFail(base::PLATFORM_FILE_ERROR_SECURITY); |
| 477 return false; |
| 478 } |
| 479 |
| 480 FilePath root_path = path_manager->GetFileSystemRootPathOnFileThread( |
| 481 file_origin_url, |
| 482 fileapi::kFileSystemTypeExternal, |
| 483 virtual_path, |
| 484 false); // create |
| 485 FilePath final_file_path = root_path.Append(virtual_path); |
| 486 |
| 487 // Check if this file system entry exists first. |
| 488 base::PlatformFileInfo file_info; |
| 489 FilePath platform_path; |
| 490 fileapi::FileSystemOperationContext file_system_operation_context( |
| 491 profile_->GetFileSystemContext(), |
| 492 fileapi::LocalFileSystemFileUtil::GetInstance()); |
| 493 if (base::PLATFORM_FILE_OK != |
| 494 fileapi::FileSystemFileUtil::GetInstance()->GetFileInfo( |
| 495 &file_system_operation_context, final_file_path, &file_info, |
| 496 &platform_path)) { |
| 497 return false; |
| 498 } |
| 499 |
| 500 // TODO(zelidrag): Let's just prevent all symlinks for now. We don't want a |
| 501 // USB drive content to point to something in the rest of the file system. |
| 502 // Ideally, we should permit symlinks within the boundary of the same |
| 503 // virtual mount point. |
| 504 if (file_info.is_symbolic_link) |
| 505 return false; |
| 506 |
| 507 // TODO(zelidrag): Add explicit R/W + R/O permissions for non-component |
| 508 // extensions. |
| 509 |
| 510 // Get task details. |
| 511 std::string target_extension_id; |
| 512 std::string action_id; |
| 513 if (!CrackTaskIdentifier(task_id_, &target_extension_id, |
| 514 &action_id)) { |
| 515 return false; |
| 516 } |
| 517 |
| 518 // Get target extension's process. |
| 519 RenderProcessHost* target_host = |
| 520 profile_->GetExtensionProcessManager()->GetExtensionProcess( |
| 521 target_extension_id); |
| 522 if (!target_host) |
| 523 return false; |
| 524 |
| 525 // Grant R/O access permission to non-component extension and R/W to |
| 526 // component extensions. |
| 527 ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile( |
| 528 target_host->id(), final_file_path, |
| 529 extension_->location() != Extension::COMPONENT ? |
| 530 kReadOnlyFilePermissions : kReadWriteFilePermissions); |
| 531 |
| 532 // Grant access to this particular file to target extension. This will |
| 533 // ensure that the target extension can access only this FS entry and |
| 534 // prevent from traversing FS hierarchy upward. |
| 535 fileapi::ExternalFileSystemMountPointProvider* external_provider = |
| 536 path_manager->external_provider(); |
| 537 if (!external_provider) |
| 538 return false; |
| 539 external_provider->GrantFileAccessToExtension(target_extension_id, |
| 540 virtual_path); |
| 541 |
| 542 // Output values. |
| 543 GURL target_origin_url(Extension::GetBaseURLFromExtensionId( |
| 544 target_extension_id)); |
| 545 GURL base_url = fileapi::GetFileSystemRootURI(target_origin_url, |
| 546 fileapi::kFileSystemTypeExternal); |
| 547 *target_file_url = GURL(base_url.spec() + virtual_path.value()); |
| 548 *file_path = virtual_path; |
| 549 *is_directory = file_info.is_directory; |
| 550 return true; |
| 551 } |
| 552 |
| 553 ExecuteTasksFileBrowserFunction* function_; |
| 554 Profile* profile_; |
| 555 // Extension source URL. |
| 556 GURL source_url_; |
| 557 scoped_refptr<const Extension> extension_; |
| 558 std::string task_id_; |
| 559 std::vector<GURL> origin_file_urls_; |
| 560 DISALLOW_COPY_AND_ASSIGN(ExecuteTasksFileSystemCallbackDispatcher); |
| 561 }; |
| 562 |
| 563 bool ExecuteTasksFileBrowserFunction::RunImpl() { |
| 564 // First param is task id that was to the extension with getFileTasks call. |
| 565 std::string task_id; |
| 566 if (!args_->GetString(0, &task_id) || !task_id.size()) |
| 567 return false; |
| 568 |
| 569 // The second param is the list of files that need to be executed with this |
| 570 // task. |
| 571 ListValue* files_list = NULL; |
| 572 if (!args_->GetList(1, &files_list)) |
| 573 return false; |
| 574 |
| 575 if (!files_list->GetSize()) |
| 576 return true; |
| 577 |
| 578 InitiateFileTaskExecution(task_id, files_list); |
| 579 SendResponse(true); |
| 580 return true; |
| 581 } |
| 582 |
| 583 bool ExecuteTasksFileBrowserFunction::InitiateFileTaskExecution( |
| 584 const std::string& task_id, ListValue* files_list) { |
| 585 std::vector<GURL> file_urls; |
| 586 for (size_t i = 0; i < files_list->GetSize(); i++) { |
| 587 std::string origin_file_url; |
| 588 if (!files_list->GetString(i, &origin_file_url)) { |
| 589 error_ = kInvalidFileUrl; |
| 590 SendResponse(false); |
| 591 return false; |
| 592 } |
| 593 file_urls.push_back(GURL(origin_file_url)); |
| 594 } |
| 595 // Get local file system instance on file thread. |
| 596 BrowserThread::PostTask( |
| 597 BrowserThread::FILE, FROM_HERE, |
| 598 NewRunnableMethod(this, |
| 599 &ExecuteTasksFileBrowserFunction::RequestFileEntryOnFileThread, |
| 600 source_url_, |
| 601 task_id, |
| 602 file_urls)); |
| 603 result_.reset(new FundamentalValue(true)); |
| 604 return true; |
| 605 } |
| 606 |
| 607 void ExecuteTasksFileBrowserFunction::RequestFileEntryOnFileThread( |
| 608 const GURL& source_url, const std::string& task_id, |
| 609 const std::vector<GURL>& file_urls) { |
| 610 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); |
| 611 fileapi::FileSystemOperation* operation = |
| 612 new fileapi::FileSystemOperation( |
| 613 new ExecuteTasksFileSystemCallbackDispatcher( |
| 614 this, |
| 615 profile(), |
| 616 dispatcher()->render_view_host()->process()->id(), |
| 617 source_url, |
| 618 GetExtension(), |
| 619 task_id, |
| 620 file_urls), |
| 621 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), |
| 622 profile()->GetFileSystemContext(), |
| 623 NULL); |
| 624 GURL origin_url = source_url.GetOrigin(); |
| 625 operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeExternal, |
| 626 false); // create |
| 627 } |
| 628 |
| 629 void ExecuteTasksFileBrowserFunction::ExecuteFileActionsOnUIThread( |
| 630 const std::string& task_id, |
| 631 const std::string& file_system_name, |
| 632 const GURL& file_system_root, |
| 633 const FileDefinitionList& file_list) { |
| 634 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 635 ExtensionService* service = profile_->GetExtensionService(); |
| 636 if (!service) |
| 637 return; |
| 638 // Get task details. |
| 639 std::string handler_extension_id; |
| 640 std::string action_id; |
| 641 if (!CrackTaskIdentifier(task_id, &handler_extension_id, |
| 642 &action_id)) { |
| 643 LOG(WARNING) << "Invalid task " << task_id; |
| 644 return; |
| 645 } |
| 646 |
| 647 const Extension* extension = service->GetExtensionById(handler_extension_id, |
| 648 false); |
| 649 if (!extension) |
| 650 return; |
| 651 |
| 652 ExtensionEventRouter* event_router = profile_->GetExtensionEventRouter(); |
| 653 if (!event_router) |
| 654 return; |
| 655 |
| 656 scoped_ptr<ListValue> event_args(new ListValue()); |
| 657 ListValue* files_urls = new ListValue(); |
| 658 event_args->Append(Value::CreateStringValue(action_id)); |
| 659 event_args->Append(files_urls); |
| 660 for (FileDefinitionList::const_iterator iter = file_list.begin(); |
| 661 iter != file_list.end(); |
| 662 ++iter) { |
| 663 DictionaryValue* file_def = new DictionaryValue(); |
| 664 files_urls->Append(file_def); |
| 665 file_def->SetString("fileSystemName", file_system_name); |
| 666 file_def->SetString("fileSystemRoot", file_system_root.spec()); |
| 667 file_def->SetString("fileFullPath", iter->virtual_path.value()); |
| 668 file_def->SetBoolean("fileIsDirectory", iter->is_directory); |
| 669 } |
| 670 std::string json_args; |
| 671 base::JSONWriter::Write(event_args.get(), false, &json_args); |
| 672 std::string event_name = "contextMenus"; |
| 673 event_router->DispatchEventToExtension( |
| 674 handler_extension_id, std::string("fileBrowserHandler.onExecute"), |
| 675 json_args, profile_, |
| 676 GURL()); |
106 } | 677 } |
107 | 678 |
108 FileDialogFunction::FileDialogFunction() { | 679 FileDialogFunction::FileDialogFunction() { |
109 } | 680 } |
110 | 681 |
111 FileDialogFunction::~FileDialogFunction() { | 682 FileDialogFunction::~FileDialogFunction() { |
112 } | 683 } |
113 | 684 |
114 // static | 685 // static |
115 FileDialogFunction::Callback | 686 FileDialogFunction::Callback |
(...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
309 SET_STRING(IDS_FILE_BROWSER, COMPUTING_SELECTION); | 880 SET_STRING(IDS_FILE_BROWSER, COMPUTING_SELECTION); |
310 SET_STRING(IDS_FILE_BROWSER, NOTHING_SELECTED); | 881 SET_STRING(IDS_FILE_BROWSER, NOTHING_SELECTED); |
311 SET_STRING(IDS_FILE_BROWSER, ONE_FILE_SELECTED); | 882 SET_STRING(IDS_FILE_BROWSER, ONE_FILE_SELECTED); |
312 SET_STRING(IDS_FILE_BROWSER, MANY_FILES_SELECTED); | 883 SET_STRING(IDS_FILE_BROWSER, MANY_FILES_SELECTED); |
313 | 884 |
314 #undef SET_STRING | 885 #undef SET_STRING |
315 | 886 |
316 SendResponse(true); | 887 SendResponse(true); |
317 return true; | 888 return true; |
318 } | 889 } |
OLD | NEW |