diff options
author | zelidrag@chromium.org <zelidrag@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-16 08:39:20 +0000 |
---|---|---|
committer | zelidrag@chromium.org <zelidrag@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-16 08:39:20 +0000 |
commit | 76066e5180a794b39d8f9550937b37b05f7793e8 (patch) | |
tree | 7b8a709544d0293808452ac622f01a0de195f013 /chrome/browser | |
parent | cb23cdd5107b3f809f1a5a04a99098842364a15b (diff) | |
download | chromium_src-76066e5180a794b39d8f9550937b37b05f7793e8.zip chromium_src-76066e5180a794b39d8f9550937b37b05f7793e8.tar.gz chromium_src-76066e5180a794b39d8f9550937b37b05f7793e8.tar.bz2 |
New fileBrowserPrivate and fileHandler APIs added + plus magic needed to safely hand over access to local file system elements from content extension to 3rd party extension.
As agreed with aa@ and asargent@, this new API defines following event:
chrome.fileHandler.onExecute.addListener(function(id, file_entries) {
}
This event is invoked when user selects files in ChromeOS file browser.
The extension needs to register itself as file content hanlder with following manifest changes:
...
"file_browser_actions": [
{
"id" : "ActionIdentifier",
"default_title" : "Action title",
"default_icon" : "icon.png",
"file_filters" : [ "filesystem:*.jpeg", ... ]
}
...
],
...
BUG=chromium-os:11996
TEST=ExtensionApiTest.FileBrowserTest, ExtensionManifestTest.FileBrowserActions
Review URL: http://codereview.chromium.org/6749021
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@81865 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
4 files changed, 649 insertions, 40 deletions
diff --git a/chrome/browser/extensions/extension_file_browser_private_api.cc b/chrome/browser/extensions/extension_file_browser_private_api.cc index 448b8e2..ecf88e8 100644 --- a/chrome/browser/extensions/extension_file_browser_private_api.cc +++ b/chrome/browser/extensions/extension_file_browser_private_api.cc @@ -4,55 +4,228 @@ #include "chrome/browser/extensions/extension_file_browser_private_api.h" +#include "base/base64.h" +#include "base/command_line.h" #include "base/json/json_writer.h" #include "base/logging.h" +#include "base/memory/singleton.h" +#include "base/stringprintf.h" +#include "base/string_util.h" #include "base/task.h" #include "base/values.h" #include "chrome/browser/profiles/profile.h" +#include "chrome/browser/extensions/extension_event_router.h" +#include "chrome/browser/extensions/extension_function_dispatcher.h" +#include "chrome/browser/extensions/extension_process_manager.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/ui/webui/extension_icon_source.h" +#include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension.h" +#include "chrome/common/extensions/file_browser_handler.h" #include "content/browser/browser_thread.h" +#include "content/browser/child_process_security_policy.h" +#include "content/browser/renderer_host/render_process_host.h" +#include "content/browser/renderer_host/render_view_host.h" #include "content/browser/tab_contents/tab_contents.h" #include "googleurl/src/gurl.h" #include "grit/generated_resources.h" #include "webkit/fileapi/file_system_context.h" +#include "webkit/fileapi/file_system_mount_point_provider.h" #include "webkit/fileapi/file_system_operation.h" +#include "webkit/fileapi/file_system_operation_context.h" #include "webkit/fileapi/file_system_path_manager.h" #include "webkit/fileapi/file_system_types.h" +#include "webkit/fileapi/file_system_util.h" +#include "webkit/fileapi/file_system_file_util.h" +#include "webkit/fileapi/local_file_system_file_util.h" #include "ui/base/l10n/l10n_util.h" +// Error messages. +const char kFileError[] = "File error %d"; +const char kInvalidFileUrl[] = "Invalid file URL"; + +const int kReadOnlyFilePermissions = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_EXCLUSIVE_READ | + base::PLATFORM_FILE_ASYNC; + +const int kReadWriteFilePermissions = base::PLATFORM_FILE_OPEN | + base::PLATFORM_FILE_CREATE | + base::PLATFORM_FILE_OPEN_ALWAYS | + base::PLATFORM_FILE_CREATE_ALWAYS | + base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_WRITE | + base::PLATFORM_FILE_EXCLUSIVE_READ | + base::PLATFORM_FILE_EXCLUSIVE_WRITE | + base::PLATFORM_FILE_ASYNC | + base::PLATFORM_FILE_TRUNCATE | + base::PLATFORM_FILE_WRITE_ATTRIBUTES; + +typedef std::vector< + std::pair<std::string, const FileBrowserHandler* > > + NamedHandlerList; + +typedef std::vector<const FileBrowserHandler*> ActionList; + +bool GetFileBrowserHandlers(Profile* profile, + const GURL& selected_file_url, + ActionList* results) { + ExtensionService* service = profile->GetExtensionService(); + if (!service) + return false; // In unit-tests, we may not have an ExtensionService. + + for (ExtensionList::const_iterator iter = service->extensions()->begin(); + iter != service->extensions()->end(); + ++iter) { + const Extension* extension = iter->get(); + if (!extension->file_browser_handlers()) + continue; + + for (Extension::FileBrowserHandlerList::const_iterator action_iter = + extension->file_browser_handlers()->begin(); + action_iter != extension->file_browser_handlers()->end(); + ++action_iter) { + const FileBrowserHandler* action = action_iter->get(); + if (!action->MatchesURL(selected_file_url)) + continue; + + results->push_back(action_iter->get()); + } + } + return true; +} + +// Given the list of selected files, returns array of context menu tasks +// that are shared +bool FindCommonTasks(Profile* profile, + ListValue* files_list, + NamedHandlerList* named_action_list) { + named_action_list->clear(); + ActionList common_tasks; + for (size_t i = 0; i < files_list->GetSize(); ++i) { + std::string file_url; + if (!files_list->GetString(i, &file_url)) + return false; + + ActionList file_actions; + if (!GetFileBrowserHandlers(profile, GURL(file_url), &file_actions)) + return false; + // If there is nothing to do for one file, the intersection of tasks for all + // files will be empty at the end. + if (!file_actions.size()) { + common_tasks.clear(); + return true; + } + // For the very first file, just copy elements. + if (i == 0) { + common_tasks.insert(common_tasks.begin(), + file_actions.begin(), + file_actions.end()); + std::sort(common_tasks.begin(), common_tasks.end()); + } else if (common_tasks.size()) { + // For all additional files, find intersection between the accumulated + // and file specific set. + std::sort(file_actions.begin(), file_actions.end()); + ActionList intersection(common_tasks.size()); + ActionList::iterator intersection_end = + std::set_intersection(common_tasks.begin(), + common_tasks.end(), + file_actions.begin(), + file_actions.end(), + intersection.begin()); + common_tasks.clear(); + common_tasks.insert(common_tasks.begin(), + intersection.begin(), + intersection_end); + std::sort(common_tasks.begin(), common_tasks.end()); + } + } + + // At the end, sort the results by task title. + // TODO(zelidrag): Wire this with ICU to make this sort I18N happy. + for (ActionList::const_iterator iter = common_tasks.begin(); + iter != common_tasks.end(); ++iter) { + named_action_list->push_back( + std::pair<std::string, const FileBrowserHandler* >( + (*iter)->title(), *iter)); + } + std::sort(named_action_list->begin(), named_action_list->end()); + return true; +} + +// Breaks down task_id that is used between getFileTasks() and executeTask() on +// its building blocks. task_id field the following structure: +// <task-type>:<extension-id>/<task-action-id> +// Currently, the only supported task-type is of 'context'. +bool CrackTaskIdentifier(const std::string& task_id, + std::string* target_extension_id, + std::string* action_id) { + std::vector<std::string> result; + int count = Tokenize(task_id, std::string("|"), &result); + if (count != 2) + return false; + *target_extension_id = result[0]; + *action_id = result[1]; + return true; +} + +std::string MakeTaskID(const char* extension_id, + const char* action_id) { + return base::StringPrintf("%s|%s", extension_id, action_id); +} class LocalFileSystemCallbackDispatcher : public fileapi::FileSystemCallbackDispatcher { public: explicit LocalFileSystemCallbackDispatcher( - RequestLocalFileSystemFunction* function) : function_(function) { + RequestLocalFileSystemFunction* function, + Profile* profile, + int child_id, + scoped_refptr<const Extension> extension) + : function_(function), + profile_(profile), + child_id_(child_id), + extension_(extension) { DCHECK(function_); } + // fileapi::FileSystemCallbackDispatcher overrides. virtual void DidSucceed() OVERRIDE { NOTREACHED(); } + virtual void DidReadMetadata(const base::PlatformFileInfo& info, const FilePath& unused) OVERRIDE { NOTREACHED(); } + virtual void DidReadDirectory( const std::vector<base::FileUtilProxy::Entry>& entries, bool has_more) OVERRIDE { NOTREACHED(); } + virtual void DidWrite(int64 bytes, bool complete) OVERRIDE { NOTREACHED(); } + virtual void DidOpenFileSystem(const std::string& name, - const GURL& root) OVERRIDE { + const GURL& root_path) OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + // Set up file permission access. + if (!SetupFileSystemAccessPermissions()) { + DidFail(base::PLATFORM_FILE_ERROR_SECURITY); + return; + } + BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod(function_, &RequestLocalFileSystemFunction::RespondSuccessOnUIThread, name, - root)); + root_path)); } + virtual void DidFail(base::PlatformFileError error_code) OVERRIDE { BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, @@ -60,38 +233,92 @@ class LocalFileSystemCallbackDispatcher &RequestLocalFileSystemFunction::RespondFailedOnUIThread, error_code)); } + private: + + // Grants file system access permissions to file browser component. + bool SetupFileSystemAccessPermissions() { + if (!extension_.get()) + return false; + + // Make sure that only component extension can access the entire + // local file system. + if (extension_->location() != Extension::COMPONENT +#ifndef NDEBUG + && !CommandLine::ForCurrentProcess()->HasSwitch( + switches::kExposePrivateExtensionApi) +#endif + ) { + NOTREACHED() << "Private method access by non-component extension " + << extension_->id(); + return false; + } + + fileapi::FileSystemPathManager* path_manager = + profile_->GetFileSystemContext()->path_manager(); + fileapi::ExternalFileSystemMountPointProvider* provider = + path_manager->external_provider(); + if (!provider) + return false; + + // Grant full access to File API from this component extension. + provider->GrantFullAccessToExtension(extension_->id()); + + // Grant R/W file permissions to the renderer hosting component + // extension for all paths exposed by our local file system provider. + std::vector<FilePath> root_dirs = provider->GetRootDirectories(); + for (std::vector<FilePath>::iterator iter = root_dirs.begin(); + iter != root_dirs.end(); + ++iter) { + ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile( + child_id_, *iter, kReadWriteFilePermissions); + } + return true; + } + RequestLocalFileSystemFunction* function_; + Profile* profile_; + // Renderer process id. + int child_id_; + // Extension source URL. + scoped_refptr<const Extension> extension_; DISALLOW_COPY_AND_ASSIGN(LocalFileSystemCallbackDispatcher); }; -RequestLocalFileSystemFunction::RequestLocalFileSystemFunction() { -} - -RequestLocalFileSystemFunction::~RequestLocalFileSystemFunction() { -} - -bool RequestLocalFileSystemFunction::RunImpl() { +void RequestLocalFileSystemFunction::RequestOnFileThread( + const GURL& source_url) { fileapi::FileSystemOperation* operation = new fileapi::FileSystemOperation( - new LocalFileSystemCallbackDispatcher(this), + new LocalFileSystemCallbackDispatcher( + this, + profile(), + dispatcher()->render_view_host()->process()->id(), + GetExtension()), BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), profile()->GetFileSystemContext(), NULL); - GURL origin_url = source_url().GetOrigin(); + GURL origin_url = source_url.GetOrigin(); operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeExternal, false); // create +} + +bool RequestLocalFileSystemFunction::RunImpl() { + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(this, + &RequestLocalFileSystemFunction::RequestOnFileThread, + source_url_)); // Will finish asynchronously. return true; } void RequestLocalFileSystemFunction::RespondSuccessOnUIThread( - const std::string& name, const GURL& root) { + const std::string& name, const GURL& root_path) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); result_.reset(new DictionaryValue()); DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get()); dict->SetString("name", name); - dict->SetString("path", root.spec()); + dict->SetString("path", root_path.spec()); dict->SetInteger("error", base::PLATFORM_FILE_OK); SendResponse(true); } @@ -99,10 +326,354 @@ void RequestLocalFileSystemFunction::RespondSuccessOnUIThread( void RequestLocalFileSystemFunction::RespondFailedOnUIThread( base::PlatformFileError error_code) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - result_.reset(new DictionaryValue()); - DictionaryValue* dict = reinterpret_cast<DictionaryValue*>(result_.get()); - dict->SetInteger("error", static_cast<int>(error_code)); + error_ = base::StringPrintf(kFileError, static_cast<int>(error_code)); + SendResponse(false); +} + +bool GetFileTasksFileBrowserFunction::RunImpl() { + ListValue* files_list = NULL; + if (!args_->GetList(0, &files_list)) + return false; + + ListValue* result_list = new ListValue(); + result_.reset(result_list); + + NamedHandlerList common_tasks; + if (!FindCommonTasks(profile_, files_list, &common_tasks)) + return false; + + ExtensionService* service = profile_->GetExtensionService(); + for (NamedHandlerList::iterator iter = common_tasks.begin(); + iter != common_tasks.end(); + ++iter) { + const std::string extension_id = iter->second->extension_id(); + const Extension* extension = service->GetExtensionById(extension_id, false); + CHECK(extension); + DictionaryValue* task = new DictionaryValue(); + task->SetString("taskId", MakeTaskID(extension_id.data(), + iter->second->id().data())); + task->SetString("title", iter->second->title()); + // TODO(zelidrag): Figure out how to expose icon URL that task defined in + // manifest instead of the default extension icon. + GURL icon = + ExtensionIconSource::GetIconURL(extension, + Extension::EXTENSION_ICON_SMALLISH, + ExtensionIconSet::MATCH_BIGGER, + false); // grayscale + task->SetString("iconUrl", icon.spec()); + result_list->Append(task); + } + + // TODO(zelidrag, serya): Add intent content tasks to result_list once we + // implement that API. + SendResponse(true); + return true; +} + +class ExecuteTasksFileSystemCallbackDispatcher + : public fileapi::FileSystemCallbackDispatcher { + public: + explicit 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) + : function_(function), + profile_(profile), + source_url_(source_url), + extension_(extension), + task_id_(task_id), + origin_file_urls_(file_urls) { + DCHECK(function_); + } + + // fileapi::FileSystemCallbackDispatcher overrides. + virtual void DidSucceed() OVERRIDE { + NOTREACHED(); + } + + virtual void DidReadMetadata(const base::PlatformFileInfo& info, + const FilePath& unused) OVERRIDE { + NOTREACHED(); + } + + virtual void DidReadDirectory( + const std::vector<base::FileUtilProxy::Entry>& entries, + bool has_more) OVERRIDE { + NOTREACHED(); + } + + virtual void DidWrite(int64 bytes, bool complete) OVERRIDE { + NOTREACHED(); + } + + virtual void DidOpenFileSystem(const std::string& file_system_name, + const GURL& file_system_root) OVERRIDE { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + ExecuteTasksFileBrowserFunction::FileDefinitionList file_list; + for (std::vector<GURL>::iterator iter = origin_file_urls_.begin(); + iter != origin_file_urls_.end(); + ++iter) { + // Set up file permission access. + ExecuteTasksFileBrowserFunction::FileDefinition file; + if (!SetupFileAccessPermissions(*iter, &file.target_file_url, + &file.virtual_path, &file.is_directory)) { + continue; + } + file_list.push_back(file); + } + if (file_list.empty()) + return; + + BrowserThread::PostTask( + BrowserThread::UI, FROM_HERE, + NewRunnableMethod(function_, + &ExecuteTasksFileBrowserFunction::ExecuteFileActionsOnUIThread, + task_id_, + file_system_name, + file_system_root, + file_list)); + } + + virtual void DidFail(base::PlatformFileError error_code) OVERRIDE { + LOG(WARNING) << "Local file system cant be resolved"; + } + + private: + // Checks legitimacy of file url and grants file RO access permissions from + // handler (target) extension and its renderer process. + bool SetupFileAccessPermissions(const GURL& origin_file_url, + GURL* target_file_url, FilePath* file_path, bool* is_directory) { + + if (!extension_.get()) + return false; + + GURL file_origin_url; + FilePath virtual_path; + fileapi::FileSystemType type; + + if (!CrackFileSystemURL(origin_file_url, &file_origin_url, &type, + &virtual_path)) { + return false; + } + + if (type != fileapi::kFileSystemTypeExternal) + return false; + + fileapi::FileSystemPathManager* path_manager = + profile_->GetFileSystemContext()->path_manager(); + if (!path_manager->IsAccessAllowed(file_origin_url, + type, + virtual_path)) { + return false; + } + + // Make sure this url really being used by the right caller extension. + if (source_url_.GetOrigin() != file_origin_url) { + DidFail(base::PLATFORM_FILE_ERROR_SECURITY); + return false; + } + + FilePath root_path = path_manager->GetFileSystemRootPathOnFileThread( + file_origin_url, + fileapi::kFileSystemTypeExternal, + virtual_path, + false); // create + FilePath final_file_path = root_path.Append(virtual_path); + + // Check if this file system entry exists first. + base::PlatformFileInfo file_info; + FilePath platform_path; + fileapi::FileSystemOperationContext file_system_operation_context( + profile_->GetFileSystemContext(), + fileapi::LocalFileSystemFileUtil::GetInstance()); + if (base::PLATFORM_FILE_OK != + fileapi::FileSystemFileUtil::GetInstance()->GetFileInfo( + &file_system_operation_context, final_file_path, &file_info, + &platform_path)) { + return false; + } + + // TODO(zelidrag): Let's just prevent all symlinks for now. We don't want a + // USB drive content to point to something in the rest of the file system. + // Ideally, we should permit symlinks within the boundary of the same + // virtual mount point. + if (file_info.is_symbolic_link) + return false; + + // TODO(zelidrag): Add explicit R/W + R/O permissions for non-component + // extensions. + + // Get task details. + std::string target_extension_id; + std::string action_id; + if (!CrackTaskIdentifier(task_id_, &target_extension_id, + &action_id)) { + return false; + } + + // Get target extension's process. + RenderProcessHost* target_host = + profile_->GetExtensionProcessManager()->GetExtensionProcess( + target_extension_id); + if (!target_host) + return false; + + // Grant R/O access permission to non-component extension and R/W to + // component extensions. + ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile( + target_host->id(), final_file_path, + extension_->location() != Extension::COMPONENT ? + kReadOnlyFilePermissions : kReadWriteFilePermissions); + + // Grant access to this particular file to target extension. This will + // ensure that the target extension can access only this FS entry and + // prevent from traversing FS hierarchy upward. + fileapi::ExternalFileSystemMountPointProvider* external_provider = + path_manager->external_provider(); + if (!external_provider) + return false; + external_provider->GrantFileAccessToExtension(target_extension_id, + virtual_path); + + // Output values. + GURL target_origin_url(Extension::GetBaseURLFromExtensionId( + target_extension_id)); + GURL base_url = fileapi::GetFileSystemRootURI(target_origin_url, + fileapi::kFileSystemTypeExternal); + *target_file_url = GURL(base_url.spec() + virtual_path.value()); + *file_path = virtual_path; + *is_directory = file_info.is_directory; + return true; + } + + ExecuteTasksFileBrowserFunction* function_; + Profile* profile_; + // Extension source URL. + GURL source_url_; + scoped_refptr<const Extension> extension_; + std::string task_id_; + std::vector<GURL> origin_file_urls_; + DISALLOW_COPY_AND_ASSIGN(ExecuteTasksFileSystemCallbackDispatcher); +}; + +bool ExecuteTasksFileBrowserFunction::RunImpl() { + // First param is task id that was to the extension with getFileTasks call. + std::string task_id; + if (!args_->GetString(0, &task_id) || !task_id.size()) + return false; + + // The second param is the list of files that need to be executed with this + // task. + ListValue* files_list = NULL; + if (!args_->GetList(1, &files_list)) + return false; + + if (!files_list->GetSize()) + return true; + + InitiateFileTaskExecution(task_id, files_list); SendResponse(true); + return true; +} + +bool ExecuteTasksFileBrowserFunction::InitiateFileTaskExecution( + const std::string& task_id, ListValue* files_list) { + std::vector<GURL> file_urls; + for (size_t i = 0; i < files_list->GetSize(); i++) { + std::string origin_file_url; + if (!files_list->GetString(i, &origin_file_url)) { + error_ = kInvalidFileUrl; + SendResponse(false); + return false; + } + file_urls.push_back(GURL(origin_file_url)); + } + // Get local file system instance on file thread. + BrowserThread::PostTask( + BrowserThread::FILE, FROM_HERE, + NewRunnableMethod(this, + &ExecuteTasksFileBrowserFunction::RequestFileEntryOnFileThread, + source_url_, + task_id, + file_urls)); + result_.reset(new FundamentalValue(true)); + return true; +} + +void ExecuteTasksFileBrowserFunction::RequestFileEntryOnFileThread( + const GURL& source_url, const std::string& task_id, + const std::vector<GURL>& file_urls) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); + fileapi::FileSystemOperation* operation = + new fileapi::FileSystemOperation( + new ExecuteTasksFileSystemCallbackDispatcher( + this, + profile(), + dispatcher()->render_view_host()->process()->id(), + source_url, + GetExtension(), + task_id, + file_urls), + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), + profile()->GetFileSystemContext(), + NULL); + GURL origin_url = source_url.GetOrigin(); + operation->OpenFileSystem(origin_url, fileapi::kFileSystemTypeExternal, + false); // create +} + +void ExecuteTasksFileBrowserFunction::ExecuteFileActionsOnUIThread( + const std::string& task_id, + const std::string& file_system_name, + const GURL& file_system_root, + const FileDefinitionList& file_list) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + ExtensionService* service = profile_->GetExtensionService(); + if (!service) + return; + // Get task details. + std::string handler_extension_id; + std::string action_id; + if (!CrackTaskIdentifier(task_id, &handler_extension_id, + &action_id)) { + LOG(WARNING) << "Invalid task " << task_id; + return; + } + + const Extension* extension = service->GetExtensionById(handler_extension_id, + false); + if (!extension) + return; + + ExtensionEventRouter* event_router = profile_->GetExtensionEventRouter(); + if (!event_router) + return; + + scoped_ptr<ListValue> event_args(new ListValue()); + ListValue* files_urls = new ListValue(); + event_args->Append(Value::CreateStringValue(action_id)); + event_args->Append(files_urls); + for (FileDefinitionList::const_iterator iter = file_list.begin(); + iter != file_list.end(); + ++iter) { + DictionaryValue* file_def = new DictionaryValue(); + files_urls->Append(file_def); + file_def->SetString("fileSystemName", file_system_name); + file_def->SetString("fileSystemRoot", file_system_root.spec()); + file_def->SetString("fileFullPath", iter->virtual_path.value()); + file_def->SetBoolean("fileIsDirectory", iter->is_directory); + } + std::string json_args; + base::JSONWriter::Write(event_args.get(), false, &json_args); + std::string event_name = "contextMenus"; + event_router->DispatchEventToExtension( + handler_extension_id, std::string("fileBrowserHandler.onExecute"), + json_args, profile_, + GURL()); } FileDialogFunction::FileDialogFunction() { diff --git a/chrome/browser/extensions/extension_file_browser_private_api.h b/chrome/browser/extensions/extension_file_browser_private_api.h index d6d1005..684e8e1 100644 --- a/chrome/browser/extensions/extension_file_browser_private_api.h +++ b/chrome/browser/extensions/extension_file_browser_private_api.h @@ -13,29 +13,62 @@ #include "base/platform_file.h" #include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/ui/shell_dialogs.h" +#include "googleurl/src/url_util.h" #include "webkit/fileapi/file_system_callback_dispatcher.h" class GURL; -// Implements the Chrome Extension local File API. -class RequestLocalFileSystemFunction - : public AsyncExtensionFunction { - public: - RequestLocalFileSystemFunction(); +// Implements the chrome.fileBrowserPrivate.requestLocalFileSystem method. +class RequestLocalFileSystemFunction : public AsyncExtensionFunction { + protected: + friend class LocalFileSystemCallbackDispatcher; + // AsyncExtensionFunction overrides. + virtual bool RunImpl() OVERRIDE; + void RespondSuccessOnUIThread(const std::string& name, + const GURL& root_path); + void RespondFailedOnUIThread(base::PlatformFileError error_code); + void RequestOnFileThread(const GURL& source_url); + DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.requestLocalFileSystem"); +}; +// Implements the chrome.fileBrowserPrivate.getFileTasks method. +class GetFileTasksFileBrowserFunction : public AsyncExtensionFunction { protected: - virtual ~RequestLocalFileSystemFunction(); + // AsyncExtensionFunction overrides. + virtual bool RunImpl() OVERRIDE; + + private: + DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.getFileTasks"); +}; + +// Implements the chrome.fileBrowserPrivate.executeTask method. +class ExecuteTasksFileBrowserFunction : public AsyncExtensionFunction { + protected: // AsyncExtensionFunction overrides. virtual bool RunImpl() OVERRIDE; private: - friend class LocalFileSystemCallbackDispatcher; - void RespondSuccessOnUIThread(const std::string& name, - const GURL& root); + struct FileDefinition { + GURL target_file_url; + FilePath virtual_path; + bool is_directory; + }; + typedef std::vector<FileDefinition> FileDefinitionList; + friend class ExecuteTasksFileSystemCallbackDispatcher; + // Initates execution of context menu tasks identified with |task_id| for + // each element of |files_list|. + bool InitiateFileTaskExecution(const std::string& task_id, + ListValue* files_list); + void RequestFileEntryOnFileThread(const GURL& source_url, + const std::string& task_id, + const std::vector<GURL>& file_urls); void RespondFailedOnUIThread(base::PlatformFileError error_code); - - DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.requestLocalFileSystem"); + void ExecuteFileActionsOnUIThread(const std::string& task_id, + const std::string& file_system_name, + const GURL& file_system_root, + const FileDefinitionList& file_list); + DECLARE_EXTENSION_FUNCTION_NAME("fileBrowserPrivate.executeTask"); }; // Parent class for the chromium extension APIs for the file dialog. diff --git a/chrome/browser/extensions/extension_function_dispatcher.cc b/chrome/browser/extensions/extension_function_dispatcher.cc index 69494a3..1fd1ccf 100644 --- a/chrome/browser/extensions/extension_function_dispatcher.cc +++ b/chrome/browser/extensions/extension_function_dispatcher.cc @@ -20,7 +20,6 @@ #include "chrome/browser/extensions/extension_context_menu_api.h" #include "chrome/browser/extensions/extension_cookies_api.h" #include "chrome/browser/extensions/extension_debugger_api.h" -#include "chrome/browser/extensions/extension_file_browser_private_api.h" #include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/extensions/extension_history_api.h" #include "chrome/browser/extensions/extension_i18n_api.h" @@ -66,6 +65,7 @@ #endif #if defined(OS_CHROMEOS) +#include "chrome/browser/extensions/extension_file_browser_private_api.h" #include "chrome/browser/extensions/extension_info_private_api_chromeos.h" #endif @@ -201,9 +201,6 @@ void FactoryRegistry::ResetFunctions() { // Processes. RegisterFunction<GetProcessIdForTabFunction>(); - // Local filesystem. - RegisterFunction<RequestLocalFileSystemFunction>(); - // Metrics. RegisterFunction<MetricsGetEnabledFunction>(); RegisterFunction<MetricsSetEnabledFunction>(); @@ -312,15 +309,19 @@ void FactoryRegistry::ResetFunctions() { RegisterFunction<SetPreferenceFunction>(); RegisterFunction<ClearPreferenceFunction>(); - // File Dialog. + // ChromeOS-specific part of the API. +#if defined(OS_CHROMEOS) + // Device Customization. + RegisterFunction<GetChromeosInfoFunction>(); + + // FileBrowserPrivate functions. + RegisterFunction<ExecuteTasksFileBrowserFunction>(); + RegisterFunction<GetFileTasksFileBrowserFunction>(); + RegisterFunction<RequestLocalFileSystemFunction>(); RegisterFunction<SelectFileFunction>(); RegisterFunction<SelectFilesFunction>(); RegisterFunction<CancelFileDialogFunction>(); RegisterFunction<FileDialogStringsFunction>(); - -#if defined(OS_CHROMEOS) - // Device Customization. - RegisterFunction<GetChromeosInfoFunction>(); #endif // Debugger diff --git a/chrome/browser/extensions/extension_local_filesystem_apitest.cc b/chrome/browser/extensions/extension_local_filesystem_apitest.cc index 5a8ba72..856cbb4 100644 --- a/chrome/browser/extensions/extension_local_filesystem_apitest.cc +++ b/chrome/browser/extensions/extension_local_filesystem_apitest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -6,9 +6,13 @@ #if defined(OS_CHROMEOS) -// TODO(zelidrag): Remove disable prefix on this test once API changes land. -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_LocalFileSystem) { +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, LocalFileSystem) { ASSERT_TRUE(RunComponentExtensionTest("local_filesystem")) << message_; } +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, FileBrowserTest) { + ASSERT_TRUE(RunExtensionTest("filesystem_handler")) << message_; + ASSERT_TRUE(RunComponentExtensionTest("filebrowser_component")) << message_; +} + #endif |