blob: ef75d71b704a8854ff888844a85087235c0c6aea [file] [log] [blame]
// Copyright (c) 2012 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.
#include "chrome/browser/chromeos/file_manager/open_util.h"
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chromeos/drive/file_system_util.h"
#include "chrome/browser/chromeos/file_manager/app_id.h"
#include "chrome/browser/chromeos/file_manager/file_browser_handlers.h"
#include "chrome/browser/chromeos/file_manager/file_tasks.h"
#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
#include "chrome/browser/chromeos/file_manager/mime_util.h"
#include "chrome/browser/chromeos/file_manager/url_util.h"
#include "chrome/browser/extensions/api/file_handlers/app_file_handler_util.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/google_apis/task_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_tabstrip.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/extensions/application_launch.h"
#include "chrome/browser/ui/simple_message_box.h"
#include "chrome/common/extensions/api/file_browser_handlers/file_browser_handler.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/user_metrics.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"
#include "webkit/browser/fileapi/file_system_backend.h"
#include "webkit/browser/fileapi/file_system_context.h"
#include "webkit/browser/fileapi/file_system_operation_runner.h"
#include "webkit/browser/fileapi/file_system_url.h"
using content::BrowserContext;
using content::BrowserThread;
using content::UserMetricsAction;
using extensions::Extension;
using extensions::app_file_handler_util::FindFileHandlersForFiles;
using extensions::app_file_handler_util::PathAndMimeTypeSet;
using fileapi::FileSystemURL;
namespace file_manager {
namespace util {
namespace {
// Shows a warning message box saying that the file could not be opened.
void ShowWarningMessageBox(Profile* profile, const base::FilePath& file_path) {
Browser* browser = chrome::FindTabbedBrowser(
profile, false, chrome::HOST_DESKTOP_TYPE_ASH);
chrome::ShowMessageBox(
browser ? browser->window()->GetNativeWindow() : NULL,
l10n_util::GetStringFUTF16(
IDS_FILE_BROWSER_ERROR_VIEWING_FILE_TITLE,
UTF8ToUTF16(file_path.BaseName().value())),
l10n_util::GetStringUTF16(IDS_FILE_BROWSER_ERROR_VIEWING_FILE),
chrome::MESSAGE_BOX_TYPE_WARNING);
}
// Grants file system access to the file manager.
bool GrantFileSystemAccessToFileBrowser(Profile* profile) {
// The file manager always runs in the site for its extension id, so that
// is the site for which file access permissions should be granted.
fileapi::ExternalFileSystemBackend* backend =
GetFileSystemContextForExtensionId(
profile, kFileManagerAppId)->external_backend();
if (!backend)
return false;
backend->GrantFullAccessToExtension(kFileManagerAppId);
return true;
}
// Executes the |task| for the file specified by |url|.
void ExecuteFileTaskForUrl(Profile* profile,
const file_tasks::TaskDescriptor& task,
const GURL& url) {
// If the file manager has not been open yet then it did not request access
// to the file system. Do it now.
if (!GrantFileSystemAccessToFileBrowser(profile))
return;
fileapi::FileSystemContext* file_system_context =
GetFileSystemContextForExtensionId(
profile, kFileManagerAppId);
// We are executing the task on behalf of the file manager.
const GURL source_url = GetFileManagerMainPageUrl();
std::vector<FileSystemURL> urls;
urls.push_back(file_system_context->CrackURL(url));
file_tasks::ExecuteFileTask(
profile,
source_url,
kFileManagerAppId,
0, // no tab id
task,
urls,
file_tasks::FileTaskFinishedCallback());
}
// Opens the file manager for the specified |file_path|. Used to implement
// internal handlers of special action IDs:
//
// "open" - Open the file manager for the given folder.
// "auto-open" - Open the file manager for the given removal drive and close
// the file manager when the removal drive is unmounted.
// "select" - Open the file manager for the given file. The folder containing
// the file will be opened with the file selected.
void OpenFileManagerWithInternalActionId(const base::FilePath& file_path,
const std::string& action_id) {
DCHECK(action_id == "auto-open" ||
action_id == "open" ||
action_id == "select");
content::RecordAction(UserMetricsAction("ShowFileBrowserFullTab"));
Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
GURL url;
if (!ConvertAbsoluteFilePathToFileSystemUrl(
profile, file_path, kFileManagerAppId, &url))
return;
file_tasks::TaskDescriptor task(kFileManagerAppId,
file_tasks::TASK_TYPE_FILE_BROWSER_HANDLER,
action_id);
ExecuteFileTaskForUrl(profile, task, url);
}
// Opens the file specified by |file_path| by finding and executing a file
// task for the file. Returns false if failed to open the file (i.e. no file
// task is found).
bool OpenFile(Profile* profile, const base::FilePath& file_path) {
GURL url;
if (!ConvertAbsoluteFilePathToFileSystemUrl(
profile, file_path, kFileManagerAppId, &url))
return false;
// The file is opened per the file extension, hence extension-less files
// cannot be opened properly.
std::string mime_type = GetMimeTypeForPath(file_path);
extensions::app_file_handler_util::PathAndMimeTypeSet path_mime_set;
path_mime_set.insert(std::make_pair(file_path, mime_type));
std::vector<GURL> file_urls;
file_urls.push_back(url);
std::vector<file_tasks::FullTaskDescriptor> tasks;
file_tasks::FindAllTypesOfTasks(
profile,
drive::util::GetDriveAppRegistryByProfile(profile),
path_mime_set,
file_urls,
&tasks);
if (tasks.empty())
return false;
const file_tasks::FullTaskDescriptor* chosen_task = &tasks[0];
for (size_t i = 0; i < tasks.size(); ++i) {
if (tasks[i].is_default()) {
chosen_task = &tasks[i];
break;
}
}
ExecuteFileTaskForUrl(profile, chosen_task->task_descriptor(), url);
return true;
}
// Used to implement OpenItem().
void ContinueOpenItem(Profile* profile,
const base::FilePath& file_path,
base::PlatformFileError error) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (error == base::PLATFORM_FILE_OK) {
// A directory exists at |file_path|. Open it with the file manager.
OpenFileManagerWithInternalActionId(file_path, "open");
} else {
// |file_path| should be a file. Open it.
if (!OpenFile(profile, file_path))
ShowWarningMessageBox(profile, file_path);
}
}
// Used to implement CheckIfDirectoryExists().
void CheckIfDirectoryExistsOnIOThread(
scoped_refptr<fileapi::FileSystemContext> file_system_context,
const GURL& url,
const fileapi::FileSystemOperationRunner::StatusCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
fileapi::FileSystemURL file_system_url = file_system_context->CrackURL(url);
file_system_context->operation_runner()->DirectoryExists(
file_system_url, callback);
}
// Checks if a directory exists at |url|.
void CheckIfDirectoryExists(
scoped_refptr<fileapi::FileSystemContext> file_system_context,
const GURL& url,
const fileapi::FileSystemOperationRunner::StatusCallback& callback) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
BrowserThread::PostTask(
BrowserThread::IO, FROM_HERE,
base::Bind(&CheckIfDirectoryExistsOnIOThread,
file_system_context,
url,
google_apis::CreateRelayCallback(callback)));
}
} // namespace
void OpenRemovableDrive(const base::FilePath& file_path) {
OpenFileManagerWithInternalActionId(file_path, "auto-open");
}
void OpenItem(const base::FilePath& file_path) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
Profile* profile = ProfileManager::GetDefaultProfileOrOffTheRecord();
GURL url;
if (!ConvertAbsoluteFilePathToFileSystemUrl(
profile, file_path, kFileManagerAppId, &url) ||
!GrantFileSystemAccessToFileBrowser(profile)) {
ShowWarningMessageBox(profile, file_path);
return;
}
scoped_refptr<fileapi::FileSystemContext> file_system_context =
GetFileSystemContextForExtensionId(
profile, kFileManagerAppId);
CheckIfDirectoryExists(file_system_context, url,
base::Bind(&ContinueOpenItem, profile, file_path));
}
void ShowItemInFolder(const base::FilePath& file_path) {
// This action changes the selection so we do not reuse existing tabs.
OpenFileManagerWithInternalActionId(file_path, "select");
}
} // namespace util
} // namespace file_manager