Commit 479efc9a authored by Joel Hockey's avatar Joel Hockey Committed by Commit Bot

CrOS FilesApp: Crostini shared path management

New JS class Crostini to manage crostini shared paths.
Record paths that are shared, and do not show 'Share with Linux'
option for paths that are already shared.

Update Crostini UI test:
 * Verify that 'Share with Linux' is not shown for files (only dirs).
 * Fixed drive_welcome.css path.

Bug: 878324
Change-Id: I6b8445d0a127043f9d24126384267284af5ed7ab
Reviewed-on: https://chromium-review.googlesource.com/1215154Reviewed-by: default avatarLuciano Pacheco <lucmult@chromium.org>
Commit-Queue: Joel Hockey <joelhockey@chromium.org>
Cr-Commit-Position: refs/heads/master@{#590211}
parent bf5fffc9
......@@ -217,3 +217,8 @@ IN_PROC_BROWSER_TEST_F(FileManagerJsTest, FilesAppEntryTypes) {
IN_PROC_BROWSER_TEST_F(FileManagerJsTest, UtilTest) {
RunTest(base::FilePath(FILE_PATH_LITERAL("common/js/util_unittest.html")));
}
IN_PROC_BROWSER_TEST_F(FileManagerJsTest, Crostini) {
RunTest(base::FilePath(
FILE_PATH_LITERAL("foreground/js/crostini_unittest.html")));
}
......@@ -18,6 +18,7 @@ js_type_check("closure_compile_module") {
":closure_compile_externs",
":column_visibility_controller",
":constants",
":crostini",
":dialog_action_controller",
":dialog_type",
":directory_contents",
......@@ -160,6 +161,12 @@ js_library("dialog_action_controller") {
]
}
js_library("crostini") {
deps = [
":volume_manager_wrapper",
]
}
js_library("dialog_type") {
}
......@@ -304,6 +311,7 @@ js_library("file_selection") {
js_library("file_tasks") {
deps = [
":crostini",
":directory_model",
":task_history",
":volume_manager_wrapper",
......
......@@ -57,3 +57,9 @@ constants.LIST_CONTAINER_METADATA_PREFETCH_PROPERTY_NAMES = [
* @type {string}
*/
constants.FILES_QUICK_VIEW_HTML = 'foreground/elements/files_quick_view.html';
/**
* Path for drive_welcome.css file. Allow override for testing.
* @type {string}
*/
constants.DRIVE_WELCOME_CSS = 'foreground/css/drive_welcome.css';
\ No newline at end of file
// Copyright 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.
const Crostini = {};
/**
* Maintains a list of paths shared with the crostini container.
* Keyed by VolumeManagerCommon.RootType, with boolean set values
* of string paths. e.g. {'Downloads': {'/foo': true, '/bar': true}}.
* @private @dict {!Object<!Object<boolean>>}
*/
Crostini.SHARED_PATHS_ = {};
/**
* Add entry as a shared path.
* @param {!Entry} entry
* @param {!VolumeManagerWrapper} volumeManager
*/
Crostini.addSharedPath = function(entry, volumeManager) {
const root = volumeManager.getLocationInfo(entry).rootType;
let paths = Crostini.SHARED_PATHS_[root];
if (!paths) {
paths = {};
Crostini.SHARED_PATHS_[root] = paths;
}
paths[entry.fullPath] = true;
};
/**
* Returns true if entry is shared.
* @param {!Entry} entry
* @param {!VolumeManagerWrapper} volumeManager
* @return {boolean} True if path is shared either by a direct
* share or from one of its ancestor directories.
*/
Crostini.isPathShared = function(entry, volumeManager) {
const root = volumeManager.getLocationInfo(entry).rootType;
const paths = Crostini.SHARED_PATHS_[root];
if (!paths)
return false;
// Check path and all ancestor directories.
let path = entry.fullPath;
while (path != '') {
if (paths[path])
return true;
path = path.substring(0, path.lastIndexOf('/'));
}
return false;
};
/**
* @param {!Entry} entry
* @param {!VolumeManagerWrapper} volumeManager
* @return {boolean} True if the entry is from crostini.
*/
Crostini.isCrostiniEntry = function(entry, volumeManager) {
return volumeManager.getLocationInfo(entry).rootType ===
VolumeManagerCommon.RootType.CROSTINI;
};
<!DOCTYPE html>
<!-- Copyright 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.
-->
<script src="../../common/js/mock_entry.js"></script>
<script src="crostini.js"></script>
<script src="crostini_unittest.js"></script>
// Copyright 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.
const volumeManager = {
getLocationInfo: (entry) => {
return {root: 'testroot'};
},
};
function testIsPathShared() {
const mockFileSystem = new MockFileSystem('volumeId');
const root = new MockDirectoryEntry(mockFileSystem, '/');
const foo1 = new MockDirectoryEntry(mockFileSystem, '/foo1');
const foobar1 = new MockDirectoryEntry(mockFileSystem, '/foo1/bar1');
const foo2 = new MockDirectoryEntry(mockFileSystem, '/foo2');
const foobar2 = new MockDirectoryEntry(mockFileSystem, '/foo2/bar2');
assertFalse(Crostini.isPathShared(foo1, volumeManager));
Crostini.addSharedPath(foo1, volumeManager);
assertFalse(Crostini.isPathShared(root, volumeManager));
assertTrue(Crostini.isPathShared(foo1, volumeManager));
assertTrue(Crostini.isPathShared(foobar1, volumeManager));
Crostini.addSharedPath(foobar2, volumeManager);
assertFalse(Crostini.isPathShared(foo2, volumeManager));
assertTrue(Crostini.isPathShared(foobar2, volumeManager));
}
......@@ -1656,12 +1656,16 @@ CommandHandler.COMMANDS_['share-with-linux'] = /** @type {Command} */ ({
execute: function(event, fileManager) {
const entry = CommandUtil.getCommandEntry(event.target);
if (entry && entry.isDirectory) {
const dir = /** @type {!DirectoryEntry} */ (entry);
chrome.fileManagerPrivate.sharePathWithCrostiniContainer(
/** @type {!DirectoryEntry} */ (entry), () => {
if (chrome.runtime.lastError)
dir, () => {
if (chrome.runtime.lastError) {
console.error(
'Error sharing with linux: ' +
chrome.runtime.lastError.message);
} else {
Crostini.addSharedPath(dir, assert(fileManager.volumeManager));
}
});
}
},
......@@ -1670,10 +1674,11 @@ CommandHandler.COMMANDS_['share-with-linux'] = /** @type {Command} */ ({
* @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use.
*/
canExecute: function(event, fileManager) {
// Must be single directory subfolder of Downloads.
// Must be single directory subfolder of Downloads not already shared.
const entries = CommandUtil.getCommandEntries(event.target);
event.canExecute = CommandHandler.IS_CROSTINI_FILES_ENABLED_ &&
entries.length === 1 && entries[0].isDirectory &&
!Crostini.isPathShared(entries[0], assert(fileManager.volumeManager)) &&
entries[0].fullPath !== '/' &&
fileManager.volumeManager.getLocationInfo(entries[0]).rootType ===
VolumeManagerCommon.RootType.DOWNLOADS;
......
......@@ -188,7 +188,7 @@ FileTasks.create = function(
// a dialog with an error message, similar to when attempting to run
// Crostini tasks with non-Crostini entries.
if (entries.length !== 1 ||
!FileTasks.isCrostiniEntry_(entries[0], volumeManager)) {
!Crostini.isCrostiniEntry(entries[0], volumeManager)) {
taskItems = taskItems.filter(function(item) {
var taskParts = item.taskId.split('|');
var appId = taskParts[0];
......@@ -448,24 +448,13 @@ FileTasks.isCrostiniTask_ = function(taskId) {
return taskId.split('|', 2)[1] === 'crostini';
};
/**
* @param {!Entry} entry
* @param {!VolumeManagerWrapper} volumeManager
* @return {boolean} True if the entry is from crostini.
* @private
*/
FileTasks.isCrostiniEntry_ = function(entry, volumeManager) {
return volumeManager.getLocationInfo(entry).rootType ===
VolumeManagerCommon.RootType.CROSTINI;
};
/**
* @return {boolean} True if all entries are crostini.
* @private
*/
FileTasks.prototype.allCrostiniEntries_ = function() {
return this.entries_.every(
entry => FileTasks.isCrostiniEntry_(entry, this.volumeManager_));
entry => Crostini.isCrostiniEntry(entry, this.volumeManager_));
};
/**
......
......@@ -15,5 +15,6 @@
<script src="constants.js"></script>
<script src="web_store_utils.js"></script>
<script src="crostini.js"></script>
<script src="file_tasks.js"></script>
<script src="file_tasks_unittest.js"></script>
......@@ -108,6 +108,7 @@
// <include src="actions_model.js">
// <include src="app_state_controller.js">
// <include src="column_visibility_controller.js">
// <include src="crostini.js">
// <include src="dialog_action_controller.js">
// <include src="dialog_type.js">
// <include src="directory_contents.js">
......
......@@ -20,6 +20,7 @@
<script src="../../common/js/unittest_util.js"></script>
<script src="../../common/js/util.js"></script>
<script src="../../common/js/volume_manager_common.js"></script>
<script src="crostini.js"></script>
<script src="dialog_type.js"></script>
<script src="file_selection.js"></script>
<script src="file_tasks.js"></script>
......
......@@ -212,7 +212,7 @@ Banners.prototype.prepareAndShowWelcomeBanner_ = function(type, messageId) {
if (!this.document_.querySelector('link[drive-welcome-style]')) {
var style = this.document_.createElement('link');
style.rel = 'stylesheet';
style.href = 'foreground/css/drive_welcome.css';
style.href = constants.DRIVE_WELCOME_CSS;
style.setAttribute('drive-welcome-style', '');
this.document_.head.appendChild(style);
}
......
......@@ -228,6 +228,7 @@ crostini.testSharePathCrostiniSuccess = (done) => {
};
// Verify right-click menu with 'Share with Linux' is not shown for:
// * Files (not directory)
// * Root Downloads folder
// * Any folder outside of downloads (e.g. crostini or drive)
crostini.testSharePathNotShown = (done) => {
......@@ -239,6 +240,15 @@ crostini.testSharePathNotShown = (done) => {
'[command="#share-with-linux"][hidden][disabled="disabled"]';
test.setupAndWaitUntilReady()
.then(() => {
// Right-click 'hello.txt' file.
// Check 'Share with Linux' is not shown in menu.
test.selectFile('hello.txt');
assertTrue(
test.fakeMouseRightClick('#file-list li[selected]'),
'right-click hello.txt');
return test.waitForElement(menuNoShareWithLinux);
})
.then(() => {
// Select 'My files' in directory tree to show Downloads in file list.
assertTrue(test.fakeMouseClick(myFiles), 'click My files');
......
......@@ -9,6 +9,7 @@ var test = test || {};
// Update paths for testing.
constants.FILES_QUICK_VIEW_HTML = 'test/gen/elements/files_quick_view.html';
constants.DRIVE_WELCOME_CSS = FILE_MANAGER_ROOT + constants.DRIVE_WELCOME_CSS;
// Stores Blobs loaded from src/chrome/test/data/chromeos/file_manager.
test.DATA = {
......
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