Commit a3d6c9cd authored by Joel Hockey's avatar Joel Hockey Committed by Commit Bot

Show FilesApp with move dialog for PluginVM file drop

If we drop an unshared file into Plugin VM, we do not share it since
Plugin VM requires the whole directory to be shared for file dances.
Instead, we show the 'Move to Windows files' dialog in FilesApp.

ChromeFileHelper sends new CrostiniEvent
'drop_failed_plugin_vm_directory_not_shared' and FilesApp will
show the dialog if it has a drag in process.

Bug: 1144138
Change-Id: I647978390aa451af7fb23ce28640d3511c96c4b0
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2556742
Commit-Queue: Joel Hockey <joelhockey@chromium.org>
Reviewed-by: default avatarLuciano Pacheco <lucmult@chromium.org>
Reviewed-by: default avatarJason Lin <lxj@google.com>
Cr-Commit-Position: refs/heads/master@{#830880}
parent 2c034d66
......@@ -18,6 +18,8 @@
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/chromeos/crostini/crostini_util.h"
#include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
#include "chrome/browser/chromeos/extensions/file_manager/event_router_factory.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/path_util.h"
......@@ -216,13 +218,25 @@ void ShareAndSend(aura::Window* target,
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));
if (!is_plugin_vm) {
share_path->SharePaths(
vm_name, std::move(paths_to_share),
/*persist=*/false,
base::BindOnce(&SendAfterShare, std::move(callback),
std::move(data)));
return;
}
// Show FilesApp move-to-windows-files dialog when Plugin VM is not shared.
if (auto* event_router =
file_manager::EventRouterFactory::GetForProfile(primary_profile)) {
event_router->DropFailedPluginVmDirectoryNotShared();
}
std::string empty;
data = base::RefCountedString::TakeString(&empty);
}
std::move(callback).Run(std::move(data));
}
} // namespace
......
......@@ -212,6 +212,10 @@ TEST_F(ChromeFileHelperTest, SendFileInfoConvertPaths) {
ChromeFileHelper file_helper;
ui::FileInfo file1(myfiles_dir_.Append("file1"), base::FilePath());
ui::FileInfo file2(myfiles_dir_.Append("file2"), base::FilePath());
auto* guest_os_share_path =
guest_os::GuestOsSharePath::GetForProfile(profile());
guest_os_share_path->RegisterSharedPath(plugin_vm::kPluginVmName,
myfiles_dir_);
// Arc should convert path to UTF16 URL.
std::string data;
......@@ -286,7 +290,7 @@ TEST_F(ChromeFileHelperTest, SendFileInfoConvertPaths) {
EXPECT_EQ("", data);
}
TEST_F(ChromeFileHelperTest, SendFileInfoSharePaths) {
TEST_F(ChromeFileHelperTest, SendFileInfoSharePathsCrostini) {
ChromeFileHelper file_helper;
// A path which is already shared should not be shared again.
......@@ -313,6 +317,19 @@ TEST_F(ChromeFileHelperTest, SendFileInfoSharePaths) {
EXPECT_TRUE(fake_seneschal_client_->share_path_called());
}
TEST_F(ChromeFileHelperTest, SendFileInfoSharePathsPluginVm) {
ChromeFileHelper file_helper;
// Plugin VM should send empty data and not share path if not already shared.
ui::FileInfo file(myfiles_dir_.Append("file"), base::FilePath());
std::string data;
file_helper.SendFileInfo(plugin_vm_window_, {file},
base::BindOnce(&Capture, &data));
task_environment_.RunUntilIdle();
EXPECT_EQ("", data);
EXPECT_FALSE(fake_seneschal_client_->share_path_called());
}
TEST_F(ChromeFileHelperTest, HasUrlsInPickle) {
ChromeFileHelper file_helper;
......
......@@ -29,6 +29,7 @@
#include "chrome/browser/chromeos/file_manager/volume_manager.h"
#include "chrome/browser/chromeos/login/lock/screen_locker.h"
#include "chrome/browser/chromeos/login/ui/login_display_host.h"
#include "chrome/browser/chromeos/plugin_vm/plugin_vm_util.h"
#include "chrome/browser/extensions/api/file_system/chrome_file_system_delegate.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_util.h"
......@@ -953,6 +954,21 @@ void EventRouter::NotifyDriveConnectionStatusChanged() {
file_manager_private::OnDriveConnectionStatusChanged::Create());
}
void EventRouter::DropFailedPluginVmDirectoryNotShared() {
for (const auto& extension_id : GetEventListenerExtensionIds(
profile_, file_manager_private::OnCrostiniChanged::kEventName)) {
file_manager_private::CrostiniEvent event;
event.vm_name = plugin_vm::kPluginVmName;
event.event_type = file_manager_private::
CROSTINI_EVENT_TYPE_DROP_FAILED_PLUGIN_VM_DIRECTORY_NOT_SHARED;
DispatchEventToExtension(
profile_, extension_id,
extensions::events::FILE_MANAGER_PRIVATE_ON_CROSTINI_CHANGED,
file_manager_private::OnCrostiniChanged::kEventName,
file_manager_private::OnCrostiniChanged::Create(event));
}
}
base::WeakPtr<EventRouter> EventRouter::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}
......
......@@ -158,6 +158,10 @@ class EventRouter
void OnUnshare(const std::string& vm_name,
const base::FilePath& path) override;
// Notifies FilesApp that file drop to Plugin VM was not in a shared directory
// and failed FilesApp will show the "Move to Windows files" dialog.
void DropFailedPluginVmDirectoryNotShared();
// Returns a weak pointer for the event router.
base::WeakPtr<EventRouter> GetWeakPtr();
......
......@@ -966,7 +966,8 @@ WRAPPED_INSTANTIATE_TEST_SUITE_P(
TestCase("enableDisableCrostini"),
TestCase("sharePathWithCrostini"),
TestCase("pluginVmDirectoryNotSharedErrorDialog"),
TestCase("pluginVmFileOnExternalDriveErrorDialog")));
TestCase("pluginVmFileOnExternalDriveErrorDialog"),
TestCase("pluginVmFileDropFailErrorDialog")));
WRAPPED_INSTANTIATE_TEST_SUITE_P(
MyFiles, /* my_files.js */
......
......@@ -40,6 +40,8 @@
#include "chrome/browser/chromeos/crostini/crostini_pref_names.h"
#include "chrome/browser/chromeos/drive/drivefs_test_support.h"
#include "chrome/browser/chromeos/drive/file_system_util.h"
#include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
#include "chrome/browser/chromeos/extensions/file_manager/event_router_factory.h"
#include "chrome/browser/chromeos/file_manager/app_id.h"
#include "chrome/browser/chromeos/file_manager/file_manager_test_util.h"
#include "chrome/browser/chromeos/file_manager/file_tasks_notifier.h"
......@@ -2682,6 +2684,11 @@ void FileManagerBrowserTestBase::OnCommand(const std::string& name,
return;
}
if (name == "onDropFailedPluginVmDirectoryNotShared") {
EventRouterFactory::GetForProfile(profile())
->DropFailedPluginVmDirectoryNotShared();
return;
}
FAIL() << "Unknown test message: " << name;
}
......
......@@ -252,7 +252,8 @@ enum CrostiniEventType {
enable,
disable,
share,
unshare
unshare,
drop_failed_plugin_vm_directory_not_shared
};
enum ProviderSource {
......
......@@ -267,6 +267,8 @@ chrome.fileManagerPrivate.CrostiniEventType = {
DISABLE: 'disable',
SHARE: 'share',
UNSHARE: 'unshare',
DROP_FAILED_PLUGIN_VM_DIRECTORY_NOT_SHARED:
'drop_failed_plugin_vm_directory_not_shared',
};
/**
......
......@@ -1226,6 +1226,19 @@ class FileManager extends cr.EventTarget {
case chrome.fileManagerPrivate.CrostiniEventType.DISABLE:
this.crostini_.setEnabled(event.vmName, false);
return this.crostiniController_.redraw();
// Event is sent when a user drops an unshared file on Plugin VM.
// We show the move dialog so the user can move the file or share the
// directory.
case chrome.fileManagerPrivate.CrostiniEventType
.DROP_FAILED_PLUGIN_VM_DIRECTORY_NOT_SHARED:
if (this.ui_.dragInProcess) {
FileTasks.showPluginVmMoveDialog(
this.selectionHandler.selection.entries, this.volumeManager_,
assert(this.ui_), 'Windows', this.fileTransferController_,
assert(this.directoryModel_));
}
break;
}
}
......
......@@ -413,6 +413,16 @@ class FileManagerUI {
e.stopPropagation();
});
}
/**
* True while FilesApp is in the process of a drag and drop. Set to true on
* 'dragstart', set to false on 'dragend'. If CrostiniEvent
* 'drop_failed_plugin_vm_directory_not_shared' is received during drag, we
* show the move-to-windows-files dialog.
*
* @public {boolean}
*/
this.dragInProcess = false;
}
/**
......@@ -486,6 +496,13 @@ class FileManagerUI {
}
});
});
document.addEventListener('dragstart', () => {
this.dragInProcess = true;
});
document.addEventListener('dragend', () => {
this.dragInProcess = false;
});
}
/**
......
......@@ -211,3 +211,26 @@ testcase.pluginVmFileOnExternalDriveErrorDialog = async () => {
// TODO(crbug.com/1049453): Test file is moved. This can only be tested when
// tests allow creating /MyFiles/PvmDefault.
};
/**
* Tests that when drag from Files app and dropping in the Plugin VM a
* dialog is displayed if the containing folder isn't shared with Plugin VM.
*/
testcase.pluginVmFileDropFailErrorDialog = async () => {
const appId = await setupAndWaitUntilReady(RootPath.DOWNLOADS);
// Select 'hello.txt' file.
await remoteCall.callRemoteTestUtil(
'fakeMouseClick', appId, ['[id^="listitem-"][file-name="hello.txt"]']);
// Send 'dragstart'.
chrome.test.assertTrue(await remoteCall.callRemoteTestUtil(
'fakeEvent', appId, ['body', 'dragstart', {bubbles: true}]));
// Send CrostiniEvent 'drop_failed_plugin_vm_directory_not_shared'.
await sendTestMessage({name: 'onDropFailedPluginVmDirectoryNotShared'});
// Wait for error dialog.
await remoteCall.waitForElement(
appId, '.cr-dialog-frame:not(#default-task-dialog):not([hidden])');
};
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