Commit 54e85384 authored by Joel Hockey's avatar Joel Hockey Committed by Commit Bot

Support Arc, crostini, and Plugin VM in drag drop FileHelper

The current code in FileHelper assumes all windows are Arc. We now
check the source and target aura::Window to detect and do the
appropriate path translations and share files when necessary with
crostini/pluginvm.

For example, when crostini is the drag source, we will convert paths
such as:

 <homedir>/file => /media/fuse/crostini_<hash>_termina_penguin/file

And do the opposite translation when crostini is the drop target.

We also convert paths and share files such as:

 <cryptohome>/MyFiles/file => /mnt/chromeos/MyFiles/file
 /media/removable/MyUSB => /mnt/chromeos/removable/MyUSB

For PluginVM, the translation is

 <cryptohome>/MyFiles/file => //ChromeOS/MyFiles/file
 /media/removable/MyUSB => //ChromeOS/removable/MyUSB

Bug: 1144138
Change-Id: I2bb38336537acf13c4797374d6dabe6e518d689e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2517346
Commit-Queue: Joel Hockey <joelhockey@chromium.org>
Reviewed-by: default avatarJason Lin <lxj@google.com>
Reviewed-by: default avatarMitsuru Oshima <oshima@chromium.org>
Cr-Commit-Position: refs/heads/master@{#827956}
parent 79e7d157
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include <utility> #include <utility>
#include "ash/public/cpp/app_types.h"
#include "base/bind.h" #include "base/bind.h"
#include "base/callback.h" #include "base/callback.h"
#include "base/callback_helpers.h" #include "base/callback_helpers.h"
...@@ -44,6 +45,7 @@ ...@@ -44,6 +45,7 @@
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "components/user_manager/user.h" #include "components/user_manager/user.h"
#include "google_apis/gaia/gaia_auth_util.h" #include "google_apis/gaia/gaia_auth_util.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/base/l10n/l10n_util.h" #include "ui/base/l10n/l10n_util.h"
#include "ui/base/l10n/time_format.h" #include "ui/base/l10n/time_format.h"
...@@ -574,4 +576,10 @@ const ContainerId& DefaultContainerId() { ...@@ -574,4 +576,10 @@ const ContainerId& DefaultContainerId() {
kCrostiniDefaultVmName, kCrostiniDefaultContainerName); kCrostiniDefaultVmName, kCrostiniDefaultContainerName);
return *container_id; return *container_id;
} }
bool IsCrostiniWindow(aura::Window* window) {
return window->GetProperty(aura::client::kAppType) ==
static_cast<int>(ash::AppType::CROSTINI_APP);
}
} // namespace crostini } // namespace crostini
...@@ -18,6 +18,10 @@ ...@@ -18,6 +18,10 @@
#include "storage/browser/file_system/file_system_url.h" #include "storage/browser/file_system/file_system_url.h"
#include "third_party/abseil-cpp/absl/types/variant.h" #include "third_party/abseil-cpp/absl/types/variant.h"
namespace aura {
class Window;
} // namespace aura
namespace base { namespace base {
class FilePath; class FilePath;
} // namespace base } // namespace base
...@@ -205,6 +209,8 @@ void UpdateContainerPref(Profile* profile, ...@@ -205,6 +209,8 @@ void UpdateContainerPref(Profile* profile,
const ContainerId& DefaultContainerId(); const ContainerId& DefaultContainerId();
bool IsCrostiniWindow(aura::Window* window);
} // namespace crostini } // namespace crostini
#endif // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_UTIL_H_ #endif // CHROME_BROWSER_CHROMEOS_CROSTINI_CROSTINI_UTIL_H_
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include "ash/public/cpp/app_types.h"
#include "ash/public/cpp/ash_switches.h" #include "ash/public/cpp/ash_switches.h"
#include "ash/public/cpp/external_arc/keyboard/arc_input_method_surface_manager.h" #include "ash/public/cpp/external_arc/keyboard/arc_input_method_surface_manager.h"
#include "ash/public/cpp/external_arc/message_center/arc_notification_surface_manager_impl.h" #include "ash/public/cpp/external_arc/message_center/arc_notification_surface_manager_impl.h"
...@@ -21,41 +22,42 @@ ...@@ -21,41 +22,42 @@
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "chrome/browser/apps/app_service/app_service_proxy.h"
#include "chrome/browser/apps/app_service/app_service_proxy_factory.h"
#include "chrome/browser/chromeos/crostini/crostini_shelf_utils.h" #include "chrome/browser/chromeos/crostini/crostini_shelf_utils.h"
#include "chrome/browser/chromeos/crostini/crostini_util.h" #include "chrome/browser/chromeos/crostini/crostini_util.h"
#include "chrome/browser/chromeos/file_manager/app_id.h" #include "chrome/browser/chromeos/file_manager/app_id.h"
#include "chrome/browser/chromeos/file_manager/fileapi_util.h" #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
#include "chrome/browser/chromeos/file_manager/path_util.h" #include "chrome/browser/chromeos/file_manager/path_util.h"
#include "chrome/browser/chromeos/guest_os/guest_os_share_path.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_files.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_switches.h"
#include "components/exo/file_helper.h" #include "components/exo/file_helper.h"
#include "components/exo/server/wayland_server_controller.h" #include "components/exo/server/wayland_server_controller.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
#include "content/public/common/drop_data.h" #include "content/public/common/drop_data.h"
#include "net/base/filename_util.h" #include "net/base/filename_util.h"
#include "storage/browser/file_system/external_mount_points.h"
#include "storage/browser/file_system/file_system_context.h" #include "storage/browser/file_system/file_system_context.h"
#include "storage/browser/file_system/file_system_url.h" #include "storage/browser/file_system/file_system_url.h"
#include "storage/common/file_system/file_system_types.h" #include "storage/common/file_system/file_system_types.h"
#include "ui/aura/client/aura_constants.h"
#include "ui/base/dragdrop/file_info/file_info.h" #include "ui/base/dragdrop/file_info/file_info.h"
namespace { namespace {
constexpr char kMimeTypeArcUriList[] = "application/x-arc-uri-list"; constexpr char kMimeTypeArcUriList[] = "application/x-arc-uri-list";
constexpr char kMimeTypeTextUriList[] = "text/uri-list";
constexpr char kUriListSeparator[] = "\r\n"; constexpr char kUriListSeparator[] = "\r\n";
bool IsArcWindow(const aura::Window* window) {
return static_cast<ash::AppType>(window->GetProperty(
aura::client::kAppType)) == ash::AppType::ARC_APP;
}
storage::FileSystemContext* GetFileSystemContext() { storage::FileSystemContext* GetFileSystemContext() {
// Obtains the primary profile. Profile* primary_profile = ProfileManager::GetPrimaryUserProfile();
if (!user_manager::UserManager::IsInitialized())
return nullptr;
const user_manager::User* primary_user =
user_manager::UserManager::Get()->GetPrimaryUser();
if (!primary_user)
return nullptr;
Profile* primary_profile =
chromeos::ProfileHelper::Get()->GetProfileByUser(primary_user);
if (!primary_profile) if (!primary_profile)
return nullptr; return nullptr;
...@@ -97,6 +99,72 @@ void SendArcUrls(exo::FileHelper::SendDataCallback callback, ...@@ -97,6 +99,72 @@ void SendArcUrls(exo::FileHelper::SendDataCallback callback,
std::move(callback).Run(base::RefCountedString16::TakeString(&data)); std::move(callback).Run(base::RefCountedString16::TakeString(&data));
} }
void SendAfterShare(exo::FileHelper::SendDataCallback callback,
scoped_refptr<base::RefCountedMemory> data,
bool success,
const std::string& failure_reason) {
if (!success)
LOG(ERROR) << "Error sharing paths for drag and drop: " << failure_reason;
// Still send the data, even if sharing failed.
std::move(callback).Run(data);
}
struct FileInfo {
base::FilePath path;
storage::FileSystemURL url;
};
void ShareAndSend(aura::Window* target,
std::vector<FileInfo> files,
exo::FileHelper::SendDataCallback callback) {
Profile* primary_profile = ProfileManager::GetPrimaryUserProfile();
aura::Window* toplevel = target->GetToplevelWindow();
bool is_crostini = crostini::IsCrostiniWindow(toplevel);
bool is_plugin_vm = plugin_vm::IsPluginVmAppWindow(toplevel);
base::FilePath vm_mount;
std::string vm_name;
if (is_crostini) {
vm_mount = crostini::ContainerChromeOSBaseDirectory();
vm_name = crostini::kCrostiniDefaultVmName;
} else if (is_plugin_vm) {
vm_mount = plugin_vm::ChromeOSBaseDirectory();
vm_name = plugin_vm::kPluginVmName;
}
std::vector<std::string> lines_to_send;
auto* share_path = guest_os::GuestOsSharePath::GetForProfile(primary_profile);
std::vector<base::FilePath> paths_to_share;
for (auto& info : files) {
// Crostini and PluginVm converts to path inside VM. For other app types, or
// if we fail to convert, we just keep the original path.
base::FilePath path_to_send = info.path;
if ((is_crostini || is_plugin_vm) &&
file_manager::util::ConvertFileSystemURLToPathInsideVM(
primary_profile, info.url, vm_mount,
/*map_crostini_home=*/is_crostini, &path_to_send) &&
!share_path->IsPathShared(vm_name, info.path)) {
// Keep a record of any paths which are not yet shared.
paths_to_share.emplace_back(info.path);
}
lines_to_send.emplace_back(net::FilePathToFileURL(path_to_send).spec());
}
std::string joined = base::JoinString(lines_to_send, kUriListSeparator);
auto data = base::RefCountedString::TakeString(&joined);
if (!paths_to_share.empty()) {
share_path->SharePaths(
vm_name, std::move(paths_to_share),
/*persist=*/false,
base::BindOnce(&SendAfterShare, std::move(callback), std::move(data)));
} else {
std::move(callback).Run(std::move(data));
}
}
class ChromeFileHelper : public exo::FileHelper { class ChromeFileHelper : public exo::FileHelper {
public: public:
ChromeFileHelper() = default; ChromeFileHelper() = default;
...@@ -106,10 +174,10 @@ class ChromeFileHelper : public exo::FileHelper { ...@@ -106,10 +174,10 @@ class ChromeFileHelper : public exo::FileHelper {
std::vector<ui::FileInfo> GetFilenames( std::vector<ui::FileInfo> GetFilenames(
aura::Window* source, aura::Window* source,
const std::vector<uint8_t>& data) const override { const std::vector<uint8_t>& data) const override {
// TODO(crbug.com/1144138): We must translate the path if this was received Profile* primary_profile = ProfileManager::GetPrimaryUserProfile();
// from a VM. E.g. if this was from crostini as aura::Window* toplevel = source->GetToplevelWindow();
// file:///home/username/file.txt, we translate to bool is_crostini = crostini::IsCrostiniWindow(toplevel);
// file:///media/fuse/crostini_<hash>_termina_penguin/file.txt. bool is_plugin_vm = plugin_vm::IsPluginVmAppWindow(toplevel);
std::string lines(data.begin(), data.end()); std::string lines(data.begin(), data.end());
std::vector<ui::FileInfo> filenames; std::vector<ui::FileInfo> filenames;
base::FilePath path; base::FilePath path;
...@@ -118,19 +186,33 @@ class ChromeFileHelper : public exo::FileHelper { ...@@ -118,19 +186,33 @@ class ChromeFileHelper : public exo::FileHelper {
lines, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) { lines, "\n", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
if (!net::FileURLToFilePath(GURL(line), &path)) if (!net::FileURLToFilePath(GURL(line), &path))
continue; continue;
if (is_crostini &&
file_manager::util::ConvertPathInsideVMToFileSystemURL(
primary_profile, path, crostini::ContainerChromeOSBaseDirectory(),
/*map_crostini_home=*/true, &url)) {
path = url.path();
} else if (is_plugin_vm &&
file_manager::util::ConvertPathInsideVMToFileSystemURL(
primary_profile, path, plugin_vm::ChromeOSBaseDirectory(),
/*map_crostini_home=*/false, &url)) {
path = url.path();
}
filenames.emplace_back(ui::FileInfo(path, base::FilePath())); filenames.emplace_back(ui::FileInfo(path, base::FilePath()));
} }
return filenames; return filenames;
} }
std::string GetMimeTypeForUriList(aura::Window* target) const override { std::string GetMimeTypeForUriList(aura::Window* target) const override {
return kMimeTypeArcUriList; return IsArcWindow(target->GetToplevelWindow()) ? kMimeTypeArcUriList
: kMimeTypeTextUriList;
} }
void SendFileInfo(aura::Window* target, void SendFileInfo(aura::Window* target,
const std::vector<ui::FileInfo>& files, const std::vector<ui::FileInfo>& files,
exo::FileHelper::SendDataCallback callback) const override { exo::FileHelper::SendDataCallback callback) const override {
// TODO(crbug.com/1144138): Translate path and possibly share files with VM. // ARC converts to ArcUrl and uses utf-16.
if (IsArcWindow(target->GetToplevelWindow())) {
std::vector<std::string> lines; std::vector<std::string> lines;
GURL url; GURL url;
for (const auto& info : files) { for (const auto& info : files) {
...@@ -141,6 +223,25 @@ class ChromeFileHelper : public exo::FileHelper { ...@@ -141,6 +223,25 @@ class ChromeFileHelper : public exo::FileHelper {
base::string16 data = base::string16 data =
base::UTF8ToUTF16(base::JoinString(lines, kUriListSeparator)); base::UTF8ToUTF16(base::JoinString(lines, kUriListSeparator));
std::move(callback).Run(base::RefCountedString16::TakeString(&data)); std::move(callback).Run(base::RefCountedString16::TakeString(&data));
return;
}
storage::ExternalMountPoints* mount_points =
storage::ExternalMountPoints::GetSystemInstance();
base::FilePath virtual_path;
std::vector<FileInfo> list;
for (const auto& info : files) {
// Convert absolute host path to FileSystemURL if possible.
storage::FileSystemURL url;
if (mount_points->GetVirtualPath(info.path, &virtual_path)) {
url = mount_points->CreateCrackedFileSystemURL(
url::Origin(), storage::kFileSystemTypeExternal, virtual_path);
}
list.push_back({info.path, std::move(url)});
}
ShareAndSend(target, std::move(list), std::move(callback));
} }
bool HasUrlsInPickle(const base::Pickle& pickle) const override { bool HasUrlsInPickle(const base::Pickle& pickle) const override {
...@@ -152,15 +253,25 @@ class ChromeFileHelper : public exo::FileHelper { ...@@ -152,15 +253,25 @@ class ChromeFileHelper : public exo::FileHelper {
void SendPickle(aura::Window* target, void SendPickle(aura::Window* target,
const base::Pickle& pickle, const base::Pickle& pickle,
exo::FileHelper::SendDataCallback callback) override { exo::FileHelper::SendDataCallback callback) override {
// TODO(crbug.com/1144138): Translate path and possibly share files with VM.
std::vector<storage::FileSystemURL> file_system_urls; std::vector<storage::FileSystemURL> file_system_urls;
GetFileSystemUrlsFromPickle(pickle, &file_system_urls); GetFileSystemUrlsFromPickle(pickle, &file_system_urls);
// ARC FileSystemURLs are converted to Content URLs.
if (IsArcWindow(target->GetToplevelWindow())) {
if (file_system_urls.empty()) { if (file_system_urls.empty()) {
std::move(callback).Run(nullptr); std::move(callback).Run(nullptr);
return; return;
} }
file_manager::util::ConvertToContentUrls( file_manager::util::ConvertToContentUrls(
file_system_urls, base::BindOnce(&SendArcUrls, std::move(callback))); file_system_urls, base::BindOnce(&SendArcUrls, std::move(callback)));
return;
}
std::vector<FileInfo> list;
for (const auto& url : file_system_urls)
list.push_back({url.path(), url});
ShareAndSend(target, std::move(list), std::move(callback));
} }
}; };
......
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