| Index: chrome/browser/extensions/extension_file_browser_private_api.cc
|
| ===================================================================
|
| --- chrome/browser/extensions/extension_file_browser_private_api.cc (revision 81212)
|
| +++ chrome/browser/extensions/extension_file_browser_private_api.cc (working copy)
|
| @@ -4,54 +4,223 @@
|
|
|
| #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_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_action.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 "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_path_manager.h"
|
| #include "webkit/fileapi/file_system_types.h"
|
| +#include "webkit/fileapi/file_system_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 FileBrowserAction* > >
|
| + NamedActionList;
|
| +
|
| +typedef std::vector<const FileBrowserAction*> ActionList;
|
| +
|
| +bool GetFileBrowserActions(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_actions())
|
| + continue;
|
| +
|
| + for (Extension::FileBrowserActionList::const_iterator action_iter =
|
| + extension->file_browser_actions()->begin();
|
| + action_iter != extension->file_browser_actions()->end();
|
| + ++action_iter) {
|
| + const FileBrowserAction* 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,
|
| + NamedActionList* 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 (!GetFileBrowserActions(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 FileBrowserAction* >(
|
| + (*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 FilePath& path) OVERRIDE {
|
| + const FilePath& 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,
|
| - path));
|
| + root_path));
|
| }
|
| +
|
| virtual void DidFail(base::PlatformFileError error_code) OVERRIDE {
|
| BrowserThread::PostTask(
|
| BrowserThread::UI, FROM_HERE,
|
| @@ -59,38 +228,89 @@
|
| &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::FileSystemMountPointProvider* provider =
|
| + path_manager->local_provider();
|
| + if (!provider)
|
| + return false;
|
| +
|
| + // 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::kFileSystemTypeLocal,
|
| 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 FilePath& path) {
|
| + const std::string& name, const FilePath& 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", path.value());
|
| + dict->SetString("path", root_path.value());
|
| dict->SetInteger("error", base::PLATFORM_FILE_OK);
|
| SendResponse(true);
|
| }
|
| @@ -98,12 +318,326 @@
|
| 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);
|
| +
|
| + NamedActionList common_tasks;
|
| + if (!FindCommonTasks(profile_, files_list, &common_tasks))
|
| + return false;
|
| +
|
| + ExtensionService* service = profile_->GetExtensionService();
|
| + for (NamedActionList::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<FilePath>& origin_file_paths)
|
| + : function_(function),
|
| + profile_(profile),
|
| + child_id_(child_id),
|
| + source_url_(source_url),
|
| + extension_(extension),
|
| + task_id_(task_id),
|
| + origin_file_paths_(origin_file_paths) {
|
| + 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 FilePath& file_system_root) OVERRIDE {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
|
| + ExecuteTasksFileBrowserFunction::FileDefinitionList file_list;
|
| + for (std::vector<FilePath>::iterator iter = origin_file_paths_.begin();
|
| + iter != origin_file_paths_.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 FilePath& origin_file_path,
|
| + 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;
|
| + fileapi::FileSystemPathManager* path_manager =
|
| + profile_->GetFileSystemContext()->path_manager();
|
| + if (!path_manager->CrackFileSystemPath(origin_file_path, &file_origin_url,
|
| + &type, &virtual_path)) {
|
| + return false;
|
| + }
|
| +
|
| + if (type != fileapi::kFileSystemTypeLocal)
|
| + 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::kFileSystemTypeLocal,
|
| + virtual_path,
|
| + false); // create
|
| + FilePath finalFilePath = root_path.Append(virtual_path);
|
| +
|
| + // TODO(zelidrag): Add explicit R/W + R/O permissions for non-component
|
| + // extensions.
|
| +
|
| + // Grant R/O access permission to non-component extension and R/W to
|
| + // component extensions.
|
| + ChildProcessSecurityPolicy::GetInstance()->GrantPermissionsForFile(
|
| + child_id_, finalFilePath,
|
| + extension_->location() != Extension::COMPONENT ?
|
| + kReadOnlyFilePermissions : kReadWriteFilePermissions);
|
| +
|
| + // Get task details.
|
| + std::string target_extension_id;
|
| + std::string action_id;
|
| + if (!CrackTaskIdentifier(task_id_, &target_extension_id,
|
| + &action_id)) {
|
| + return false;
|
| + }
|
| +
|
| + // 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.
|
| + GURL target_origin_url(Extension::GetBaseURLFromExtensionId(
|
| + target_extension_id));
|
| + fileapi::FileSystemMountPointProvider* local_provider =
|
| + path_manager->local_provider();
|
| + if (!local_provider)
|
| + return false;
|
| + local_provider->GrantAccess(target_origin_url, virtual_path);
|
| +
|
| + // Output values.
|
| + GURL base_url = fileapi::GetFileSystemRootURI(target_origin_url,
|
| + fileapi::kFileSystemTypeLocal);
|
| + *target_file_url = GURL(base_url.spec() + virtual_path.value());
|
| + *file_path = virtual_path;
|
| + // TODO(zelidrag): Prevent directory passing for now, add support for it
|
| + // later.
|
| + is_directory = false;
|
| + return true;
|
| + }
|
| +
|
| + ExecuteTasksFileBrowserFunction* function_;
|
| + Profile* profile_;
|
| + // Renderer process id.
|
| + int child_id_;
|
| + // Extension source URL.
|
| + GURL source_url_;
|
| + scoped_refptr<const Extension> extension_;
|
| + std::string task_id_;
|
| + std::vector<FilePath> origin_file_paths_;
|
| + 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<FilePath> file_paths;
|
| + 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_paths.push_back(FilePath(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_paths));
|
| + result_.reset(new FundamentalValue(true));
|
| + return true;
|
| +}
|
| +
|
| +void ExecuteTasksFileBrowserFunction::RequestFileEntryOnFileThread(
|
| + const GURL& source_url, const std::string& task_id,
|
| + const std::vector<FilePath>& 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::kFileSystemTypeLocal,
|
| + false); // create
|
| +}
|
| +
|
| +void ExecuteTasksFileBrowserFunction::ExecuteFileActionsOnUIThread(
|
| + const std::string& task_id,
|
| + const std::string& file_system_name,
|
| + const FilePath& 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.value());
|
| + 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.onExecuteAction"),
|
| + json_args, profile_,
|
| + GURL());
|
| +}
|
| +
|
| FileDialogFunction::FileDialogFunction() {
|
| }
|
|
|
|
|