Commit 3619cfab authored by Joel Hockey's avatar Joel Hockey Committed by Commit Bot

Show retry dialog when Plugin VM unsharing folder fails

Unsharing can fail if the VM has a file open within a shared folder.
In that case, we show a dialog to notify the user that unsharing failed
and allow them to retry.

https://storage.cloud.google.com/chromium-translation-screenshots/ec0fdd89ab5d4afc79c7e65d463ecece4925ac68

Bug: 920416
Change-Id: Ib47a7ca6a1d3a8b9501613a36b211fcc5a70d1f4
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2413428
Commit-Queue: Joel Hockey <joelhockey@chromium.org>
Reviewed-by: default avatarKyle Horimoto <khorimoto@chromium.org>
Reviewed-by: default avatarRegan Hsu <hsuregan@chromium.org>
Auto-Submit: Joel Hockey <joelhockey@chromium.org>
Cr-Commit-Position: refs/heads/master@{#808159}
parent b7eb25b4
......@@ -3081,6 +3081,15 @@
<message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING" desc="Tooltip to show when hovering on the remove icon for a Parallels shared folder.">
Stop sharing
</message>
<message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE" desc="Message to show user when unsharing a Parallels shared folder fails. Do not translate 'Parallels Desktop'">
Couldn't unshare because an application is using this folder. The folder will be unshared when Parallels Desktop is next shut down.
</message>
<message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE" desc="Title of the error dialog shown to a user when unsharing a Parallels shared folder fails.">
Unshare failed
</message>
<message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN" desc="Button text in the dialog shown when unsharing a Parallels shared folder fails. Pressing this button will attempt to unshare the folder again.">
Try again
</message>
<message name="IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_EMPTY_MESSAGE" desc="Message shown when the user has not yet shared any folders in Parallels.">
Shared folders will appear here
</message>
......
......@@ -41,6 +41,7 @@ cr.define('settings', function() {
/**
* @param {string} vmName VM to stop sharing path with.
* @param {string} path Path to stop sharing.
* @return {!Promise<boolean>} Result of unsharing.
*/
removePluginVmSharedPath(vmName, path) {}
......@@ -82,7 +83,7 @@ cr.define('settings', function() {
/** @override */
removePluginVmSharedPath(vmName, path) {
chrome.send('removePluginVmSharedPath', [vmName, path]);
return cr.sendWithPromise('removePluginVmSharedPath', vmName, path);
}
/** @override */
......
......@@ -42,6 +42,27 @@
</template>
</iron-list>
</div>
<template is="dom-if" if="[[sharedPathWhichFailedRemoval_]]" restamp>
<cr-dialog id="removeSharedPathFailedDialog" close-text="$i18n{close}"
show-on-attach>
<div slot="title">
$i18n{pluginVmSharedPathsRemoveFailureDialogTitle}
</div>
<div slot="body">
$i18n{pluginVmSharedPathsRemoveFailureDialogMessage}
</div>
<div slot="button-container">
<cr-button id="cancel" class="cancel-button"
on-click="onRemoveFailedDismissClick_">
$i18n{ok}
</cr-button>
<cr-button id="retry" class="action-button"
on-click="onRemoveFailedRetryClick_">
$i18n{pluginVmSharedPathsRemoveFailureTryAgain}
</cr-button>
</div>
</cr-dialog>
</template>
</template>
<script src="plugin_vm_shared_paths.js"></script>
</dom-module>
......@@ -31,6 +31,17 @@ Polymer({
* @private {Array<!{path: string, pathDisplayText: string}>}
*/
sharedPaths_: Array,
/**
* The shared path which failed to be removed in the most recent attempt
* to remove a path. Null indicates that removal succeeded. When non-null,
* the failure dialog is shown.
* @private {?string}
*/
sharedPathWhichFailedRemoval_: {
type: String,
value: null,
},
},
observers: [
......@@ -57,14 +68,38 @@ Polymer({
});
},
/**
* @param {string} path
* @private
*/
removeSharedPath_(path) {
this.sharedPathWhichFailedRemoval_ = null;
settings.PluginVmBrowserProxyImpl.getInstance()
.removePluginVmSharedPath(PLUGIN_VM, path)
.then(success => {
if (!success) {
this.sharedPathWhichFailedRemoval_ = path;
}
});
settings.recordSettingChange();
},
/**
* @param {!Event} event
* @private
*/
onRemoveSharedPathClick_(event) {
settings.PluginVmBrowserProxyImpl.getInstance().removePluginVmSharedPath(
PLUGIN_VM, event.model.item.path);
settings.recordSettingChange();
this.removeSharedPath_(event.model.item.path);
},
/** @private */
onRemoveFailedRetryClick_() {
this.removeSharedPath_(assert(this.sharedPathWhichFailedRemoval_));
},
/** @private */
onRemoveFailedDismissClick_() {
this.sharedPathWhichFailedRemoval_ = null;
},
});
})();
......@@ -326,6 +326,12 @@ void AppsSection::AddPluginVmLoadTimeData(
IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_INSTRUCTIONS_REMOVE},
{"pluginVmSharedPathsRemoveSharing",
IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_SHARING},
{"pluginVmSharedPathsRemoveFailureDialogMessage",
IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_DIALOG_MESSAGE},
{"pluginVmSharedPathsRemoveFailureDialogTitle",
IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_DIALOG_TITLE},
{"pluginVmSharedPathsRemoveFailureTryAgain",
IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_REMOVE_FAILURE_TRY_AGAIN},
{"pluginVmSharedPathsListEmptyMessage",
IDS_SETTINGS_APPS_PLUGIN_VM_SHARED_PATHS_LIST_EMPTY_MESSAGE},
{"pluginVmSharedUsbDevicesLabel",
......
......@@ -104,22 +104,27 @@ void PluginVmHandler::HandleGetPluginVmSharedPathsDisplayText(
void PluginVmHandler::HandleRemovePluginVmSharedPath(
const base::ListValue* args) {
CHECK_EQ(2U, args->GetSize());
std::string vm_name = args->GetList()[0].GetString();
std::string path = args->GetList()[1].GetString();
CHECK_EQ(3U, args->GetList().size());
std::string callback_id = args->GetList()[0].GetString();
std::string vm_name = args->GetList()[1].GetString();
std::string path = args->GetList()[2].GetString();
guest_os::GuestOsSharePath::GetForProfile(profile_)->UnsharePath(
vm_name, base::FilePath(path),
/*unpersist=*/true,
base::BindOnce(
[](const std::string& path, bool result,
const std::string& failure_reason) {
if (!result) {
LOG(ERROR) << "Error unsharing " << path << ": "
<< failure_reason;
}
},
path));
base::BindOnce(&PluginVmHandler::OnPluginVmSharedPathRemoved,
weak_ptr_factory_.GetWeakPtr(), callback_id, path));
}
void PluginVmHandler::OnPluginVmSharedPathRemoved(
const std::string& callback_id,
const std::string& path,
bool success,
const std::string& failure_reason) {
if (!success) {
LOG(ERROR) << "Error unsharing " << path << ": " << failure_reason;
}
ResolveJavascriptCallback(base::Value(callback_id), base::Value(success));
}
void PluginVmHandler::HandleNotifyPluginVmSharedUsbDevicesPageReady(
......
......@@ -52,6 +52,11 @@ class PluginVmHandler : public ::settings::SettingsPageUIHandler,
// Relaunches Plugin VM.
void HandleRelaunchPluginVm(const base::ListValue* args);
void OnPluginVmSharedPathRemoved(const std::string& callback_id,
const std::string& path,
bool success,
const std::string& failure_reason);
Profile* profile_;
// weak_ptr_factory_ should always be last member.
......
......@@ -9,6 +9,7 @@ class TestPluginVmBrowserProxy extends TestBrowserProxy {
'getPluginVmSharedPathsDisplayText',
'removePluginVmSharedPath',
]);
this.removeSharedPathResult = true;
}
/** override */
......@@ -20,6 +21,7 @@ class TestPluginVmBrowserProxy extends TestBrowserProxy {
/** override */
removePluginVmSharedPath(vmName, path) {
this.methodCalled('removePluginVmSharedPath', [vmName, path]);
return Promise.resolve(this.removeSharedPathResult);
}
}
......@@ -94,4 +96,25 @@ suite('SharedPaths', function() {
assertTrue(page.$.pluginVmList.hidden);
assertFalse(page.$.pluginVmListEmpty.hidden);
});
test('RemoveFailedRetry', async function() {
await setPrefs({'path1': ['PvmDefault'], 'path2': ['PvmDefault']});
// Remove shared path fails.
pluginVmBrowserProxy.removeSharedPathResult = false;
page.$$('.list-item cr-icon-button').click();
await pluginVmBrowserProxy.whenCalled('removePluginVmSharedPath');
Polymer.dom.flush();
assertTrue(page.$$('#removeSharedPathFailedDialog').open);
// Click retry and make sure 'removePluginVmSharedPath' is called
// and dialog is closed/removed.
pluginVmBrowserProxy.removeSharedPathResult = true;
page.$$('#removeSharedPathFailedDialog')
.querySelector('.action-button')
.click();
await pluginVmBrowserProxy.whenCalled('removePluginVmSharedPath');
assertFalse(!!page.$$('#removeSharedPathFailedDialog'));
});
});
......@@ -12,6 +12,7 @@ class TestPluginVmBrowserProxy extends TestBrowserProxy {
'setPluginVmPermission',
'relaunchPluginVm',
]);
this.removeSharedPathResult = true;
this.pluginVmRunning = false;
this.permissions = [true, true]; // [0]Camera, [1]Microphone
}
......@@ -25,6 +26,7 @@ class TestPluginVmBrowserProxy extends TestBrowserProxy {
/** @override */
removePluginVmSharedPath(vmName, path) {
this.methodCalled('removePluginVmSharedPath', vmName, path);
return Promise.resolve(this.removeSharedPathResult);
}
/** @override */
......
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