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

Move crostini shared path state into background

Convert Crostini to a class, and move state into bg so that all
FilesApp windows will have a consistent view of which paths are shared.

Bug: 878324
Change-Id: I3c21465b625091fd6360e6b799d9e85baf1ea614
Reviewed-on: https://chromium-review.googlesource.com/c/1335747
Commit-Queue: Joel Hockey <joelhockey@chromium.org>
Reviewed-by: default avatarSam McNally <sammc@chromium.org>
Cr-Commit-Position: refs/heads/master@{#608270}
parent bde2b01f
......@@ -215,5 +215,5 @@ IN_PROC_BROWSER_TEST_F(FileManagerJsTest, UtilTest) {
}
IN_PROC_BROWSER_TEST_F(FileManagerJsTest, Crostini) {
RunGeneratedTest("/foreground/js/crostini_unittest.html");
RunGeneratedTest("/background/js/crostini_unittest.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.
/**
* Crostini shared path state handler.
* @constructor
*/
function Crostini() {}
/**
* Set from feature 'crostini-files'.
* @param {boolean} enabled
*/
Crostini.prototype.setEnabled = function(enabled) {};
/**
* @return {boolean} Whether crostini is enabled.
*/
Crostini.prototype.isEnabled = function() {};
/**
* Registers an entry as a shared path.
* @param {!Entry} entry
*/
Crostini.prototype.registerSharedPath = function(entry) {};
/**
* Unregisters entry as a shared path.
* @param {!Entry} entry
*/
Crostini.prototype.unregisterSharedPath = function(entry) {};
/**
* Returns true if entry is shared.
* @param {!Entry} entry
* @return {boolean} True if path is shared either by a direct
* share or from one of its ancestor directories.
*/
Crostini.prototype.isPathShared = function(entry) {};
/**
* Returns true if entry can be shared with Crostini.
* @param {!Entry} entry
* @param {boolean} persist If path is to be persisted.
*/
Crostini.prototype.canSharePath = function(entry, persist) {};
......@@ -44,3 +44,8 @@ FileBrowserBackgroundFull.prototype.mediaScanner;
* @type {!importer.HistoryLoader}
*/
FileBrowserBackgroundFull.prototype.historyLoader;
/**
* @type {!Crostini}
*/
FileBrowserBackgroundFull.prototype.crostini;
......@@ -107,3 +107,8 @@ CommandHandlerDeps.prototype.getSelection = function() {};
* @type {MetadataModel}
*/
CommandHandlerDeps.prototype.metadataModel;
/**
* @type {Crostini}
*/
CommandHandlerDeps.prototype.crostini;
......@@ -36,6 +36,7 @@ js_type_check("closure_compile_module") {
":background",
":background_base",
":closure_compile_externs",
":crostini",
":device_handler",
":drive_sync_handler",
":duplicate_finder",
......@@ -96,6 +97,7 @@ js_library("background") {
deps = [
":app_windows",
":background_base",
":crostini",
":device_handler",
":drive_sync_handler",
":duplicate_finder",
......@@ -124,6 +126,22 @@ js_library("background_base") {
]
}
js_library("crostini") {
deps = [
"../../common/js:metrics",
"//ui/file_manager/base/js:volume_manager_types",
"//ui/file_manager/externs:volume_manager",
]
}
js_unittest("crostini_unittest") {
deps = [
":crostini",
"../../common/js:mock_entry",
"//ui/webui/resources/js:webui_resource_test",
]
}
js_library("device_handler") {
deps = [
":volume_manager_factory",
......@@ -333,6 +351,7 @@ js_library("volume_manager_util") {
js_unit_tests("unit_tests") {
deps = [
":crostini_unittest",
":volume_manager_unittest",
]
}
......@@ -82,6 +82,9 @@ function FileBrowserBackgroundImpl() {
this.progressCenter, this.historyLoader, this.dispositionChecker_,
this.driveSyncHandler);
/** @type {!Crostini} */
this.crostini = new Crostini();
/**
* String assets.
* @type {Object<string>}
......@@ -101,7 +104,7 @@ function FileBrowserBackgroundImpl() {
chrome.contextMenus.onClicked.addListener(
this.onContextMenuClicked_.bind(this));
// Initializa string and volume manager related stuffs.
// Initialize string and volume manager related stuffs.
this.initializationPromise_.then(function(strings) {
this.stringData = strings;
this.initContextMenu_();
......@@ -110,6 +113,9 @@ function FileBrowserBackgroundImpl() {
volumeManager.addEventListener(
VolumeManagerCommon.VOLUME_ALREADY_MOUNTED,
this.handleViewEvent_.bind(this));
this.crostini.init(volumeManager);
this.crostini.listen();
}.bind(this));
this.fileOperationManager = new FileOperationManager();
......
......@@ -12,6 +12,7 @@
// <include src="../../common/js/progress_center_common.js">
// <include src="../../common/js/importer_common.js">
// <include src="metadata_proxy.js">
// <include src="crostini.js">
// <include src="device_handler.js">
// <include src="drive_sync_handler.js">
// <include src="duplicate_finder.js">
......
......@@ -2,18 +2,28 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
const Crostini = {};
/**
* Set from cmd line flag 'crostini-files'.
* @type {boolean}
* Crostini shared path state handler.
* @constructor
*/
Crostini.IS_CROSTINI_FILES_ENABLED = false;
function Crostini() {
/** @private {boolean} */
this.enabled_ = false;
/**
* 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>>}
*/
this.shared_paths_ = {};
}
/**
* Keep in sync with histograms.xml:FileBrowserCrostiniSharedPathsDepth
* histogram_suffix.
* @type {!Map<VolumeManagerCommon.RootType, string>}
* @const
*/
Crostini.VALID_ROOT_TYPES_FOR_SHARE = new Map([
[VolumeManagerCommon.RootType.DOWNLOADS, 'Downloads'],
......@@ -21,16 +31,18 @@ Crostini.VALID_ROOT_TYPES_FOR_SHARE = new Map([
]);
/**
* Can be collapsed into VALD_ROOT_TYPES_FOR_SHARE once
* Can be collapsed into VALID_ROOT_TYPES_FOR_SHARE once
* DriveFS flag is removed.
* Keep in sync with histograms.xml:FileBrowserCrostiniSharedPathsDepth
* histogram_suffix.
* @type {!Map<VolumeManagerCommon.RootType, string>}
* @const
*/
Crostini.VALID_DRIVE_FS_ROOT_TYPES_FOR_SHARE = new Map([
[VolumeManagerCommon.RootType.COMPUTERS_GRAND_ROOT, 'DriveComputers'],
[VolumeManagerCommon.RootType.COMPUTER, 'DriveComputers'],
[VolumeManagerCommon.RootType.DRIVE, 'MyDrive'],
[VolumeManagerCommon.RootType.TEAM_DRIVES_GRAND_ROOT, 'TeamDrive'],
[VolumeManagerCommon.RootType.TEAM_DRIVE, 'TeamDrive'],
]);
......@@ -38,26 +50,48 @@ Crostini.VALID_DRIVE_FS_ROOT_TYPES_FOR_SHARE = new Map([
Crostini.UMA_ROOT_TYPE_OTHER = 'Other';
/**
* 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>>}
* Initialize Volume Manager.
* @param {!VolumeManager} volumeManager
*/
Crostini.prototype.init = function(volumeManager) {
this.volumeManager_ = volumeManager;
};
/**
* Register for any shared path changes.
*/
Crostini.prototype.listen = function() {
chrome.fileManagerPrivate.onCrostiniSharedPathsChanged.addListener(
this.onChange_.bind(this));
};
/**
* Set from feature 'crostini-files'.
* @param {boolean} enabled
*/
Crostini.prototype.setEnabled = function(enabled) {
this.enabled_ = enabled;
};
/**
* @return {boolean} Whether crostini is enabled.
*/
Crostini.SHARED_PATHS_ = {};
Crostini.prototype.isEnabled = function() {
return this.enabled_;
};
/**
* Registers an entry as a shared path.
* @param {!Entry} entry
* @param {!VolumeManager} volumeManager
*/
Crostini.registerSharedPath = function(entry, volumeManager) {
const info = volumeManager.getLocationInfo(entry);
Crostini.prototype.registerSharedPath = function(entry) {
const info = this.volumeManager_.getLocationInfo(entry);
if (!info)
return;
let paths = Crostini.SHARED_PATHS_[info.rootType];
let paths = this.shared_paths_[info.rootType];
if (!paths) {
paths = {};
Crostini.SHARED_PATHS_[info.rootType] = paths;
this.shared_paths_[info.rootType] = paths;
}
// Remove any existing paths that are children of the new path.
for (let path in paths) {
......@@ -78,13 +112,12 @@ Crostini.registerSharedPath = function(entry, volumeManager) {
/**
* Unregisters entry as a shared path.
* @param {!Entry} entry
* @param {!VolumeManager} volumeManager
*/
Crostini.unregisterSharedPath = function(entry, volumeManager) {
const info = volumeManager.getLocationInfo(entry);
Crostini.prototype.unregisterSharedPath = function(entry) {
const info = this.volumeManager_.getLocationInfo(entry);
if (!info)
return;
const paths = Crostini.SHARED_PATHS_[info.rootType];
const paths = this.shared_paths_[info.rootType];
if (paths) {
delete paths[entry.fullPath];
}
......@@ -92,17 +125,17 @@ Crostini.unregisterSharedPath = function(entry, volumeManager) {
/**
* Handles shared path changes.
* @param {!VolumeManager} volumeManager
* @param {chrome.fileManagerPrivate.CrostiniSharedPathsChangedEvent} event
* @private
*/
Crostini.onSharedPathsChanged = function(volumeManager, event) {
Crostini.prototype.onChange_ = function(event) {
if (event.eventType === 'share') {
for (const entry of event.entries) {
Crostini.registerSharedPath(entry, volumeManager);
this.registerSharedPath(entry);
}
} else if (event.eventType === 'unshare') {
for (const entry of event.entries) {
Crostini.unregisterSharedPath(entry, volumeManager);
this.unregisterSharedPath(entry);
}
}
};
......@@ -110,13 +143,12 @@ Crostini.onSharedPathsChanged = function(volumeManager, event) {
/**
* Returns true if entry is shared.
* @param {!Entry} entry
* @param {!VolumeManager} 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];
Crostini.prototype.isPathShared = function(entry) {
const root = this.volumeManager_.getLocationInfo(entry).rootType;
const paths = this.shared_paths_[root];
if (!paths)
return false;
// Check path and all ancestor directories.
......@@ -129,26 +161,13 @@ Crostini.isPathShared = function(entry, volumeManager) {
return !!paths['/'];
};
/**
* @param {!Entry} entry
* @param {!VolumeManager} volumeManager
* @return {boolean} True if the entry is from crostini.
*/
Crostini.isCrostiniEntry = function(entry, volumeManager) {
return volumeManager.getLocationInfo(entry).rootType ===
VolumeManagerCommon.RootType.CROSTINI;
};
/**
* Returns true if entry can be shared with Crostini.
* @param {!Entry} entry
* @param {boolean} persist If path is to be persisted.
* @param {!VolumeManager} volumeManager
*/
Crostini.canSharePath = function(entry, persist, volumeManager) {
// Check crostini-files flag and valid volume.
if (!Crostini.IS_CROSTINI_FILES_ENABLED)
Crostini.prototype.canSharePath = function(entry, persist) {
if (!this.enabled_)
return false;
// Only directories for persistent shares.
......@@ -156,20 +175,8 @@ Crostini.canSharePath = function(entry, persist, volumeManager) {
return false;
// Allow Downloads, and Drive if DriveFS is enabled.
const rootType = volumeManager.getLocationInfo(entry).rootType;
const rootType = this.volumeManager_.getLocationInfo(entry).rootType;
return Crostini.VALID_ROOT_TYPES_FOR_SHARE.has(rootType) ||
(loadTimeData.getBoolean('DRIVE_FS_ENABLED') &&
Crostini.VALID_DRIVE_FS_ROOT_TYPES_FOR_SHARE.has(rootType));
};
/**
* Returns true if task requires entries to be shared before executing task.
* @param {!chrome.fileManagerPrivate.FileTask} task Task to run.
* @return {boolean} true if task requires entries to be shared.
*/
Crostini.taskRequiresSharing = function(task) {
const taskParts = task.taskId.split('|');
const taskType = taskParts[1];
const actionId = taskParts[2];
return taskType === 'crostini' || actionId === 'install-linux-package';
};
......@@ -20,6 +20,10 @@ const volumeManager = /** @type {!VolumeManager} */ ({
},
});
const crostini = new Crostini();
crostini.init(volumeManager);
function testIsPathShared() {
const mockFileSystem = new MockFileSystem('volumeId');
const root = new MockDirectoryEntry(mockFileSystem, '/');
......@@ -29,58 +33,57 @@ function testIsPathShared() {
const b = new MockDirectoryEntry(mockFileSystem, '/b');
const bb = new MockDirectoryEntry(mockFileSystem, '/b/b');
assertFalse(Crostini.isPathShared(a, volumeManager));
assertFalse(crostini.isPathShared(a));
Crostini.registerSharedPath(a, volumeManager);
assertFalse(Crostini.isPathShared(root, volumeManager));
assertTrue(Crostini.isPathShared(a, volumeManager));
assertTrue(Crostini.isPathShared(aa, volumeManager));
crostini.registerSharedPath(a);
assertFalse(crostini.isPathShared(root));
assertTrue(crostini.isPathShared(a));
assertTrue(crostini.isPathShared(aa));
Crostini.registerSharedPath(bb, volumeManager);
assertFalse(Crostini.isPathShared(b, volumeManager));
assertTrue(Crostini.isPathShared(bb, volumeManager));
crostini.registerSharedPath(bb);
assertFalse(crostini.isPathShared(b));
assertTrue(crostini.isPathShared(bb));
Crostini.unregisterSharedPath(bb, volumeManager);
assertFalse(Crostini.isPathShared(bb, volumeManager));
crostini.unregisterSharedPath(bb);
assertFalse(crostini.isPathShared(bb));
// Test collapsing. Setup with /a/a, /a/b, /b
Crostini.unregisterSharedPath(a, volumeManager);
Crostini.registerSharedPath(aa, volumeManager);
Crostini.registerSharedPath(ab, volumeManager);
Crostini.registerSharedPath(b, volumeManager);
assertFalse(Crostini.isPathShared(a, volumeManager));
assertTrue(Crostini.isPathShared(aa, volumeManager));
assertTrue(Crostini.isPathShared(ab, volumeManager));
assertTrue(Crostini.isPathShared(b, volumeManager));
crostini.unregisterSharedPath(a);
crostini.registerSharedPath(aa);
crostini.registerSharedPath(ab);
crostini.registerSharedPath(b);
assertFalse(crostini.isPathShared(a));
assertTrue(crostini.isPathShared(aa));
assertTrue(crostini.isPathShared(ab));
assertTrue(crostini.isPathShared(b));
// Add /a, collapses /a/a, /a/b
Crostini.registerSharedPath(a, volumeManager);
assertTrue(Crostini.isPathShared(a, volumeManager));
assertTrue(Crostini.isPathShared(aa, volumeManager));
assertTrue(Crostini.isPathShared(ab, volumeManager));
assertTrue(Crostini.isPathShared(b, volumeManager));
crostini.registerSharedPath(a);
assertTrue(crostini.isPathShared(a));
assertTrue(crostini.isPathShared(aa));
assertTrue(crostini.isPathShared(ab));
assertTrue(crostini.isPathShared(b));
// Unregister /a, /a/a and /a/b should be lost.
Crostini.unregisterSharedPath(a, volumeManager);
assertFalse(Crostini.isPathShared(a, volumeManager));
assertFalse(Crostini.isPathShared(aa, volumeManager));
assertFalse(Crostini.isPathShared(ab, volumeManager));
assertTrue(Crostini.isPathShared(b, volumeManager));
crostini.unregisterSharedPath(a);
assertFalse(crostini.isPathShared(a));
assertFalse(crostini.isPathShared(aa));
assertFalse(crostini.isPathShared(ab));
assertTrue(crostini.isPathShared(b));
// Register root, collapses all.
Crostini.registerSharedPath(root, volumeManager);
assertTrue(Crostini.isPathShared(a, volumeManager));
assertTrue(Crostini.isPathShared(aa, volumeManager));
assertTrue(Crostini.isPathShared(ab, volumeManager));
assertTrue(Crostini.isPathShared(b, volumeManager));
crostini.registerSharedPath(root);
assertTrue(crostini.isPathShared(a));
assertTrue(crostini.isPathShared(aa));
assertTrue(crostini.isPathShared(ab));
assertTrue(crostini.isPathShared(b));
// Unregister root, all should be lost.
Crostini.unregisterSharedPath(root, volumeManager);
assertFalse(Crostini.isPathShared(a, volumeManager));
assertFalse(Crostini.isPathShared(aa, volumeManager));
assertFalse(Crostini.isPathShared(ab, volumeManager));
assertFalse(Crostini.isPathShared(b, volumeManager));
crostini.unregisterSharedPath(root);
assertFalse(crostini.isPathShared(a));
assertFalse(crostini.isPathShared(aa));
assertFalse(crostini.isPathShared(ab));
assertFalse(crostini.isPathShared(b));
}
function testCanSharePath() {
Crostini.IS_CROSTINI_FILES_ENABLED = true;
crostini.setEnabled(true);
const mockFileSystem = new MockFileSystem('test');
const root = new MockDirectoryEntry(mockFileSystem, '/');
......@@ -94,16 +97,16 @@ function testCanSharePath() {
disallowed.set('test', 'test');
for (let type of disallowed.keys()) {
volumeManagerRootType = type;
assertFalse(Crostini.canSharePath(root, true, volumeManager));
assertFalse(Crostini.canSharePath(root, false, volumeManager));
assertFalse(Crostini.canSharePath(rootFile, true, volumeManager));
assertFalse(Crostini.canSharePath(rootFile, false, volumeManager));
assertFalse(Crostini.canSharePath(rootFolder, true, volumeManager));
assertFalse(Crostini.canSharePath(rootFolder, false, volumeManager));
assertFalse(Crostini.canSharePath(fooFile, true, volumeManager));
assertFalse(Crostini.canSharePath(fooFile, false, volumeManager));
assertFalse(Crostini.canSharePath(fooFolder, true, volumeManager));
assertFalse(Crostini.canSharePath(fooFolder, false, volumeManager));
assertFalse(crostini.canSharePath(root, true));
assertFalse(crostini.canSharePath(root, false));
assertFalse(crostini.canSharePath(rootFile, true));
assertFalse(crostini.canSharePath(rootFile, false));
assertFalse(crostini.canSharePath(rootFolder, true));
assertFalse(crostini.canSharePath(rootFolder, false));
assertFalse(crostini.canSharePath(fooFile, true));
assertFalse(crostini.canSharePath(fooFile, false));
assertFalse(crostini.canSharePath(fooFolder, true));
assertFalse(crostini.canSharePath(fooFolder, false));
}
window.loadTimeData.data['DRIVE_FS_ENABLED'] = true;
......@@ -113,26 +116,15 @@ function testCanSharePath() {
]);
for (let type of allowed.keys()) {
volumeManagerRootType = type;
assertTrue(Crostini.canSharePath(root, true, volumeManager));
assertTrue(Crostini.canSharePath(root, false, volumeManager));
assertFalse(Crostini.canSharePath(rootFile, true, volumeManager));
assertTrue(Crostini.canSharePath(rootFile, false, volumeManager));
assertTrue(Crostini.canSharePath(rootFolder, true, volumeManager));
assertTrue(Crostini.canSharePath(rootFolder, false, volumeManager));
assertFalse(Crostini.canSharePath(fooFile, true, volumeManager));
assertTrue(Crostini.canSharePath(fooFile, false, volumeManager));
assertTrue(Crostini.canSharePath(fooFolder, true, volumeManager));
assertTrue(Crostini.canSharePath(fooFolder, false, volumeManager));
assertTrue(crostini.canSharePath(root, true));
assertTrue(crostini.canSharePath(root, false));
assertFalse(crostini.canSharePath(rootFile, true));
assertTrue(crostini.canSharePath(rootFile, false));
assertTrue(crostini.canSharePath(rootFolder, true));
assertTrue(crostini.canSharePath(rootFolder, false));
assertFalse(crostini.canSharePath(fooFile, true));
assertTrue(crostini.canSharePath(fooFile, false));
assertTrue(crostini.canSharePath(fooFolder, true));
assertTrue(crostini.canSharePath(fooFolder, false));
}
}
function task(id) {
return /** @type{!chrome.fileManagerPrivate.FileTask} */ ({taskId: id});
}
function testTaskRequiresSharing() {
assertTrue(Crostini.taskRequiresSharing(task('app|crostini|open-with')));
assertTrue(
Crostini.taskRequiresSharing(task('appId|x|install-linux-package')));
assertFalse(Crostini.taskRequiresSharing(task('appId|x|open-with')));
}
......@@ -28,7 +28,6 @@ js_type_check("closure_compile_module") {
":closure_compile_externs",
":column_visibility_controller",
":constants",
":crostini",
":dialog_action_controller",
":dialog_type",
":directory_contents",
......@@ -84,6 +83,7 @@ js_library("closure_compile_externs") {
"$externs_path/metrics_private.js",
"$externs_path/web_animations.js",
"../../../externs/app_window_common.js",
"../../../externs/background/crostini.js",
"../../../externs/background/drive_sync_handler.js",
"../../../externs/background/file_browser_background.js",
"../../../externs/background/file_browser_background_full.js",
......@@ -216,22 +216,6 @@ js_library("dialog_action_controller") {
]
}
js_library("crostini") {
deps = [
"../../common/js:metrics",
"//ui/file_manager/base/js:volume_manager_types",
"//ui/file_manager/externs:volume_manager",
]
}
js_unittest("crostini_unittest") {
deps = [
":crostini",
"../../common/js:mock_entry",
"//ui/webui/resources/js:webui_resource_test",
]
}
js_library("dialog_type") {
}
......@@ -376,7 +360,6 @@ js_library("file_selection") {
js_library("file_tasks") {
deps = [
":crostini",
":directory_model",
":task_history",
"metadata:metadata_model",
......@@ -683,7 +666,6 @@ js_library("webui_command_extender") {
js_unit_tests("unit_tests") {
deps = [
":crostini_unittest",
":thumbnail_loader_unittest",
]
}
......@@ -26,6 +26,9 @@ function FileManager() {
/** @private {importer.HistoryLoader} */
this.historyLoader_ = null;
/** @private {Crostini} */
this.crostini_ = null;
/**
* ImportHistory. Non-null only once history observer is added in
* {@code addHistoryObserver}.
......@@ -497,6 +500,12 @@ FileManager.prototype = /** @struct */ {
get historyLoader() {
return this.historyLoader_;
},
/**
* @return {Crostini}
*/
get crostini() {
return this.crostini_;
},
/**
* @return {importer.ImportRunner}
*/
......@@ -897,6 +906,7 @@ FileManager.prototype = /** @struct */ {
this.fileBrowserBackground_.mediaScanner;
this.historyLoader_ =
this.fileBrowserBackground_.historyLoader;
this.crostini_ = this.fileBrowserBackground_.crostini;
metrics.recordInterval('Load.InitBackgroundPage');
resolve();
}.bind(this));
......@@ -1139,13 +1149,9 @@ FileManager.prototype = /** @struct */ {
// Create task controller.
this.taskController_ = new TaskController(
this.dialogType,
this.volumeManager_,
this.ui_,
this.metadataModel_,
this.directoryModel_,
this.selectionHandler_,
this.metadataUpdateController_);
this.dialogType, this.volumeManager_, this.ui_, this.metadataModel_,
this.directoryModel_, this.selectionHandler_,
this.metadataUpdateController_, assert(this.crostini_));
// Create search controller.
this.searchController_ = new SearchController(
......@@ -1219,8 +1225,6 @@ FileManager.prototype = /** @struct */ {
this.getSourceRestriction_())) :
null);
chrome.fileManagerPrivate.onCrostiniSharedPathsChanged.addListener(
Crostini.onSharedPathsChanged.bind(null, assert(this.volumeManager_)));
this.setupCrostini_();
this.ui_.initDirectoryTree(directoryTree);
......@@ -1237,8 +1241,8 @@ FileManager.prototype = /** @struct */ {
FileManager.prototype.setupCrostini_ = function() {
chrome.fileManagerPrivate.isCrostiniEnabled((crostiniEnabled) => {
// Check for 'crostini-files' feature.
Crostini.IS_CROSTINI_FILES_ENABLED =
crostiniEnabled && loadTimeData.getBoolean('CROSTINI_FILES_ENABLED');
this.crostini_.setEnabled(
crostiniEnabled && loadTimeData.getBoolean('CROSTINI_FILES_ENABLED'));
// Setup Linux files fake root.
this.directoryTree.dataModel.linuxFilesItem = crostiniEnabled ?
......@@ -1258,7 +1262,7 @@ FileManager.prototype = /** @struct */ {
// Load any existing shared paths.
chrome.fileManagerPrivate.getCrostiniSharedPaths((entries) => {
for (let i = 0; i < entries.length; i++) {
Crostini.registerSharedPath(entries[i], assert(this.volumeManager_));
this.crostini_.registerSharedPath(entries[i]);
}
});
});
......
......@@ -1659,7 +1659,7 @@ CommandHandler.COMMANDS_['share-with-linux'] = /** @type {Command} */ ({
'Error sharing with linux: ' +
chrome.runtime.lastError.message);
} else {
Crostini.registerSharedPath(dir, fileManager.volumeManager);
fileManager.crostini.registerSharedPath(dir);
}
});
}
......@@ -1697,9 +1697,8 @@ CommandHandler.COMMANDS_['share-with-linux'] = /** @type {Command} */ ({
// Must be single directory subfolder of Downloads not already shared.
const entries = CommandUtil.getCommandEntries(event.target);
event.canExecute = entries.length === 1 && entries[0].isDirectory &&
!Crostini.isPathShared(entries[0], fileManager.volumeManager) &&
Crostini.canSharePath(
entries[0], true /* persist */, fileManager.volumeManager);
!fileManager.crostini.isPathShared(entries[0]) &&
fileManager.crostini.canSharePath(entries[0], true /* persist */);
event.command.setHidden(!event.canExecute);
}
});
......@@ -1724,7 +1723,7 @@ CommandHandler.COMMANDS_['manage-linux-sharing'] = /** @type {Command} */ ({
* @param {!CommandHandlerDeps} fileManager CommandHandlerDeps to use.
*/
canExecute: function(event, fileManager) {
event.canExecute = Crostini.IS_CROSTINI_FILES_ENABLED;
event.canExecute = fileManager.crostini.isEnabled();
event.command.setHidden(!event.canExecute);
}
});
......
......@@ -15,12 +15,13 @@
* @param {!Array<!chrome.fileManagerPrivate.FileTask>} tasks
* @param {chrome.fileManagerPrivate.FileTask} defaultTask
* @param {!TaskHistory} taskHistory
* @param {!Crostini} crostini
* @constructor
* @struct
*/
function FileTasks(
volumeManager, metadataModel, directoryModel, ui, entries, mimeTypes, tasks,
defaultTask, taskHistory) {
defaultTask, taskHistory, crostini) {
/**
* @private {!VolumeManager}
* @const
......@@ -74,6 +75,12 @@ function FileTasks(
* @const
*/
this.taskHistory_ = taskHistory;
/**
* @private {!Crostini}
* @const
*/
this.crostini_ = crostini;
}
FileTasks.prototype = {
......@@ -155,11 +162,12 @@ FileTasks.TaskPickerType = {
* @param {!Array<!Entry>} entries
* @param {!Array<?string>} mimeTypes
* @param {!TaskHistory} taskHistory
* @param {!Crostini} crostini
* @return {!Promise<!FileTasks>}
*/
FileTasks.create = function(
volumeManager, metadataModel, directoryModel, ui, entries, mimeTypes,
taskHistory) {
taskHistory, crostini) {
var tasksPromise = new Promise(function(fulfill) {
// getFileTasks supports only native entries.
entries = entries.filter(util.isNativeEntry);
......@@ -181,9 +189,8 @@ 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 ||
!(Crostini.isCrostiniEntry(entries[0], volumeManager) ||
Crostini.canSharePath(
entries[0], false /* persist */, volumeManager))) {
!(FileTasks.isCrostiniEntry(entries[0], volumeManager) ||
crostini.canSharePath(entries[0], false /* persist */))) {
taskItems = taskItems.filter(function(item) {
var taskParts = item.taskId.split('|');
var appId = taskParts[0];
......@@ -213,7 +220,7 @@ FileTasks.create = function(
return Promise.all([tasksPromise, defaultTaskPromise]).then(function(args) {
return new FileTasks(
volumeManager, metadataModel, directoryModel, ui, entries, mimeTypes,
args[0], args[1], taskHistory);
args[0], args[1], taskHistory, crostini);
});
};
......@@ -548,6 +555,28 @@ FileTasks.annotateTasks_ = function(tasks, entries) {
return result;
};
/**
* @param {!Entry} entry
* @param {!VolumeManager} volumeManager
* @return {boolean} True if the entry is from crostini.
*/
FileTasks.isCrostiniEntry = function(entry, volumeManager) {
return volumeManager.getLocationInfo(entry).rootType ===
VolumeManagerCommon.RootType.CROSTINI;
};
/**
* Returns true if task requires entries to be shared before executing task.
* @param {!chrome.fileManagerPrivate.FileTask} task Task to run.
* @return {boolean} true if task requires entries to be shared.
*/
FileTasks.taskRequiresCrostiniSharing = function(task) {
const taskParts = task.taskId.split('|');
const taskType = taskParts[1];
const actionId = taskParts[2];
return taskType === 'crostini' || actionId === 'install-linux-package';
};
/**
* Checks if task is a crostini task and all entries are accessible to, or can
* be shared with crostini. Shares files as required if possible and invokes
......@@ -560,7 +589,7 @@ FileTasks.annotateTasks_ = function(tasks, entries) {
FileTasks.prototype.maybeShareWithCrostiniOrShowDialog_ = function(
task, callback) {
// Check if this is a crostini task.
if (!Crostini.taskRequiresSharing(task))
if (!FileTasks.taskRequiresCrostiniSharing(task))
return callback();
let showUnableToOpen = false;
......@@ -568,12 +597,11 @@ FileTasks.prototype.maybeShareWithCrostiniOrShowDialog_ = function(
for (let i = 0; i < this.entries_.length; i++) {
const entry = this.entries_[i];
if (Crostini.isCrostiniEntry(entry, this.volumeManager_) ||
Crostini.isPathShared(entry, this.volumeManager_)) {
if (FileTasks.isCrostiniEntry(entry, this.volumeManager_) ||
this.crostini_.isPathShared(entry)) {
continue;
}
if (!Crostini.canSharePath(
entry, false /* persist */, this.volumeManager_)) {
if (!this.crostini_.canSharePath(entry, false /* persist */)) {
showUnableToOpen = true;
break;
}
......@@ -613,7 +641,7 @@ FileTasks.prototype.maybeShareWithCrostiniOrShowDialog_ = function(
}
// Register paths as shared, and now we are ready to execute.
entriesToShare.forEach((entry) => {
Crostini.registerSharedPath(entry, this.volumeManager_);
this.crostini_.registerSharedPath(entry);
});
callback();
});
......@@ -711,7 +739,7 @@ FileTasks.prototype.executeDefaultInternal_ = function(opt_callback) {
.create(
this.volumeManager_, this.metadataModel_,
this.directoryModel_, this.ui_, this.entries_,
this.mimeTypes_, this.taskHistory_)
this.mimeTypes_, this.taskHistory_, this.crostini_)
.then(
function(tasks) {
tasks.executeDefault();
......
......@@ -12,9 +12,9 @@
<script src="../../common/js/file_type.js"></script>
<script src="../../../base/js/test_error_reporting.js"></script>
<script src="../../common/js/util.js"></script>
<script src="../../background/js/crostini.js"></script>
<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>
......@@ -15,6 +15,7 @@ var mockTaskHistory = {
};
loadTimeData.data = {
DRIVE_FS_ENABLED: false,
MORE_ACTIONS_BUTTON_LABEL: 'MORE_ACTIONS_BUTTON_LABEL',
NO_TASK_FOR_EXECUTABLE: 'NO_TASK_FOR_EXECUTABLE',
NO_TASK_FOR_FILE_URL: 'NO_TASK_FOR_FILE_URL',
......@@ -62,7 +63,7 @@ function setUp() {
* @return {!FileManager}
*/
function getMockFileManager() {
return {
const result = {
volumeManager: {
getLocationInfo: function(entry) {
return {rootType: VolumeManagerCommon.RootType.DRIVE};
......@@ -77,15 +78,19 @@ function getMockFileManager() {
}
},
ui: {
alertDialog: {showHtml: function(title, text, onOk, onCancel, onShow) {}}
alertDialog:
{showHtml: function(title, text, onOk, onCancel, onShow) {}}
},
metadataModel: {},
directoryModel: {
getCurrentRootType: function() {
return null;
}
}
},
crostini: new Crostini(),
};
result.crostini.init(result.volumeManager);
return result;
}
/**
......@@ -111,7 +116,7 @@ function showHtmlOfAlertDialogIsCalled(entries, expectedTitle, expectedText) {
.create(
fileManager.volumeManager, fileManager.metadataModel,
fileManager.directoryModel, fileManager.ui, entries, [null],
mockTaskHistory)
mockTaskHistory, fileManager.crostini)
.then(function(tasks) {
tasks.executeDefault();
});
......@@ -138,7 +143,7 @@ function openSuggestAppsDialogIsCalled(entries, mimeTypes) {
.create(
fileManager.volumeManager, fileManager.metadataModel,
fileManager.directoryModel, fileManager.ui, entries, mimeTypes,
mockTaskHistory)
mockTaskHistory, fileManager.crostini)
.then(function(tasks) {
tasks.executeDefault();
});
......@@ -166,7 +171,7 @@ function showDefaultTaskDialogCalled(entries, mimeTypes) {
.create(
fileManager.volumeManager, fileManager.metadataModel,
fileManager.directoryModel, fileManager.ui, entries, mimeTypes,
mockTaskHistory)
mockTaskHistory, fileManager.crostini)
.then(function(tasks) {
tasks.executeDefault();
});
......@@ -231,7 +236,7 @@ function testOpenSuggestAppsDialogWithMetadata(callback) {
}
}
},
[entry], ['application/rtf'], mockTaskHistory)
[entry], ['application/rtf'], mockTaskHistory, fileManager.crostini)
.then(function(tasks) {
tasks.openSuggestAppsDialog(
function() {}, function() {}, function() {});
......@@ -256,7 +261,7 @@ function testOpenSuggestAppsDialogFailure(callback) {
.create(
fileManager.volumeManager, fileManager.metadataModel,
fileManager.directoryModel, fileManager.ui, [entry], [null],
mockTaskHistory)
mockTaskHistory, fileManager.crostini)
.then(function(tasks) {
tasks.openSuggestAppsDialog(function() {}, function() {}, resolve);
});
......@@ -361,7 +366,7 @@ function testOpenWithMostRecentlyExecuted(callback) {
.create(
fileManager.volumeManager, fileManager.metadataModel,
fileManager.directoryModel, fileManager.ui, [mockEntry], [null],
taskHistory)
taskHistory, fileManager.crostini)
.then(function(tasks) {
tasks.executeDefault();
assertEquals(latestTaskId, executedTask);
......@@ -426,7 +431,7 @@ function testOpenZipWithZipArchiver(callback) {
.create(
fileManager.volumeManager, fileManager.metadataModel,
fileManager.directoryModel, fileManager.ui, [mockEntry], [null],
taskHistory)
taskHistory, fileManager.crostini)
.then(function(tasks) {
tasks.executeDefault();
assertEquals(zipArchiverTaskId, executedTask);
......@@ -470,7 +475,7 @@ function testOpenInstallLinuxPackageDialog(callback) {
.create(
fileManager.volumeManager, fileManager.metadataModel,
fileManager.directoryModel, fileManager.ui, [mockEntry], [null],
mockTaskHistory)
mockTaskHistory, fileManager.crostini)
.then(function(tasks) {
tasks.executeDefault();
});
......@@ -487,7 +492,11 @@ function testMaybeShareCrostiniOrShowDialog() {
const mockFsDownloads = new MockFileSystem('downloads');
const sharedDir = new MockDirectoryEntry(mockFsDownloads, '/shared');
const shared = new MockFileEntry(mockFsDownloads, '/shared/file');
Crostini.registerSharedPath(sharedDir, volumeManagerDownloads);
const crostini = new Crostini();
crostini.init(volumeManagerDownloads);
crostini.setEnabled(true);
crostini.registerSharedPath(sharedDir, volumeManagerDownloads);
const notShared1 = new MockFileEntry(mockFsDownloads, '/notShared/file1');
const notShared2 = new MockFileEntry(mockFsDownloads, '/notShared/file2');
const otherNotShared =
......@@ -510,6 +519,7 @@ function testMaybeShareCrostiniOrShowDialog() {
}
const fakeFilesTask = {
entries_: entries,
crostini_: crostini,
ui_: {
alertDialog: {showHtml: showHtml},
confirmDialog: {showHtml: showHtml},
......@@ -530,12 +540,12 @@ function testMaybeShareCrostiniOrShowDialog() {
expect('No entries', [], true, '', '');
Crostini.IS_CROSTINI_FILES_ENABLED = false;
crostini.setEnabled(false);
expect(
'Single entry, crostini-files not enabled', [notShared1], false,
'UNABLE_TO_OPEN_CROSTINI_TITLE', 'UNABLE_TO_OPEN_CROSTINI');
Crostini.IS_CROSTINI_FILES_ENABLED = true;
crostini.setEnabled(true);
expect('Single entry, not shared', [notShared1], true, '', '');
......@@ -565,3 +575,15 @@ function testMaybeShareCrostiniOrShowDialog() {
'2 entries, 1 not sharable', [notShared1, unsharable], false,
'UNABLE_TO_OPEN_CROSTINI_TITLE', 'UNABLE_TO_OPEN_CROSTINI');
}
function task(id) {
return /** @type{!chrome.fileManagerPrivate.FileTask} */ ({taskId: id});
}
function testTaskRequiresCrostiniSharing() {
assertTrue(
FileTasks.taskRequiresCrostiniSharing(task('app|crostini|open-with')));
assertTrue(FileTasks.taskRequiresCrostiniSharing(
task('appId|x|install-linux-package')));
assertFalse(FileTasks.taskRequiresCrostiniSharing(task('appId|x|open-with')));
}
......@@ -108,7 +108,6 @@
// <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">
......
......@@ -10,12 +10,13 @@
* @param {!DirectoryModel} directoryModel
* @param {!FileSelectionHandler} selectionHandler
* @param {!MetadataUpdateController} metadataUpdateController
* @param {!Crostini} crostini
* @constructor
* @struct
*/
function TaskController(
dialogType, volumeManager, ui, metadataModel, directoryModel,
selectionHandler, metadataUpdateController) {
selectionHandler, metadataUpdateController, crostini) {
/**
* @private {DialogType}
* @const
......@@ -59,6 +60,13 @@ function TaskController(
*/
this.metadataUpdateController_ = metadataUpdateController;
/**
* @type {!Crostini}
* @const
* @private
*/
this.crostini_ = crostini;
/**
* @type {!TaskHistory}
* @const
......@@ -376,7 +384,7 @@ TaskController.prototype.getFileTasks = function() {
.create(
this.volumeManager_, this.metadataModel_, this.directoryModel_,
this.ui_, selection.entries, assert(selection.mimeTypes),
this.taskHistory_)
this.taskHistory_, this.crostini_)
.then(function(tasks) {
if (this.selectionHandler_.selection !== selection) {
if (util.isSameEntries(this.tasksEntries_, selection.entries))
......@@ -467,7 +475,7 @@ TaskController.prototype.executeEntryTask = function(entry) {
.create(
this.volumeManager_, this.metadataModel_, this.directoryModel_,
this.ui_, [entry], [props[0].contentMimeType || null],
this.taskHistory_)
this.taskHistory_, this.crostini_)
.then(function(tasks) {
tasks.executeDefault();
});
......
......@@ -20,7 +20,7 @@
<script src="../../../base/js/test_error_reporting.js"></script>
<script src="../../common/js/util.js"></script>
<script src="../../../base/js/volume_manager_types.js"></script>
<script src="crostini.js"></script>
<script src="../../background/js/crostini.js"></script>
<script src="dialog_type.js"></script>
<script src="file_selection.js"></script>
<script src="file_tasks.js"></script>
......
......@@ -2,6 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
window.loadTimeData = {
getBoolean: function(key) {
return false;
}
};
window.metrics = {
recordEnum: function() {}
};
......@@ -31,6 +37,16 @@ function setUp() {
cr.ui.decorate('command', cr.ui.Command);
}
function createCrostini() {
const crostini = new Crostini();
crostini.init({
getLocationInfo: () => {
return 'test';
}
});
return crostini;
}
function testExecuteEntryTask(callback) {
window.chrome.fileManagerPrivate = {
getFileTasks: function(entries, callback) {
......@@ -72,7 +88,7 @@ function testExecuteEntryTask(callback) {
return null;
}
},
new cr.EventTarget(), null);
new cr.EventTarget(), null, createCrostini());
controller.executeEntryTask(fileSystem.entries['/test.png']);
reportPromise(new Promise(function(fulfill) {
......@@ -141,7 +157,7 @@ function createTaskController(selectionHandler) {
return null;
}
},
selectionHandler, null);
selectionHandler, null, createCrostini());
}
// TaskController.getFileTasks should not call fileManagerPrivate.getFileTasks
......
......@@ -57,6 +57,7 @@ js_library("closure_compile_externs") {
"$externs_path/metrics_private.js",
"../../externs/app_window_common.js",
"../../externs/background/file_browser_background.js",
"../../externs/background/crostini.js",
"../../externs/entry_location.js",
"../../externs/volume_info.js",
"../../externs/volume_info_list.js",
......@@ -74,7 +75,6 @@ js_library("check_select") {
js_library("crostini_mount") {
deps = [
"../foreground/js:crostini",
"js:test_util",
"//ui/webui/resources/js:webui_resource_test",
]
......@@ -82,7 +82,6 @@ js_library("crostini_mount") {
js_library("crostini_share") {
deps = [
"../foreground/js:crostini",
"js:test_util",
"//ui/webui/resources/js:webui_resource_test",
]
......@@ -90,7 +89,6 @@ js_library("crostini_share") {
js_library("crostini_tasks") {
deps = [
"../foreground/js:crostini",
"js:test_util",
"//ui/webui/resources/js:webui_resource_test",
]
......
......@@ -28,11 +28,11 @@ crostiniShare.testSharePathsCrostiniSuccess = (done) => {
callback();
});
};
const oldCrostiniUnregister = Crostini.unregisterSharedPath;
const oldCrostiniUnregister = fileManager.crostini.unregisterSharedPath;
let unregisterCalled = false;
Crostini.unregisterSharedPath = function(entry, volumeManager) {
fileManager.crostini.unregisterSharedPath = function(entry) {
unregisterCalled = true;
oldCrostiniUnregister(entry, volumeManager);
oldCrostiniUnregister.call(fileManager.crostini, entry);
};
chrome.metricsPrivate.smallCounts_ = [];
chrome.metricsPrivate.values_ = [];
......@@ -112,8 +112,8 @@ crostiniShare.testSharePathsCrostiniSuccess = (done) => {
.then(() => {
// Restore fmp.*.
chrome.fileManagerPrivate.sharePathsWithCrostini = oldSharePaths;
// Restore Crostini.unregisterSharedPath;
Crostini.unregisterSharedPath = oldCrostiniUnregister;
// Restore Crostini.unregisterSharedPath.
fileManager.crostini.unregisterSharedPath = oldCrostiniUnregister;
done();
});
};
......@@ -159,7 +159,7 @@ crostiniShare.testSharePathShown = (done) => {
.getCurrentProfileVolumeInfo(
VolumeManagerCommon.VolumeType.DOWNLOADS)
.fileSystem.entries['/photos'];
Crostini.registerSharedPath(alreadySharedPhotosDir, mockVolumeManager);
fileManager.crostini.registerSharedPath(alreadySharedPhotosDir);
assertTrue(
test.fakeMouseRightClick('#file-list [file-name="photos"]'),
'right-click hello.txt');
......@@ -252,8 +252,7 @@ crostiniShare.testSharePathShown = (done) => {
// Unset DRIVE_FS_ENABLED.
loadTimeData.data_['DRIVE_FS_ENABLED'] = false;
// Clear Crostini shared folders.
Crostini.unregisterSharedPath(
alreadySharedPhotosDir, mockVolumeManager);
fileManager.crostini.unregisterSharedPath(alreadySharedPhotosDir);
done();
});
};
......@@ -270,13 +269,7 @@ crostiniShare.testGearMenuManageLinuxSharing = (done) => {
test.setupAndWaitUntilReady()
.then(() => {
// Setup with crostini disabled.
chrome.fileManagerPrivate.crostiniEnabled_ = false;
fileManager.setupCrostini_();
return test.repeatUntil(
() => !Crostini.IS_CROSTINI_FILES_ENABLED ||
test.pending('crostini setup'));
})
.then(() => {
fileManager.crostini.setEnabled(false);
// Click gear menu, ensure 'Manage Linux sharing' is hidden.
assertTrue(test.fakeMouseClick('#gear-button'));
return test.waitForElement(manageLinuxSharingOptionHidden);
......@@ -288,13 +281,7 @@ crostiniShare.testGearMenuManageLinuxSharing = (done) => {
})
.then(() => {
// Setup with crostini enabled.
chrome.fileManagerPrivate.crostiniEnabled_ = true;
fileManager.setupCrostini_();
return test.repeatUntil(
() => Crostini.IS_CROSTINI_FILES_ENABLED ||
test.pending('crostini setup'));
})
.then(() => {
fileManager.crostini.setEnabled(true);
// Click gear menu, ensure 'Manage Linux sharing' is shown.
assertTrue(test.fakeMouseClick('#gear-button'));
return test.waitForElement(manageLinuxSharingOptionShown);
......
......@@ -10,7 +10,10 @@
ChromeEvent.prototype.dispatchEvent = (var_args) => {};
/** @constructor */
function FileManager() {}
function FileManager() {
/** @type {Crostini} */
this.crostini;
}
FileManager.prototype.setupCrostini_ = () => {};
/** @type {string} */
......
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