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

Unified Diff: chrome/browser/extensions/extension_file_browser_private_api.cc

Issue 6749021: Added new fileBrowserPrivate and fileHandler extension APIs (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/extensions/extension_file_browser_private_api.cc
===================================================================
--- chrome/browser/extensions/extension_file_browser_private_api.cc (revision 81868)
+++ chrome/browser/extensions/extension_file_browser_private_api.cc (working copy)
@@ -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 @@
&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,12 +326,356 @@
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() {
}

Powered by Google App Engine
This is Rietveld 408576698