this.currentVolumeInfo_ = null;
}
-/**
- * Maximum delay in milliseconds for updating thumbnails in the bottom panel
- * to mitigate flickering. If images load faster then the delay they replace
- * old images smoothly. On the other hand we don't want to keep old images
- * too long.
- *
- * @type {number}
- * @const
- */
-FileManager.THUMBNAIL_SHOW_DELAY = 100;
-
FileManager.prototype = {
__proto__: cr.EventTarget.prototype,
get directoryModel() {
},
get volumeManager() {
return this.volumeManager_;
+ },
+ get ui() {
+ return this.ui_;
}
};
}.bind(this));
}.bind(this));
- // TODO(yoshiki): Remove the flag when the feature is launched.
- this.enableExperimentalWebstoreIntegration_ = true;
-
group.run(callback);
};
* @private
*/
FileManager.prototype.initDataTransferOperations_ = function() {
- this.fileOperationManager_ = FileOperationManagerWrapper.getInstance(
- this.backgroundPage_);
+ this.fileOperationManager_ =
+ this.backgroundPage_.background.fileOperationManager;
// CopyManager are required for 'Delete' operation in
// Open and Save dialogs. But drag-n-drop and copy-paste are not needed.
this.blinkSelection.bind(this));
controller.addEventListener('selection-cut',
this.blinkSelection.bind(this));
+ controller.addEventListener('source-not-found',
+ this.onSourceNotFound_.bind(this));
+ };
+
+ /**
+ * Handles an error that the source entry of file operation is not found.
+ * @private
+ */
+ FileManager.prototype.onSourceNotFound_ = function(event) {
+ // Ensure this.sourceNotFoundErrorCount_ is integer.
+ this.sourceNotFoundErrorCount_ = ~~this.sourceNotFoundErrorCount_;
+ var item = new ProgressCenterItem();
+ item.id = 'source-not-found-' + this.sourceNotFoundErrorCount_;
+ if (event.progressType === ProgressItemType.COPY)
+ item.message = strf('COPY_SOURCE_NOT_FOUND_ERROR', event.fileName);
+ else if (event.progressType === ProgressItemType.MOVE)
+ item.message = strf('MOVE_SOURCE_NOT_FOUND_ERROR', event.fileName);
+ item.state = ProgressItemState.ERROR;
+ this.backgroundPage_.background.progressCenter.updateItem(item);
+ this.sourceNotFoundErrorCount_++;
};
/**
this.gearButton_.addEventListener('menushow',
this.refreshRemainingSpace_.bind(this,
false /* Without loading caption. */));
- this.gearButton_.addEventListener(
- 'menushow',
+ chrome.fileBrowserPrivate.onDesktopChanged.addListener(function() {
+ this.updateVisitDesktopMenus_();
+ this.ui_.updateProfileBadge();
+ }.bind(this));
+ chrome.fileBrowserPrivate.onProfileAdded.addListener(
this.updateVisitDesktopMenus_.bind(this));
+ this.updateVisitDesktopMenus_();
+
this.dialogDom_.querySelector('#gear-menu').menuItemSelector =
'menuitem, hr';
cr.ui.decorate(this.gearButton_, cr.ui.MenuButton);
};
FileManager.prototype.onMaximize = function() {
- this.document_.activeElement.blur();
var appWindow = chrome.app.window.current();
if (appWindow.isMaximized())
appWindow.restore();
*/
FileManager.prototype.initGeneral_ = function(callback) {
// Initialize the application state.
+ // TODO(mtomasz): Unify window.appState with location.search format.
if (window.appState) {
this.params_ = window.appState.params || {};
- this.defaultPath = window.appState.defaultPath;
+ this.initCurrentDirectoryURL_ = window.appState.currentDirectoryURL;
+ this.initSelectionURL_ = window.appState.selectionURL;
+ this.initTargetName_ = window.appState.targetName;
} else {
+ // Used by the select dialog only.
this.params_ = location.search ?
JSON.parse(decodeURIComponent(location.search.substr(1))) :
{};
- this.defaultPath = this.params_.defaultPath;
+ this.initCurrentDirectoryURL_ = this.params_.currentDirectoryURL;
+ this.initSelectionURL_ = this.params_.selectionURL;
+ this.initTargetName_ = this.params_.targetName;
}
// Initialize the member variables that depend this.params_.
* @private
*/
FileManager.prototype.initEssentialUI_ = function(callback) {
- // Optional list of file types.
+ // Record stats of dialog types. New values must NOT be inserted into the
+ // array enumerating the types. It must be in sync with
+ // FileDialogType enum in tools/metrics/histograms/histogram.xml.
metrics.recordEnum('Create', this.dialogType,
[DialogType.SELECT_FOLDER,
DialogType.SELECT_UPLOAD_FOLDER,
this.updateStartupPrefs_.bind(this));
// Restore preferences.
- this.directoryModel_.sortFileList(
+ this.directoryModel_.getFileList().sort(
this.viewOptions_.sortField || 'modificationTime',
this.viewOptions_.sortDirection || 'desc');
if (this.viewOptions_.columns) {
};
/**
- * Restores current directory and may be a selected item after page load (or
- * reload) or popping a state (after click on back/forward). defaultPath
- * primarily is used with save/open dialogs.
- * Default path may also contain a file name. Freshly opened file manager
- * window has neither.
- *
+ * Sets up the current directory during initialization.
* @private
*/
FileManager.prototype.setupCurrentDirectory_ = function() {
this.volumeManager_.ensureInitialized(callback);
}.bind(this));
- // Obtains the fallback path.
- var defaultDisplayRoot;
+ var nextCurrentDirEntry;
+ var selectionEntry;
+
+ // Resolve the selectionURL to selectionEntry or to currentDirectoryEntry
+ // in case of being a display root.
queue.run(function(callback) {
- this.volumeManager_.getDefaultDisplayRoot(function(displayRoot) {
- defaultDisplayRoot = displayRoot;
+ if (!this.initSelectionURL_) {
callback();
- }.bind(this));
+ return;
+ }
+ webkitResolveLocalFileSystemURL(
+ this.initSelectionURL_,
+ function(inEntry) {
+ var 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;
+ else
+ selectionEntry = inEntry;
+ callback();
+ }.bind(this), callback);
}.bind(this));
-
- // Resolve the default path.
- var defaultFullPath;
- var candidateFullPath;
- var candidateEntry;
+ // Resolve the currentDirectoryURL to currentDirectoryEntry (if not done
+ // by the previous step).
queue.run(function(callback) {
- // Cancel this sequence if the current directory has already changed.
- if (tracker.hasChanged) {
+ if (nextCurrentDirEntry || !this.initCurrentDirectoryURL_) {
callback();
return;
}
-
- // Resolve the absolute path in case only the file name or an empty string
- // is passed.
- if (!this.defaultPath) {
- // TODO(mtomasz): that in this case we can directly jump to #1540
- // and avoid fullPath conversion -> Entry.
- defaultFullPath = defaultDisplayRoot.fullPath;
- } else if (this.defaultPath.indexOf('/') === -1) {
- // Path is a file name.
- defaultFullPath = defaultDisplayRoot.fullPath + '/' + this.defaultPath;
- } else {
- defaultFullPath = this.defaultPath;
- }
-
- // If Drive is disabled but the path points to Drive's entry, fallback to
- // defaultDisplayRootPath.
- if (PathUtil.isDriveBasedPath(defaultFullPath) &&
- !this.volumeManager_.getVolumeInfo(RootDirectory.DRIVE)) {
- candidateFullPath = defaultDisplayRoot.fullPath + '/' +
- PathUtil.basename(defaultFullPath);
- } else {
- candidateFullPath = defaultFullPath;
- }
-
- // If the path points a fake entry, use the entry directly.
- // TODO(hirono): Obtains proper volume.
- var volumeInfo = this.volumeManager_.getCurrentProfileVolumeInfo(
- util.VolumeType.DRIVE);
- if (volumeInfo) {
- for (var name in volumeInfo.fakeEntries) {
- var fakeEntry = volumeInfo.fakeEntries[name];
- // Skip the drive root fake entry, because we can need actual drive
- // root to list its files.
- if (fakeEntry.rootType === RootType.DRIVE)
- continue;
- if (candidateFullPath === fakeEntry.fullPath) {
- candidateEntry = fakeEntry;
+ webkitResolveLocalFileSystemURL(
+ this.initCurrentDirectoryURL_,
+ function(inEntry) {
+ var locationInfo = this.volumeManager_.getLocationInfo(inEntry);
+ if (!locationInfo) {
+ callback();
+ return;
+ }
+ nextCurrentDirEntry = inEntry;
callback();
- return;
- }
- }
- }
+ }.bind(this), callback);
+ // TODO(mtomasz): Implement reopening on special search, when fake
+ // entries are converted to directory providers.
+ }.bind(this));
- // Convert the path to the directory entry and an optional selection
- // entry.
- // TODO(hirono): There may be a race here. The path on Drive, may not
- // be available yet.
- this.volumeManager_.resolveAbsolutePath(candidateFullPath,
- function(inEntry) {
- candidateEntry = inEntry;
+ // If the directory to be changed to is not available, then first fallback
+ // to the parent of the selection entry.
+ queue.run(function(callback) {
+ if (nextCurrentDirEntry || !selectionEntry) {
callback();
- }, function() {
+ return;
+ }
+ selectionEntry.getParent(function(inEntry) {
+ nextCurrentDirEntry = inEntry;
callback();
- });
+ }.bind(this));
}.bind(this));
- // Check the obtained entry.
- var nextCurrentDirEntry;
- var selectionEntry = null;
- var suggestedName = null;
- var error = null;
+ // If the directory to be changed to is still not resolved, then fallback
+ // to the default display root.
queue.run(function(callback) {
- // Cancel this sequence if the current directory has already changed.
- if (tracker.hasChanged) {
+ if (nextCurrentDirEntry) {
callback();
return;
}
+ this.volumeManager_.getDefaultDisplayRoot(function(displayRoot) {
+ nextCurrentDirEntry = displayRoot;
+ callback();
+ }.bind(this));
+ }.bind(this));
- if (candidateEntry) {
- // The entry is directory. Use it.
- if (candidateEntry.isDirectory) {
- nextCurrentDirEntry = candidateEntry;
- callback();
- return;
- }
- // The entry exists, but it is not a directory. Therefore use a
- // parent.
- candidateEntry.getParent(function(parentEntry) {
- nextCurrentDirEntry = parentEntry;
- selectionEntry = candidateEntry;
- callback();
- }, function() {
- error = new Error('Unable to resolve parent for: ' +
- candidateEntry.fullPath);
- callback();
- });
+ // 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(function(callback) {
+ if (selectionEntry || !nextCurrentDirEntry || !this.initTargetName_) {
+ callback();
return;
}
-
- // If the entry doesn't exist, most probably because the path contains a
- // suggested name. Therefore try to open its parent. However, the parent
- // may also not exist. In such situation, fallback.
- var pathNodes = candidateFullPath.split('/');
- var baseName = pathNodes.pop();
- var parentPath = pathNodes.join('/');
- this.volumeManager_.resolveAbsolutePath(
- parentPath,
- function(parentEntry) {
- nextCurrentDirEntry = parentEntry;
- suggestedName = baseName;
+ // Try to resolve as a file first. If it fails, then as a directory.
+ nextCurrentDirEntry.getFile(
+ this.initTargetName_,
+ {},
+ function(targetEntry) {
+ selectionEntry = targetEntry;
callback();
- },
- callback); // In case of an error, continue.
+ }, function() {
+ // Failed to resolve as a file
+ nextCurrentDirEntry.getDirectory(
+ this.initTargetName_,
+ {},
+ function(targetEntry) {
+ selectionEntry = targetEntry;
+ callback();
+ }, function() {
+ // Failed to resolve as either file or directory.
+ callback();
+ });
+ }.bind(this));
}.bind(this));
- // If the directory is not set at this stage, fallback to the default
- // mount point.
+
+ // Finalize.
queue.run(function(callback) {
- // Check error.
- if (error) {
- callback();
- throw error;
- }
// Check directory change.
tracker.stop();
if (tracker.hasChanged) {
}
// Finish setup current directory.
this.finishSetupCurrentDirectory_(
- nextCurrentDirEntry || defaultDisplayRoot,
+ nextCurrentDirEntry,
selectionEntry,
- suggestedName);
+ this.initTargetName_);
callback();
}.bind(this));
};
}
if (this.dialogType === DialogType.FULL_PAGE) {
- // In the FULL_PAGE mode if the restored path points to a file we might
+ // In the FULL_PAGE mode if the restored URL points to a file we might
// have to invoke a task after selecting it.
if (this.params_.action === 'select')
return;
var task = null;
// Handle restoring after crash, or the gallery action.
+ // TODO(mtomasz): Use the gallery action instead of just the gallery
+ // field.
if (this.params_.gallery || this.params_.action === 'gallery') {
if (!opt_selectionEntry) {
// Non-existent file or a directory.
} else {
// The file or the directory exists.
task = function() {
- // TODO(mtomasz): Replace the url with an entry.
new FileTasks(this, this.params_).openGallery([opt_selectionEntry]);
}.bind(this);
}
// If there is a task to be run, run it after the scan is completed.
if (task) {
var listener = function() {
+ if (!util.isSameEntry(this.directoryModel_.getCurrentDirEntry(),
+ directoryEntry)) {
+ // Opened on a different URL. Probably fallbacked. Therefore,
+ // do not invoke a task.
+ return;
+ }
this.directoryModel_.removeEventListener(
'scan-completed', listener);
task();
}
} else if (this.dialogType === DialogType.SELECT_SAVEAS_FILE) {
this.filenameInput_.value = opt_suggestedName || '';
- this.selectDefaultPathInFilenameInput_();
+ this.selectTargetNameInFilenameInput_();
}
};
/**
* @private
*/
- FileManager.prototype.selectDefaultPathInFilenameInput_ = function() {
+ FileManager.prototype.selectTargetNameInFilenameInput_ = function() {
var input = this.filenameInput_;
input.focus();
var selectionEnd = input.value.lastIndexOf('.');
input.selectionStart = 0;
input.selectionEnd = selectionEnd;
}
- // Clear, so we never do this again.
- this.defaultPath = '';
};
/**
cr.ui.MenuItem.decorate(item);
gearMenu.insertBefore(item, insertingPosition);
item.className = 'visit-desktop';
- item.label =
- strf('VISIT_DESKTOP_OF_USER', profile.displayName);
+ item.label = strf('VISIT_DESKTOP_OF_USER',
+ profile.displayName,
+ profile.profileId);
item.addEventListener('activate', function(inProfile, event) {
// Stop propagate and hide the menu manually, in order to prevent the
// focus from being back to the button. (cf. http://crbug.com/248479)
event.stopPropagation();
this.gearButton_.hideMenu();
this.gearButton_.blur();
- chrome.fileBrowserPrivate.visitDesktop(inProfile.profileId,
- function() {
- this.ui_.updateProfileBatch();
- }.bind(this));
+ chrome.fileBrowserPrivate.visitDesktop(inProfile.profileId);
}.bind(this, profile));
}
}.bind(this));
var currentVolumeInfo = this.currentVolumeInfo_;
chrome.fileBrowserPrivate.getSizeStats(
- currentVolumeInfo.root.toURL(), function(result) {
+ currentVolumeInfo.volumeId, function(result) {
var volumeInfo = this.volumeManager_.getVolumeInfo(
this.directoryModel_.getCurrentDirEntry());
if (currentVolumeInfo !== this.currentVolumeInfo_)
* @private
*/
FileManager.prototype.onDirectoryChanged_ = function(event) {
- this.selectionHandler_.onFileSelectionChanged();
- this.ui_.searchBox.clear();
- // TODO(mtomasz): Use Entry.toURL() instead of fullPath.
- util.updateAppState(
- this.getCurrentDirectoryEntry() &&
- this.getCurrentDirectoryEntry().fullPath, '' /* opt_param */);
-
- if (this.commandHandler)
- this.commandHandler.updateAvailability();
-
- this.updateUnformattedVolumeStatus_();
- this.updateTitle_();
var newCurrentVolumeInfo = this.volumeManager_.getVolumeInfo(
event.newDirEntry);
this.closeOnUnmount_ = false;
}
+ // Remember the current volume info.
+ this.currentVolumeInfo_ = newCurrentVolumeInfo;
+
+ this.selectionHandler_.onFileSelectionChanged();
+ this.ui_.searchBox.clear();
+ // TODO(mtomasz): Consider remembering the selection.
+ util.updateAppState(
+ this.getCurrentDirectoryEntry() ?
+ this.getCurrentDirectoryEntry().toURL() : '',
+ '' /* selectionURL */,
+ '' /* opt_param */);
+
+ if (this.commandHandler)
+ this.commandHandler.updateAvailability();
+
+ this.updateUnformattedVolumeStatus_();
+ this.updateTitle_();
+
var currentEntry = this.getCurrentDirectoryEntry();
this.previewPanel_.currentEntry = util.isFakeEntry(currentEntry) ?
null : currentEntry;
-
- // Remember the current volume info.
- this.currentVolumeInfo_ = newCurrentVolumeInfo;
};
FileManager.prototype.updateUnformattedVolumeStatus_ = function() {
// Show error dialog.
var message;
if (error.name == util.FileError.PATH_EXISTS_ERR ||
- error.name == util.FileError.TYPE_MISMACH_ERR) {
+ error.name == util.FileError.TYPE_MISMATCH_ERR) {
// Check the existing entry is file or not.
// 1) If the entry is a file:
// a) If we get PATH_EXISTS_ERR, a file exists.
(entry.isFile && error.name ==
util.FileError.PATH_EXISTS_ERR) ||
(!entry.isFile && error.name ==
- util.FileError.TYPE_MISMACH_ERR) ?
+ util.FileError.TYPE_MISMATCH_ERR) ?
'FILE_ALREADY_EXISTS' :
'DIRECTORY_ALREADY_EXISTS',
newName);
selectFileAndClose();
return;
}
- if (error.name == util.FileError.TYPE_MISMACH_ERR) {
+ if (error.name == util.FileError.TYPE_MISMATCH_ERR) {
// An directory is found.
// Do not allow to overwrite directory.
this.alert.show(strf('DIRECTORY_ALREADY_EXISTS', filename));