Commit d11a5c2a authored by Timothy Loh's avatar Timothy Loh Committed by Commit Bot

Support launching Crostini apps from the File Manager

This CL adds support for launching Crostini apps from the File Manager.
For the moment, we only support files which are already in the Crostini
container, but in the future we will support external files as well.

Support for display app icons will be added in a separate patch, but as
it will require FindCrostiniTasks to be asynchronous, it is made to take
a callback to run upon completion.

Bug: 822513
Change-Id: I257b482991ab8cf2e4d0ef5be18d0e33205df4e3
Reviewed-on: https://chromium-review.googlesource.com/1100606
Commit-Queue: Timothy Loh <timloh@chromium.org>
Reviewed-by: default avatarJoel Hockey <joelhockey@chromium.org>
Reviewed-by: default avatarNicholas Verne <nverne@chromium.org>
Cr-Commit-Position: refs/heads/master@{#569171}
parent d35b92b9
......@@ -640,6 +640,8 @@ source_set("chromeos") {
"file_manager/app_id.h",
"file_manager/arc_file_tasks.cc",
"file_manager/arc_file_tasks.h",
"file_manager/crostini_file_tasks.cc",
"file_manager/crostini_file_tasks.h",
"file_manager/file_browser_handlers.cc",
"file_manager/file_browser_handlers.h",
"file_manager/file_tasks.cc",
......
......@@ -706,12 +706,16 @@ void CrostiniManager::LaunchContainerApplication(
std::string vm_name,
std::string container_name,
std::string desktop_file_id,
const std::vector<std::string>& files,
LaunchContainerApplicationCallback callback) {
vm_tools::cicerone::LaunchContainerApplicationRequest request;
request.set_owner_id(CryptohomeIdForProfile(profile));
request.set_vm_name(std::move(vm_name));
request.set_container_name(std::move(container_name));
request.set_desktop_file_id(std::move(desktop_file_id));
std::copy(
files.begin(), files.end(),
google::protobuf::RepeatedFieldBackInserter(request.mutable_files()));
GetCiceroneClient()->LaunchContainerApplication(
std::move(request),
......
......@@ -197,6 +197,7 @@ class CrostiniManager : public chromeos::ConciergeClient::Observer,
std::string vm_name,
std::string container_name,
std::string desktop_file_id,
const std::vector<std::string>& files,
LaunchContainerApplicationCallback callback);
// Asynchronously gets app icons as specified by their desktop file ids.
......
......@@ -83,12 +83,12 @@ base::Value ProtoToDictionary(const App::LocaleString& locale_string) {
return result;
}
std::vector<std::string> ListToStringVector(const base::Value* list) {
std::vector<std::string> result;
std::set<std::string> ListToStringSet(const base::Value* list) {
std::set<std::string> result;
if (!list)
return result;
for (const base::Value& value : list->GetList())
result.emplace_back(value.GetString());
result.insert(value.GetString());
return result;
}
......@@ -254,11 +254,10 @@ std::string CrostiniRegistryService::Registration::Comment() const {
return LocalizedString(kAppCommentKey);
}
std::vector<std::string> CrostiniRegistryService::Registration::MimeTypes()
const {
std::set<std::string> CrostiniRegistryService::Registration::MimeTypes() const {
if (pref_.is_none())
return {};
return ListToStringVector(
return ListToStringSet(
pref_.FindKeyOfType(kAppMimeTypesKey, base::Value::Type::LIST));
}
......
......@@ -79,7 +79,7 @@ class CrostiniRegistryService : public KeyedService {
std::string Name() const;
std::string Comment() const;
std::vector<std::string> MimeTypes() const;
std::set<std::string> MimeTypes() const;
bool NoDisplay() const;
base::Time InstallTime() const;
......
......@@ -81,7 +81,7 @@ TEST_F(CrostiniRegistryServiceTest, SetAndGetRegistration) {
std::string container_name = "awesomecontainer";
std::map<std::string, std::string> name = {{"", "Vim"}};
std::map<std::string, std::string> comment = {{"", "Edit text files"}};
std::vector<std::string> mime_types = {"text/plain", "text/x-python"};
std::set<std::string> mime_types = {"text/plain", "text/x-python"};
bool no_display = true;
std::string app_id = CrostiniTestHelper::GenerateAppId(
......
......@@ -17,15 +17,23 @@ using vm_tools::apps::ApplicationList;
namespace crostini {
CrostiniTestHelper::CrostiniTestHelper(Profile* profile)
: registry_service_(
: profile_(profile),
registry_service_(
crostini::CrostiniRegistryServiceFactory::GetForProfile(profile)) {
SetCrostiniUIAllowedForTesting(true);
EnableCrostini(profile);
current_apps_.set_vm_name(kCrostiniDefaultVmName);
current_apps_.set_container_name(kCrostiniDefaultContainerName);
}
CrostiniTestHelper::~CrostiniTestHelper() {
DisableCrostini(profile_);
SetCrostiniUIAllowedForTesting(false);
}
void CrostiniTestHelper::SetupDummyApps() {
current_apps_ = BasicAppList("dummy1", kCrostiniDefaultVmName,
kCrostiniDefaultContainerName);
// This updates the registry for us.
AddApp(BasicApp("dummy1"));
AddApp(BasicApp("dummy2"));
}
......
......@@ -20,8 +20,10 @@ class CrostiniRegistryService;
// simple interface to add, update, and remove apps from the registry.
class CrostiniTestHelper {
public:
// For convenience, instantiating this enables Crostini.
// For convenience, instantiating this enables Crostini and also calls
// SetCrostiniUIAllowedForTesting(true). The destructor resets these.
explicit CrostiniTestHelper(Profile*);
~CrostiniTestHelper();
// Creates the apps named "dummy1" and "dummy2" in the default container.
void SetupDummyApps();
......@@ -56,6 +58,7 @@ class CrostiniTestHelper {
private:
void UpdateRegistry();
Profile* profile_;
vm_tools::apps::ApplicationList current_apps_;
CrostiniRegistryService* registry_service_;
};
......
......@@ -6,6 +6,7 @@
#include "base/callback.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_util.h"
#include "chrome/browser/chromeos/crostini/crostini_app_launch_observer.h"
......@@ -99,7 +100,8 @@ void LaunchContainerApplication(
Profile* profile,
const std::string& app_id,
crostini::CrostiniRegistryService::Registration registration,
int64_t display_id) {
int64_t display_id,
const std::vector<std::string>& files) {
ChromeLauncherController* chrome_launcher_controller =
ChromeLauncherController::instance();
DCHECK_NE(chrome_launcher_controller, nullptr);
......@@ -109,7 +111,7 @@ void LaunchContainerApplication(
observer->OnAppLaunchRequested(registration.DesktopFileId(), display_id);
crostini::CrostiniManager::GetInstance()->LaunchContainerApplication(
profile, registration.VmName(), registration.ContainerName(),
registration.DesktopFileId(),
registration.DesktopFileId(), files,
base::BindOnce(OnContainerApplicationLaunched, app_id));
}
......@@ -147,6 +149,13 @@ bool IsCrostiniEnabled(Profile* profile) {
void LaunchCrostiniApp(Profile* profile,
const std::string& app_id,
int64_t display_id) {
LaunchCrostiniApp(profile, app_id, display_id, std::vector<std::string>());
}
void LaunchCrostiniApp(Profile* profile,
const std::string& app_id,
int64_t display_id,
const std::vector<std::string>& files) {
auto* crostini_manager = crostini::CrostiniManager::GetInstance();
crostini::CrostiniRegistryService* registry_service =
crostini::CrostiniRegistryServiceFactory::GetForProfile(profile);
......@@ -165,6 +174,7 @@ void LaunchCrostiniApp(Profile* profile,
base::OnceClosure launch_closure;
Browser* browser = nullptr;
if (app_id == kCrostiniTerminalId) {
DCHECK(files.empty());
RecordAppLaunchHistogram(CrostiniAppLaunchAppType::kTerminal);
if (!crostini_manager->IsCrosTerminaInstalled() ||
......@@ -187,7 +197,7 @@ void LaunchCrostiniApp(Profile* profile,
RecordAppLaunchHistogram(CrostiniAppLaunchAppType::kRegisteredApp);
launch_closure =
base::BindOnce(&LaunchContainerApplication, profile, app_id,
std::move(*registration), display_id);
std::move(*registration), display_id, std::move(files));
}
// Update the last launched time.
......@@ -224,6 +234,10 @@ std::string ContainerUserNameForProfile(Profile* profile) {
return container_username;
}
base::FilePath HomeDirectoryForProfile(Profile* profile) {
return base::FilePath("/home/" + ContainerUserNameForProfile(profile));
}
std::string AppNameFromCrostiniAppId(const std::string& id) {
return kCrostiniAppNamePrefix + id;
}
......
......@@ -9,6 +9,10 @@
#include "base/optional.h"
namespace base {
class FilePath;
} // namespace base
class Profile;
// Enables/disables overriding IsCrostiniUIAllowedForProfile's normal
......@@ -33,6 +37,14 @@ void LaunchCrostiniApp(Profile* profile,
const std::string& app_id,
int64_t display_id);
// Launch a Crostini App with a given set of files, given as absolute paths in
// the container. For apps which can only be launched with a single file,
// launch multiple instances.
void LaunchCrostiniApp(Profile* profile,
const std::string& app_id,
int64_t display_id,
const std::vector<std::string>& files);
// Retrieves cryptohome_id from profile.
std::string CryptohomeIdForProfile(Profile* profile);
......@@ -40,6 +52,9 @@ std::string CryptohomeIdForProfile(Profile* profile);
// profile->GetProfileUserName() email address.
std::string ContainerUserNameForProfile(Profile* profile);
// Returns the home directory within the container for a given profile.
base::FilePath HomeDirectoryForProfile(Profile* profile);
// The Terminal opens Crosh but overrides the Browser's app_name so that we can
// identify it as the Crostini Terminal. In the future, we will also use these
// for Crostini apps marked Terminal=true in their .desktop file.
......
// Copyright (c) 2018 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/crostini_file_tasks.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/base64.h"
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/chromeos/crostini/crostini_registry_service.h"
#include "chrome/browser/chromeos/crostini/crostini_registry_service_factory.h"
#include "chrome/browser/chromeos/crostini/crostini_util.h"
#include "chrome/browser/chromeos/file_manager/app_id.h"
#include "chrome/browser/chromeos/file_manager/path_util.h"
#include "extensions/browser/entry_info.h"
#include "storage/browser/fileapi/file_system_url.h"
#include "ui/base/layout.h"
#include "ui/display/types/display_constants.h"
#include "ui/gfx/codec/png_codec.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/gfx/image/image_skia_operations.h"
#include "url/gurl.h"
namespace file_manager {
namespace file_tasks {
void FindCrostiniTasks(Profile* profile,
const std::vector<extensions::EntryInfo>& entries,
std::vector<FullTaskDescriptor>* result_list,
base::OnceClosure completion_closure) {
if (!IsCrostiniUIAllowedForProfile(profile)) {
std::move(completion_closure).Run();
return;
}
// We currently don't support opening directories or files not already inside
// the Crostini directories.
base::FilePath crostini_mount = util::GetCrostiniMountDirectory(profile);
for (const extensions::EntryInfo& entry : entries) {
if (!crostini_mount.IsParent(entry.path) ||
entry.path.EndsWithSeparator()) {
std::move(completion_closure).Run();
return;
}
}
std::set<std::string> target_mime_types;
for (const extensions::EntryInfo& entry : entries)
target_mime_types.insert(entry.mime_type);
crostini::CrostiniRegistryService* registry_service =
crostini::CrostiniRegistryServiceFactory::GetForProfile(profile);
for (const std::string& app_id : registry_service->GetRegisteredAppIds()) {
crostini::CrostiniRegistryService::Registration registration =
*registry_service->GetRegistration(app_id);
const std::set<std::string>& supported_mime_types =
registration.MimeTypes();
bool had_unsupported_mime_type = false;
for (const std::string& target_mime_type : target_mime_types) {
if (supported_mime_types.find(target_mime_type) !=
supported_mime_types.end())
continue;
had_unsupported_mime_type = true;
break;
}
if (had_unsupported_mime_type)
continue;
// TODO(timloh): Add support for Crostini icons
result_list->push_back(FullTaskDescriptor(
TaskDescriptor(app_id, TASK_TYPE_CROSTINI_APP, kCrostiniAppActionID),
registration.Name(),
extensions::api::file_manager_private::Verb::VERB_OPEN_WITH, GURL(),
false /* is_default */, false /* is_generic */));
}
std::move(completion_closure).Run();
}
void ExecuteCrostiniTask(
Profile* profile,
const TaskDescriptor& task,
const std::vector<storage::FileSystemURL>& file_system_urls,
const FileTaskFinishedCallback& done) {
DCHECK(IsCrostiniUIAllowedForProfile(profile));
base::FilePath folder(util::GetCrostiniMountPointName(profile));
std::vector<std::string> files;
for (const storage::FileSystemURL& file_system_url : file_system_urls) {
DCHECK(file_system_url.mount_type() == storage::kFileSystemTypeExternal);
DCHECK(file_system_url.type() == storage::kFileSystemTypeNativeLocal);
// Reformat virtual_path()
// from <mount_label>/path/to/file
// to /<home-directory>/path/to/file
base::FilePath result = HomeDirectoryForProfile(profile);
bool success =
folder.AppendRelativePath(file_system_url.virtual_path(), &result);
DCHECK(success);
files.emplace_back(result.AsUTF8Unsafe());
}
LaunchCrostiniApp(profile, task.app_id, display::kInvalidDisplayId, files);
}
} // namespace file_tasks
} // namespace file_manager
// Copyright (c) 2018 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.
#ifndef CHROME_BROWSER_CHROMEOS_FILE_MANAGER_CROSTINI_FILE_TASKS_H_
#define CHROME_BROWSER_CHROMEOS_FILE_MANAGER_CROSTINI_FILE_TASKS_H_
#include <memory>
#include <string>
#include <vector>
#include "chrome/browser/chromeos/file_manager/file_tasks.h"
class Profile;
namespace extensions {
struct EntryInfo;
}
namespace storage {
class FileSystemURL;
}
namespace file_manager {
namespace file_tasks {
// Crostini apps all use the same action ID.
constexpr char kCrostiniAppActionID[] = "open-with";
// Finds the Crostini tasks that can handle |entries|, appends them to
// |result_list|, and calls back to |callback| once finished.
void FindCrostiniTasks(Profile* profile,
const std::vector<extensions::EntryInfo>& entries,
std::vector<FullTaskDescriptor>* result_list,
base::OnceClosure completion_closure);
// Executes the specified task by Crostini.
void ExecuteCrostiniTask(
Profile* profile,
const TaskDescriptor& task,
const std::vector<storage::FileSystemURL>& file_system_urls,
const FileTaskFinishedCallback& done);
} // namespace file_tasks
} // namespace file_manager
#endif // CHROME_BROWSER_CHROMEOS_FILE_MANAGER_CROSTINI_FILE_TASKS_H_
......@@ -14,10 +14,12 @@
#include "base/metrics/histogram_macros.h"
#include "base/strings/string_split.h"
#include "base/strings/stringprintf.h"
#include "chrome/browser/chromeos/crostini/crostini_util.h"
#include "chrome/browser/chromeos/drive/file_system_util.h"
#include "chrome/browser/chromeos/drive/file_task_executor.h"
#include "chrome/browser/chromeos/file_manager/app_id.h"
#include "chrome/browser/chromeos/file_manager/arc_file_tasks.h"
#include "chrome/browser/chromeos/file_manager/crostini_file_tasks.h"
#include "chrome/browser/chromeos/file_manager/file_browser_handlers.h"
#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
#include "chrome/browser/chromeos/file_manager/open_util.h"
......@@ -61,6 +63,7 @@ const char kFileBrowserHandlerTaskType[] = "file";
const char kFileHandlerTaskType[] = "app";
const char kDriveAppTaskType[] = "drive";
const char kArcAppTaskType[] = "arc";
const char kCrostiniAppTaskType[] = "crostini";
// Drive apps always use the action ID.
const char kDriveAppActionID[] = "open-with";
......@@ -76,6 +79,8 @@ std::string TaskTypeToString(TaskType task_type) {
return kDriveAppTaskType;
case TASK_TYPE_ARC_APP:
return kArcAppTaskType;
case TASK_TYPE_CROSTINI_APP:
return kCrostiniAppTaskType;
case TASK_TYPE_UNKNOWN:
case NUM_TASK_TYPE:
break;
......@@ -94,6 +99,8 @@ TaskType StringToTaskType(const std::string& str) {
return TASK_TYPE_DRIVE_APP;
if (str == kArcAppTaskType)
return TASK_TYPE_ARC_APP;
if (str == kCrostiniAppTaskType)
return TASK_TYPE_CROSTINI_APP;
return TASK_TYPE_UNKNOWN;
}
......@@ -336,6 +343,12 @@ bool ExecuteFileTask(Profile* profile,
return true;
}
if (task.task_type == TASK_TYPE_CROSTINI_APP) {
DCHECK_EQ(kCrostiniAppActionID, task.action_id);
ExecuteCrostiniTask(profile, task, file_urls, done);
return true;
}
// drive::FileTaskExecutor is responsible to handle drive tasks.
if (task.task_type == TASK_TYPE_DRIVE_APP) {
DCHECK_EQ(kDriveAppActionID, task.action_id);
......@@ -597,16 +610,21 @@ void FindExtensionAndAppTasks(
const std::vector<GURL>& file_urls,
const FindTasksCallback& callback,
std::unique_ptr<std::vector<FullTaskDescriptor>> result_list) {
std::vector<FullTaskDescriptor>* result_list_ptr = result_list.get();
// 3. Continues from FindAllTypesOfTasks. Find and append file handler tasks.
FindFileHandlerTasks(profile, entries, result_list.get());
FindFileHandlerTasks(profile, entries, result_list_ptr);
// 4. Find and append file browser handler tasks. We know there aren't
// duplicates because "file_browser_handlers" and "file_handlers" shouldn't
// be used in the same manifest.json.
FindFileBrowserHandlerTasks(profile, file_urls, result_list.get());
FindFileBrowserHandlerTasks(profile, file_urls, result_list_ptr);
// Done. Apply post-filtering and callback.
PostProcessFoundTasks(profile, entries, callback, std::move(result_list));
// 5. Find and append Crostini tasks.
FindCrostiniTasks(profile, entries, result_list_ptr,
// Done. Apply post-filtering and callback.
base::BindOnce(PostProcessFoundTasks, profile, entries,
callback, std::move(result_list)));
}
void FindAllTypesOfTasks(Profile* profile,
......
......@@ -46,7 +46,8 @@
// },
// {
// "driveApp": false,
// "iconUrl": "chrome://extension-icon/hhaomjibdihmijegdhdafkllkbggdgoj/16/1",
// "iconUrl":
// "chrome://extension-icon/hhaomjibdihmijegdhdafkllkbggdgoj/16/1",
// "isDefault": true,
// "taskId": "hhaomjibdihmijegdhdafkllkbggdgoj|file|gallery",
// "title": "__MSG_OPEN_ACTION__"
......@@ -85,12 +86,13 @@
// - "app" - File handler - app declaring "file_handlers" in manifest.json.
// - "drive" - Drive App
// - "arc" - ARC App
// - "crostini" - Crostini App
//
// <task-action-id> is an ID string used for identifying actions provided
// from a single Chrome Extension/App. In other words, a single
// Chrome/Extension can provide multiple file handlers hence each of them
// needs to have a unique action ID. For Drive apps, <task-action-id> is
// always "open-with".
// needs to have a unique action ID. For Drive and Crostini apps,
// <task-action-id> is always "open-with".
//
// HOW TASKS ARE EXECUTED?
//
......@@ -146,6 +148,7 @@ enum TaskType {
TASK_TYPE_FILE_HANDLER,
TASK_TYPE_DRIVE_APP,
TASK_TYPE_ARC_APP,
TASK_TYPE_CROSTINI_APP,
// The enum values must be kept in sync with FileManagerTaskType in
// tools/metrics/histograms/enums.xml. Since enums for histograms are
// append-only (for keeping the number consistent across versions), new values
......
......@@ -12,8 +12,10 @@
#include "base/command_line.h"
#include "base/run_loop.h"
#include "base/values.h"
#include "chrome/browser/chromeos/crostini/crostini_test_helper.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/path_util.h"
#include "chrome/browser/chromeos/login/users/scoped_test_user_manager.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "chrome/browser/chromeos/settings/device_settings_service.h"
......@@ -21,6 +23,8 @@
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/testing_profile.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/fake_concierge_client.h"
#include "components/drive/drive_app_registry.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/testing_pref_service.h"
......@@ -1247,5 +1251,122 @@ TEST_F(FileManagerFileTasksComplexTest, FindFileHandlerTask_Verbs) {
EXPECT_EQ(Verb::VERB_PACK_WITH, tasks[0].task_verb());
}
// Test using the test extension system, which needs lots of setup.
class FileManagerFileTasksCrostiniTest
: public FileManagerFileTasksComplexTest {
protected:
FileManagerFileTasksCrostiniTest()
: crostini_test_helper_(&test_profile_),
crostini_folder_(util::GetCrostiniMountDirectory(&test_profile_)) {
chromeos::DBusThreadManager::GetSetterForTesting()->SetConciergeClient(
std::make_unique<chromeos::FakeConciergeClient>());
vm_tools::apps::App text_app =
crostini::CrostiniTestHelper::BasicApp("text_app");
*text_app.add_mime_types() = "text/plain";
crostini_test_helper_.AddApp(text_app);
vm_tools::apps::App image_app =
crostini::CrostiniTestHelper::BasicApp("image_app");
*image_app.add_mime_types() = "image/gif";
*image_app.add_mime_types() = "image/jpeg";
*image_app.add_mime_types() = "image/jpg";
*image_app.add_mime_types() = "image/png";
crostini_test_helper_.AddApp(image_app);
vm_tools::apps::App gif_app =
crostini::CrostiniTestHelper::BasicApp("gif_app");
*gif_app.add_mime_types() = "image/gif";
crostini_test_helper_.AddApp(gif_app);
text_app_id_ = crostini::CrostiniTestHelper::GenerateAppId("text_app");
image_app_id_ = crostini::CrostiniTestHelper::GenerateAppId("image_app");
gif_app_id_ = crostini::CrostiniTestHelper::GenerateAppId("gif_app");
}
crostini::CrostiniTestHelper crostini_test_helper_;
base::FilePath crostini_folder_;
std::string text_app_id_;
std::string image_app_id_;
std::string gif_app_id_;
};
TEST_F(FileManagerFileTasksCrostiniTest, BasicFiles) {
std::vector<extensions::EntryInfo> entries{
{crostini_folder_.Append("foo.txt"), "text/plain", false}};
std::vector<GURL> file_urls{
GURL("filesystem:chrome-extension://id/dir/foo.txt")};
std::vector<FullTaskDescriptor> tasks;
FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
file_urls, &tasks);
ASSERT_EQ(1U, tasks.size());
EXPECT_EQ(text_app_id_, tasks[0].task_descriptor().app_id);
// Multiple text files
entries.emplace_back(crostini_folder_.Append("bar.txt"), "text/plain", false);
file_urls.emplace_back("filesystem:chrome-extension://id/dir/bar.txt");
FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
file_urls, &tasks);
ASSERT_EQ(1U, tasks.size());
EXPECT_EQ(text_app_id_, tasks[0].task_descriptor().app_id);
}
TEST_F(FileManagerFileTasksCrostiniTest, Directories) {
std::vector<extensions::EntryInfo> entries{
{crostini_folder_.Append("dir"), "", true}};
std::vector<GURL> file_urls{GURL("filesystem:chrome-extension://id/dir/dir")};
std::vector<FullTaskDescriptor> tasks;
FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
file_urls, &tasks);
EXPECT_EQ(0U, tasks.size());
entries.emplace_back(crostini_folder_.Append("foo.txt"), "text/plain", false);
file_urls.emplace_back("filesystem:chrome-extension://id/dir/foo.txt");
FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
file_urls, &tasks);
EXPECT_EQ(0U, tasks.size());
}
TEST_F(FileManagerFileTasksCrostiniTest, MultipleMatches) {
std::vector<extensions::EntryInfo> entries{
{crostini_folder_.Append("foo.gif"), "image/gif", false},
{crostini_folder_.Append("bar.gif"), "image/gif", false}};
std::vector<GURL> file_urls{
GURL("filesystem:chrome-extension://id/dir/foo.gif"),
GURL("filesystem:chrome-extension://id/dir/bar.gif")};
std::vector<FullTaskDescriptor> tasks;
FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
file_urls, &tasks);
// The returned values happen to be ordered alphabetically by app_id, so we
// rely on this to keep the test simple.
EXPECT_LT(gif_app_id_, image_app_id_);
ASSERT_EQ(2U, tasks.size());
EXPECT_EQ(gif_app_id_, tasks[0].task_descriptor().app_id);
EXPECT_EQ(image_app_id_, tasks[1].task_descriptor().app_id);
}
TEST_F(FileManagerFileTasksCrostiniTest, MultipleTypes) {
std::vector<extensions::EntryInfo> entries{
{crostini_folder_.Append("foo.gif"), "image/gif", false},
{crostini_folder_.Append("bar.png"), "image/png", false}};
std::vector<GURL> file_urls{
GURL("filesystem:chrome-extension://id/dir/foo.gif"),
GURL("filesystem:chrome-extension://id/dir/bar.png")};
std::vector<FullTaskDescriptor> tasks;
FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
file_urls, &tasks);
ASSERT_EQ(1U, tasks.size());
EXPECT_EQ(image_app_id_, tasks[0].task_descriptor().app_id);
entries.emplace_back(crostini_folder_.Append("qux.mp4"), "video/mp4", false);
file_urls.emplace_back("filesystem:chrome-extension://id/dir/qux.mp4");
FindAllTypesOfTasksSynchronousWrapper().Call(&test_profile_, nullptr, entries,
file_urls, &tasks);
EXPECT_EQ(0U, tasks.size());
}
} // namespace file_tasks
} // namespace file_manager.
......@@ -123,6 +123,10 @@ std::string GetCrostiniMountPointName(Profile* profile) {
"_");
}
base::FilePath GetCrostiniMountDirectory(Profile* profile) {
return base::FilePath("/media/fuse/" + GetCrostiniMountPointName(profile));
}
bool ConvertPathToArcUrl(const base::FilePath& path, GURL* arc_url_out) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
......
......@@ -46,6 +46,9 @@ std::string GetDownloadsMountPointName(Profile* profile);
// The canonical mount point name for crostini "Linux Files" folder.
std::string GetCrostiniMountPointName(Profile* profile);
// The actual directory the crostini "Linux Files" folder is mounted.
base::FilePath GetCrostiniMountDirectory(Profile* profile);
// DEPRECATED. Use |ConvertToContentUrls| instead.
// While this function can convert paths under Downloads, /media/removable
// and /special/drive, this CANNOT convert paths under ARC media directories
......
......@@ -19957,6 +19957,7 @@ Called by update_net_error_codes.py.-->
<int value="2" label="Chrome App"/>
<int value="3" label="Drive App"/>
<int value="4" label="ARC App"/>
<int value="5" label="Crostini App"/>
</enum>
<enum name="FileManagerVolumeType">
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment