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