[Files app] Simplified FileManager
Used Promises, async and await where it makes sense.
Removed the use of AsyncUtil.Queue from setupCurrentDirectory_().
Change-Id: I58281408c1bc3f6f1f9c6216d4862c88e393ef9a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1626838
Auto-Submit: François Degros <fdegros@chromium.org>
Reviewed-by: Luciano Pacheco <lucmult@chromium.org>
Commit-Queue: François Degros <fdegros@chromium.org>
Cr-Commit-Position: refs/heads/master@{#663449}
diff --git a/ui/file_manager/file_manager/foreground/js/elements_importer.js b/ui/file_manager/file_manager/foreground/js/elements_importer.js
index 4b645282..e411885 100644
--- a/ui/file_manager/file_manager/foreground/js/elements_importer.js
+++ b/ui/file_manager/file_manager/foreground/js/elements_importer.js
@@ -3,8 +3,8 @@
// found in the LICENSE file.
/**
- * @type {Promise} A promise which is fulfilled when HTML imports for custom
- * elements for file manager UI are loaded.
+ * @type {!Promise<void>} A promise which is fulfilled when HTML imports for
+ * custom elements for file manager UI are loaded.
*/
window.importElementsPromise = new Promise((resolve, reject) => {
const startTime = Date.now();
diff --git a/ui/file_manager/file_manager/foreground/js/file_manager.js b/ui/file_manager/file_manager/foreground/js/file_manager.js
index 93c2fd3..5302055 100644
--- a/ui/file_manager/file_manager/foreground/js/file_manager.js
+++ b/ui/file_manager/file_manager/foreground/js/file_manager.js
@@ -134,7 +134,7 @@
/**
* Dialog type of this window.
- * @type {DialogType}
+ * @public {DialogType}
*/
this.dialogType = DialogType.FULL_PAGE;
@@ -294,14 +294,14 @@
/**
* Promise object which is fulfilled when initialization for app state
* controller is done.
- * @private {?Promise}
+ * @private {?Promise<void>}
*/
this.initSettingsPromise_ = null;
/**
* Promise object which is fulfilled when initialization related to the
* background page is done.
- * @private {?Promise}
+ * @private {?Promise<void>}
*/
this.initBackgroundPagePromise_ = null;
@@ -489,19 +489,14 @@
/**
* One time initialization for app state controller to load view option from
* local storage.
- * @return {!Promise} A promise to be fillfilled when initialization is done.
+ * @return {!Promise<void>}
* @private
*/
- startInitSettings_() {
+ async startInitSettings_() {
metrics.startInterval('Load.InitSettings');
this.appStateController_ = new AppStateController(this.dialogType);
- return Promise
- .all([
- this.appStateController_.loadInitialViewOptions(),
- ])
- .then(values => {
- metrics.recordInterval('Load.InitSettings');
- });
+ await this.appStateController_.loadInitialViewOptions();
+ metrics.recordInterval('Load.InitSettings');
}
/**
@@ -632,7 +627,7 @@
this.ui_.selectionMenuButton.hidden = false;
- console.warn('Files app sync startup finished.');
+ console.warn('Files app sync startup finished');
}
/**
@@ -749,47 +744,41 @@
this.initGeneral_();
this.initSettingsPromise_ = this.startInitSettings_();
this.initBackgroundPagePromise_ = this.startInitBackgroundPage_();
- this.initBackgroundPagePromise_.then(() => {
- this.initVolumeManager_();
- });
+ this.initBackgroundPagePromise_.then(() => this.initVolumeManager_());
window.addEventListener('pagehide', this.onUnload_.bind(this));
}
/**
- * @return {!Promise} A promise to be fillfilled when initialization is done.
+ * @return {!Promise<void>}
*/
- initializeUI(dialogDom) {
+ async initializeUI(dialogDom) {
this.dialogDom_ = dialogDom;
this.document_ = this.dialogDom_.ownerDocument;
metrics.startInterval('Load.InitDocuments');
- return Promise
- .all([this.initBackgroundPagePromise_, window.importElementsPromise])
- .then(() => {
- metrics.recordInterval('Load.InitDocuments');
- metrics.startInterval('Load.InitUI');
- this.initEssentialUI_();
- this.initAdditionalUI_();
- return this.initSettingsPromise_;
- })
- .then(() => {
- this.initFileSystemUI_();
- this.initUIFocus_();
- metrics.recordInterval('Load.InitUI');
- });
+ await Promise.all(
+ [this.initBackgroundPagePromise_, window.importElementsPromise]);
+ metrics.recordInterval('Load.InitDocuments');
+
+ metrics.startInterval('Load.InitUI');
+ this.initEssentialUI_();
+ this.initAdditionalUI_();
+ await this.initSettingsPromise_;
+ this.initFileSystemUI_();
+ this.initUIFocus_();
+ metrics.recordInterval('Load.InitUI');
}
/**
* Initializes general purpose basic things, which are used by other
* initializing methods.
- *
* @private
*/
initGeneral_() {
// Initialize the application state.
// TODO(mtomasz): Unify window.appState with location.search format.
- console.warn('Files app starting up.');
+ console.warn('Files app starting up');
if (window.appState) {
const params = {};
for (let name in window.appState) {
@@ -813,37 +802,34 @@
/**
* Initializes the background page.
- * @return {!Promise} A promise to be fillfilled when initialization is done.
+ * @return {!Promise<void>}
* @private
*/
- startInitBackgroundPage_() {
- return new Promise(resolve => {
- metrics.startInterval('Load.InitBackgroundPage');
- chrome.runtime.getBackgroundPage(
- /** @type {function(Window=)} */ (opt_backgroundPage => {
- assert(opt_backgroundPage);
- this.backgroundPage_ =
- /** @type {!BackgroundWindow} */ (opt_backgroundPage);
- this.fileBrowserBackground_ =
- /** @type {!FileBrowserBackgroundFull} */ (
- this.backgroundPage_.background);
- this.fileBrowserBackground_.ready(() => {
- loadTimeData.data = this.fileBrowserBackground_.stringData;
- if (util.runningInBrowser()) {
- this.backgroundPage_.registerDialog(window);
- }
- this.fileOperationManager_ =
- this.fileBrowserBackground_.fileOperationManager;
- this.mediaImportHandler_ =
- this.fileBrowserBackground_.mediaImportHandler;
- this.mediaScanner_ = this.fileBrowserBackground_.mediaScanner;
- this.historyLoader_ = this.fileBrowserBackground_.historyLoader;
- this.crostini_ = this.fileBrowserBackground_.crostini;
- metrics.recordInterval('Load.InitBackgroundPage');
- resolve();
- });
- }));
- });
+ async startInitBackgroundPage_() {
+ metrics.startInterval('Load.InitBackgroundPage');
+
+ /** @type {!Window} */
+ const backgroundPage =
+ await new Promise(resolve => chrome.runtime.getBackgroundPage(resolve));
+ assert(backgroundPage);
+ this.backgroundPage_ =
+ /** @type {!BackgroundWindow} */ (backgroundPage);
+ this.fileBrowserBackground_ =
+ /** @type {!FileBrowserBackgroundFull} */ (
+ this.backgroundPage_.background);
+
+ await new Promise(resolve => this.fileBrowserBackground_.ready(resolve));
+ loadTimeData.data = this.fileBrowserBackground_.stringData;
+ if (util.runningInBrowser()) {
+ this.backgroundPage_.registerDialog(window);
+ }
+ this.fileOperationManager_ =
+ this.fileBrowserBackground_.fileOperationManager;
+ this.mediaImportHandler_ = this.fileBrowserBackground_.mediaImportHandler;
+ this.mediaScanner_ = this.fileBrowserBackground_.mediaScanner;
+ this.historyLoader_ = this.fileBrowserBackground_.historyLoader;
+ this.crostini_ = this.fileBrowserBackground_.crostini;
+ metrics.recordInterval('Load.InitBackgroundPage');
}
/**
@@ -1145,7 +1131,7 @@
* Setup crostini 'Linux files'.
* @private
*/
- setupCrostini_() {
+ async setupCrostini_() {
// Setup Linux files fake root.
this.directoryTree.dataModel.linuxFilesItem =
this.crostini_.isEnabled(constants.DEFAULT_CROSTINI_VM) ?
@@ -1193,27 +1179,24 @@
});
};
- Promise
- .all([
- getSharedPaths(constants.DEFAULT_CROSTINI_VM),
- getSharedPaths(constants.PLUGIN_VM)
- ])
- .then(([crostiniShareCount, pluginVmShareCount]) => {
- toast(
- crostiniShareCount, 'FOLDER_SHARED_WITH_CROSTINI',
- 'FOLDER_SHARED_WITH_CROSTINI_PLURAL',
- 'MANAGE_LINUX_SHARING_BUTTON_LABEL', 'crostini/sharedPaths',
- CommandHandler.MenuCommandsForUMA
- .MANAGE_LINUX_SHARING_TOAST_STARTUP);
- // TODO(crbug.com/949356): UX to provide guidance for what to do
- // when we have shared paths with both Linux and Plugin VM.
- toast(
- pluginVmShareCount, 'FOLDER_SHARED_WITH_PLUGIN_VM',
- 'FOLDER_SHARED_WITH_PLUGIN_VM_PLURAL',
- 'MANAGE_PLUGIN_VM_SHARING_BUTTON_LABEL', 'pluginVm/sharedPaths',
- CommandHandler.MenuCommandsForUMA
- .MANAGE_PLUGIN_VM_SHARING_TOAST_STARTUP);
- });
+ const [crostiniShareCount, pluginVmShareCount] = await Promise.all([
+ getSharedPaths(constants.DEFAULT_CROSTINI_VM),
+ getSharedPaths(constants.PLUGIN_VM)
+ ]);
+
+ toast(
+ crostiniShareCount, 'FOLDER_SHARED_WITH_CROSTINI',
+ 'FOLDER_SHARED_WITH_CROSTINI_PLURAL',
+ 'MANAGE_LINUX_SHARING_BUTTON_LABEL', 'crostini/sharedPaths',
+ CommandHandler.MenuCommandsForUMA.MANAGE_LINUX_SHARING_TOAST_STARTUP);
+ // TODO(crbug.com/949356): UX to provide guidance for what to do
+ // when we have shared paths with both Linux and Plugin VM.
+ toast(
+ pluginVmShareCount, 'FOLDER_SHARED_WITH_PLUGIN_VM',
+ 'FOLDER_SHARED_WITH_PLUGIN_VM_PLURAL',
+ 'MANAGE_PLUGIN_VM_SHARING_BUTTON_LABEL', 'pluginVm/sharedPaths',
+ CommandHandler.MenuCommandsForUMA
+ .MANAGE_PLUGIN_VM_SHARING_TOAST_STARTUP);
}
/**
@@ -1234,205 +1217,161 @@
* Sets up the current directory during initialization.
* @private
*/
- setupCurrentDirectory_() {
+ async setupCurrentDirectory_() {
const tracker = this.directoryModel_.createDirectoryChangeTracker();
- const queue = new AsyncUtil.Queue();
+ tracker.start();
// Wait until the volume manager is initialized.
- queue.run((callback) => {
- tracker.start();
- this.volumeManager_.ensureInitialized(callback);
- });
+ await new Promise(
+ resolve => this.volumeManager_.ensureInitialized(resolve));
let nextCurrentDirEntry;
let selectionEntry;
- // Resolve the selectionURL to selectionEntry or to currentDirectoryEntry
- // in case of being a display root or a default directory to open files.
- queue.run((callback) => {
- if (!this.launchParams_.selectionURL) {
- callback();
- return;
- }
-
- window.webkitResolveLocalFileSystemURL(
- this.launchParams_.selectionURL, (inEntry) => {
- const locationInfo = this.volumeManager_.getLocationInfo(inEntry);
- // If location information is not available, then the volume is
- // no longer (or never) available.
- if (!locationInfo) {
- callback();
- return;
- }
- // If the selection is root, then use it as a current directory
- // instead. This is because, selecting a root entry is done as
- // opening it.
- if (locationInfo.isRootEntry) {
- nextCurrentDirEntry = inEntry;
- }
-
- // If this dialog attempts to open file(s) and the selection is a
- // directory, the selection should be the current directory.
- if (DialogType.isOpenFileDialog(this.dialogType) &&
- inEntry.isDirectory) {
- nextCurrentDirEntry = inEntry;
- }
-
- // By default, the selection should be selected entry and the
- // parent directory of it should be the current directory.
- if (!nextCurrentDirEntry) {
- selectionEntry = inEntry;
- }
-
- callback();
- }, callback);
- });
- // Resolve the currentDirectoryURL to currentDirectoryEntry (if not done
- // by the previous step).
- queue.run((callback) => {
- if (nextCurrentDirEntry || !this.launchParams_.currentDirectoryURL) {
- callback();
- return;
- }
-
- window.webkitResolveLocalFileSystemURL(
- this.launchParams_.currentDirectoryURL, (inEntry) => {
- const locationInfo = this.volumeManager_.getLocationInfo(inEntry);
- if (!locationInfo) {
- callback();
- return;
- }
+ // Resolve the selectionURL to selectionEntry or to currentDirectoryEntry in
+ // case of being a display root or a default directory to open files.
+ if (this.launchParams_.selectionURL) {
+ try {
+ const inEntry = await new Promise((resolve, reject) => {
+ window.webkitResolveLocalFileSystemURL(
+ this.launchParams_.selectionURL, resolve, reject);
+ });
+ const locationInfo = this.volumeManager_.getLocationInfo(inEntry);
+ // If location information is not available, then the volume is no
+ // longer (or never) available.
+ if (locationInfo) {
+ // If the selection is root, then use it as a current directory
+ // instead. This is because, selecting a root entry is done as opening
+ // it.
+ if (locationInfo.isRootEntry) {
nextCurrentDirEntry = inEntry;
- callback();
- }, callback);
- });
+ }
+
+ // If this dialog attempts to open file(s) and the selection is a
+ // directory, the selection should be the current directory.
+ if (DialogType.isOpenFileDialog(this.dialogType) &&
+ inEntry.isDirectory) {
+ nextCurrentDirEntry = inEntry;
+ }
+
+ // By default, the selection should be selected entry and the parent
+ // directory of it should be the current directory.
+ if (!nextCurrentDirEntry) {
+ selectionEntry = inEntry;
+ }
+ }
+ } catch (error) {
+ console.warn(error.stack || error);
+ }
+ }
+
+ // Resolve the currentDirectoryURL to currentDirectoryEntry (if not done by
+ // the previous step).
+ if (!nextCurrentDirEntry && this.launchParams_.currentDirectoryURL) {
+ try {
+ const inEntry = await new Promise((resolve, reject) => {
+ window.webkitResolveLocalFileSystemURL(
+ this.launchParams_.currentDirectoryURL, resolve, reject);
+ });
+ const locationInfo = this.volumeManager_.getLocationInfo(inEntry);
+ if (locationInfo) {
+ nextCurrentDirEntry = inEntry;
+ }
+ } catch (error) {
+ console.warn(error.stack || error);
+ }
+ }
// If the directory to be changed to is not available, then first fallback
// to the parent of the selection entry.
- queue.run((callback) => {
- if (nextCurrentDirEntry || !selectionEntry) {
- callback();
- return;
- }
- selectionEntry.getParent((inEntry) => {
- nextCurrentDirEntry = inEntry;
- callback();
+ if (!nextCurrentDirEntry && selectionEntry) {
+ nextCurrentDirEntry = await new Promise(resolve => {
+ selectionEntry.getParent(resolve);
});
- });
+ }
// Check if the next current directory is not a virtual directory which is
// not available in UI. This may happen to shared on Drive.
- queue.run((callback) => {
- if (!nextCurrentDirEntry) {
- callback();
- return;
- }
+ if (nextCurrentDirEntry) {
const locationInfo =
this.volumeManager_.getLocationInfo(nextCurrentDirEntry);
// If we can't check, assume that the directory is illegal.
if (!locationInfo) {
nextCurrentDirEntry = null;
- callback();
- return;
- }
- // Having root directory of DRIVE_OTHER here should be only for shared
- // with me files. Fallback to Drive root in such case.
- if (locationInfo.isRootEntry &&
- locationInfo.rootType === VolumeManagerCommon.RootType.DRIVE_OTHER) {
- const volumeInfo =
- this.volumeManager_.getVolumeInfo(nextCurrentDirEntry);
- if (!volumeInfo) {
- nextCurrentDirEntry = null;
- callback();
- return;
- }
- volumeInfo.resolveDisplayRoot()
- .then((entry) => {
- nextCurrentDirEntry = entry;
- callback();
- })
- .catch((error) => {
+ } else {
+ // Having root directory of DRIVE_OTHER here should be only for shared
+ // with me files. Fallback to Drive root in such case.
+ if (locationInfo.isRootEntry &&
+ locationInfo.rootType ===
+ VolumeManagerCommon.RootType.DRIVE_OTHER) {
+ const volumeInfo =
+ this.volumeManager_.getVolumeInfo(nextCurrentDirEntry);
+ if (!volumeInfo) {
+ nextCurrentDirEntry = null;
+ } else {
+ try {
+ nextCurrentDirEntry = await volumeInfo.resolveDisplayRoot();
+ } catch (error) {
console.error(error.stack || error);
nextCurrentDirEntry = null;
- callback();
- });
- } else {
- callback();
+ }
+ }
+ }
}
- });
+ }
- // If the directory to be changed to is still not resolved, then fallback
- // to the default display root.
- queue.run((callback) => {
- if (nextCurrentDirEntry) {
- callback();
- return;
- }
- this.volumeManager_.getDefaultDisplayRoot((displayRoot) => {
- nextCurrentDirEntry = displayRoot;
- callback();
+ // If the directory to be changed to is still not resolved, then fallback to
+ // the default display root.
+ if (!nextCurrentDirEntry) {
+ nextCurrentDirEntry = await new Promise(resolve => {
+ this.volumeManager_.getDefaultDisplayRoot(resolve);
});
- });
+ }
- // If selection failed to be resolved (eg. didn't exist, in case of saving
- // a file, or in case of a fallback of the current directory, then try to
+ // If selection failed to be resolved (eg. didn't exist, in case of saving a
+ // file, or in case of a fallback of the current directory, then try to
// resolve again using the target name.
- queue.run((callback) => {
- if (selectionEntry || !nextCurrentDirEntry ||
- !this.launchParams_.targetName) {
- callback();
- return;
- }
+ if (!selectionEntry && nextCurrentDirEntry &&
+ this.launchParams_.targetName) {
// Try to resolve as a file first. If it fails, then as a directory.
- nextCurrentDirEntry.getFile(
- this.launchParams_.targetName, {},
- (targetEntry) => {
- selectionEntry = targetEntry;
- callback();
- },
- () => {
- // Failed to resolve as a file
+ try {
+ selectionEntry = await new Promise((resolve, reject) => {
+ nextCurrentDirEntry.getFile(
+ this.launchParams_.targetName, {}, resolve, reject);
+ });
+ } catch (error1) {
+ // Failed to resolve as a file. Try to resolve as a directory.
+ try {
+ selectionEntry = await new Promise((resolve, reject) => {
nextCurrentDirEntry.getDirectory(
- this.launchParams_.targetName, {},
- (targetEntry) => {
- selectionEntry = targetEntry;
- callback();
- },
- () => {
- // Failed to resolve as either file or directory.
- callback();
- });
+ this.launchParams_.targetName, {}, resolve, reject);
});
- });
+ } catch (error2) {
+ // Failed to resolve as either file or directory.
+ console.error(error1.stack || error1);
+ console.error(error2.stack || error2);
+ }
+ }
+ }
// If there is no target select MyFiles by default.
- queue.run((callback) => {
- if (!nextCurrentDirEntry && this.directoryTree.dataModel.myFilesModel_) {
- nextCurrentDirEntry = this.directoryTree.dataModel.myFilesModel_.entry;
- }
+ if (!nextCurrentDirEntry && this.directoryTree.dataModel.myFilesModel_) {
+ nextCurrentDirEntry = this.directoryTree.dataModel.myFilesModel_.entry;
+ }
- callback();
- });
-
- // Finalize.
- queue.run((callback) => {
- // Check directory change.
- tracker.stop();
- if (tracker.hasChanged) {
- callback();
- return;
- }
+ // Check directory change.
+ tracker.stop();
+ if (!tracker.hasChanged) {
// Finish setup current directory.
this.finishSetupCurrentDirectory_(
nextCurrentDirEntry, selectionEntry, this.launchParams_.targetName);
- callback();
- });
+ }
}
/**
- * @param {DirectoryEntry} directoryEntry Directory to be opened.
+ * @param {?DirectoryEntry} directoryEntry Directory to be opened.
* @param {Entry=} opt_selectionEntry Entry to be selected.
- * @param {string=} opt_suggestedName Suggested name for a non-existing\
+ * @param {string=} opt_suggestedName Suggested name for a non-existing
* selection.
* @private
*/
@@ -1442,14 +1381,13 @@
if (directoryEntry) {
const entryDescription = util.entryDebugString(directoryEntry);
console.warn(
- 'Files app start up: changing to directory: ' + entryDescription);
+ `Files app start up: Changing to directory: ${entryDescription}`);
this.directoryModel_.changeDirectoryEntry(directoryEntry, () => {
if (opt_selectionEntry) {
this.directoryModel_.selectEntry(opt_selectionEntry);
}
console.warn(
- 'Files app start up: finished changing to directory: ' +
- entryDescription);
+ `Files app start up: Changed to directory: ${entryDescription}`);
this.ui_.addLoadedAttribute();
});
} else {