Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(6064)

Unified Diff: chrome/browser/resources/file_manager/js/directory_model.js

Issue 10310163: Refactoring file manager: moving volume mounting related code to a separate class. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/browser/resources/file_manager/js/directory_model.js
diff --git a/chrome/browser/resources/file_manager/js/directory_model.js b/chrome/browser/resources/file_manager/js/directory_model.js
index 89747a4c7b3822cf40c6b3c83dc1c1cb2342bc9f..4ba537ff14b5442ba98b916df5cab70ff2578181 100644
--- a/chrome/browser/resources/file_manager/js/directory_model.js
+++ b/chrome/browser/resources/file_manager/js/directory_model.js
@@ -19,7 +19,8 @@ var SHORT_RESCAN_INTERVAL = 100;
* (regardless of its mounts status).
* @param {MetadataCache} metadataCache The metadata cache service.
*/
-function DirectoryModel(root, singleSelection, showGData, metadataCache) {
+function DirectoryModel(root, singleSelection, showGData,
+ metadataCache, volumeManager) {
this.root_ = root;
this.metadataCache_ = metadataCache;
this.fileList_ = new cr.ui.ArrayDataModel([]);
@@ -54,11 +55,18 @@ function DirectoryModel(root, singleSelection, showGData, metadataCache) {
this.filters_ = {};
this.setFilterHidden(true);
- /**
- * @private
- * @type {Object.<string, boolean>}
- */
- this.volumeReadOnlyStatus_ = {};
+ if (this.showGData_) {
+ this.unmountedGDataEntry_ = {
+ unmouted: true,
+ fullPath: '/' + DirectoryModel.GDATA_DIRECTORY
+ };
+ }
+
+ this.volumeManager_ = volumeManager;
+ var volumesChangeHandler = this.onMountChanged_.bind(this);
+ this.updateRoots(function() {
Vladislav Kaznacheev 2012/05/15 11:11:54 Calling updateRoots from the constructor somehow f
+ volumeManager.addEventListener('change', volumesChangeHandler);
+ });
}
/**
@@ -92,7 +100,11 @@ DirectoryModel.DOWNLOADS_DIRECTORY = 'Downloads';
/**
* The name of the gdata provider directory.
*/
-DirectoryModel.GDATA_DIRECTORY = 'drive';
+DirectoryModel.GDATA_DIRECTORY = 'gdata'; // TODO(serya): return 'drive'
+
+DirectoryModel.fakeGDataEntry_ = {
+ fullPath: '/' + DirectoryModel.GDATA_DIRECTORY
+};
/**
* DirectoryModel extends cr.EventTarget.
@@ -172,7 +184,7 @@ DirectoryModel.prototype.isReadOnly = function() {
DirectoryModel.prototype.isPathReadOnly = function(path) {
switch (DirectoryModel.getRootType(path)) {
case DirectoryModel.RootType.REMOVABLE:
- return !!this.volumeReadOnlyStatus_[DirectoryModel.getRootPath(path)];
+ return !!this.volumeManager_.isReadOnly(DirectoryModel.getRootPath(path));
case DirectoryModel.RootType.ARCHIVE:
return true;
case DirectoryModel.RootType.DOWNLOADS:
@@ -313,6 +325,10 @@ DirectoryModel.prototype.getCurrentRootPath = function() {
return DirectoryModel.getRootPath(this.currentDirEntry_.fullPath);
};
+DirectoryModel.prototype.getCurrentRootType = function() {
+ return DirectoryModel.getRootType(this.currentDirEntry_.fullPath);
+};
+
/**
* @return {cr.ui.ListSingleSelectionModel} Root list selection model.
*/
@@ -488,7 +504,7 @@ DirectoryModel.prototype.scan_ = function(callback) {
// Clear the table first.
this.fileList_.splice(0, this.fileList_.length);
cr.dispatchSimpleEvent(this, 'scan-started');
- if (this.currentDirEntry_ == this.unmountedGDataEntry_) {
+ if (this.currentDirEntry_ == DirectoryModel.fakeGDataEntry_) {
onDone();
return;
}
@@ -691,16 +707,20 @@ DirectoryModel.prototype.changeDirectory = function(path) {
*/
DirectoryModel.prototype.resolveDirectory = function(path, successCallback,
errorCallback) {
- if (this.unmountedGDataEntry_ &&
- DirectoryModel.getRootType(path) == DirectoryModel.RootType.GDATA) {
+ if (DirectoryModel.getRootType(path) == DirectoryModel.RootType.GDATA) {
// TODO(kaznacheeev): Currently if path points to some GData subdirectory
// and GData is not mounted we will change to the fake GData root and
// ignore the rest of the path. Consider remembering the path and
// changing to it once GDdata is mounted. This is only relevant for cases
// when we open the File Manager with an URL pointing to GData (e.g. via
// a bookmark).
- successCallback(this.unmountedGDataEntry_);
- return;
+ if (!this.isGDataMounted_()) {
+ if (path == DirectoryModel.fakeGDataEntry_.fullPath)
+ successCallback(DirectoryModel.fakeGDataEntry_);
+ else // Subdirectory.
+ errorCallback({ code: FileError.NOT_FOUND_ERR });
+ return;
+ }
}
if (path == '/') {
@@ -776,6 +796,51 @@ DirectoryModel.prototype.changeDirectoryEntry_ = function(initial, dirEntry,
e.newDirEntry = dirEntry;
e.initial = initial;
this.dispatchEvent(e);
+
+ if (dirEntry == DirectoryModel.fakeGDataEntry_) {
+ this.volumeManager_.mountGData(function() {}, function() {});
+ }
+};
+
+/**
+ * Creates an object wich could cay wether directory has changed while it has
Vladislav Kaznacheev 2012/05/15 11:11:54 "cay", "canncelled" = typo?
+ * been active or not. Designed for long operations that should be canncelled
+ * if the used change current directory.
+ * @return {Object} Created object.
+ */
+DirectoryModel.prototype.createDirectoryChangeTracker = function() {
+ var tracker = {
+ dm_: this,
+ active_: false,
+ hasChanged: false,
+ exceptInitialChange: false,
+
+ start: function() {
+ if (!this.active_) {
+ this.dm_.addEventListener('directory-changed',
+ this.onDirectoryChange_);
+ this.active_ = true;
+ this.hasChanged = false;
+ }
+ },
+
+ stop: function() {
+ if (this.active_) {
+ this.dm_.removeEventListener('directory-changed',
+ this.onDirectoryChange_);
+ active_ = false;
+ }
+ },
+
+ onDirectoryChange_: function(event) {
+ if (tracker.exceptInitialChange && event.initial)
+ return;
+ // This is incorrect here.
+ tracker.stop();
+ tracker.hasChange = true;
+ }
+ };
+ return tracker;
};
/**
@@ -796,19 +861,21 @@ DirectoryModel.prototype.changeDirectoryEntry_ = function(initial, dirEntry,
*/
DirectoryModel.prototype.setupPath = function(path, opt_loadedCallback,
opt_pathResolveCallback) {
- var overridden = false;
- function onExternalDirChange() { overridden = true }
- this.addEventListener('directory-changed', onExternalDirChange);
+ var tracker = this.createDirectoryChangeTracker();
+ tracker.start();
var resolveCallback = function(exists) {
- this.removeEventListener('directory-changed', onExternalDirChange);
- if (opt_pathResolveCallback)
- opt_pathResolveCallback(baseName, leafName, exists && !overridden);
+ tracker.stop();
+ if (opt_pathResolveCallback) {
+ opt_pathResolveCallback(baseName, leafName,
+ exists && !tracker.hasChanged);
+ }
}.bind(this);
var changeDirectoryEntry = function(entry, initial, exists, opt_callback) {
+ tracker.stop();
resolveCallback(exists);
- if (!overridden)
+ if (!tracker.hasChanged)
this.changeDirectoryEntry_(initial, entry, opt_callback);
}.bind(this);
@@ -862,7 +929,7 @@ DirectoryModel.prototype.setupPath = function(path, opt_loadedCallback,
if (path != this.getDefaultDirectory()) {
// Can't find the provided path, let's go to default one instead.
resolveCallback(!EXISTS);
- if (!overridden)
+ if (!tracker.hasChanged)
this.setupDefaultPath(opt_loadedCallback);
} else {
// Well, we can't find the downloads dir. Let's just show something,
@@ -958,9 +1025,8 @@ DirectoryModel.prototype.prepareSortEntries_ = function(entries, field,
* Get root entries asynchronously.
* @private
* @param {function(Array.<Entry>)} callback Called when roots are resolved.
- * @param {boolean} resolveGData See comment for updateRoots.
*/
-DirectoryModel.prototype.resolveRoots_ = function(callback, resolveGData) {
+DirectoryModel.prototype.resolveRoots_ = function(callback) {
var groups = {
downloads: null,
archives: null,
@@ -975,7 +1041,6 @@ DirectoryModel.prototype.resolveRoots_ = function(callback, resolveGData) {
if (!groups[i])
return;
- self.updateVolumeReadOnlyStatus_(groups.removables);
callback(groups.downloads.
concat(groups.gdata).
concat(groups.archives).
@@ -988,60 +1053,43 @@ DirectoryModel.prototype.resolveRoots_ = function(callback, resolveGData) {
done();
}
- function onDownloads(entry) {
- groups.downloads = [entry];
+ function appendSingle(index, entry) {
+ groups[index] = [entry];
done();
}
- function onDownloadsError(error) {
- groups.downloads = [];
+ function onSingleError(index, error, defaultValue) {
+ groups[index] = defailtValue || [];
done();
}
- function onGData(entry) {
- console.log('GData found:', entry);
- self.unmountedGDataEntry_ = null;
- groups.gdata = [entry];
- done();
- }
-
- function onGDataError(error) {
- console.log('GData error: ' + error);
- self.unmountedGDataEntry_ = {
- unmounted: true, // Clients use this field to distinguish a fake root.
- toURL: function() { return '' },
- fullPath: '/' + DirectoryModel.GDATA_DIRECTORY
- };
- groups.gdata = [self.unmountedGDataEntry_];
- done();
+ var root = this.root_;
+ function readSingle(dir, index, defaultValue) {
+ root.getDirectory(dir, { create: false },
+ appendSingle.bind(this, index),
+ onSingleError.bind(this, index, defaultValue));
}
- var root = this.root_;
- root.getDirectory(DirectoryModel.DOWNLOADS_DIRECTORY, { create: false },
- onDownloads, onDownloadsError);
+ readSingle(DirectoryModel.DOWNLOADS_DIRECTORY, 'downloads');
util.readDirectory(root, DirectoryModel.ARCHIVE_DIRECTORY,
append.bind(this, 'archives'));
util.readDirectory(root, DirectoryModel.REMOVABLE_DIRECTORY,
append.bind(this, 'removables'));
if (this.showGData_) {
- if (resolveGData) {
- root.getDirectory(DirectoryModel.GDATA_DIRECTORY, { create: false },
- onGData, onGDataError);
- } else {
- onGDataError('lazy mount');
- }
+ var fake = [DirectoryModel.fakeGDataEntry_];
+ if (this.isGDataMounted_())
+ readSingle(DirectoryModel.GDATA_DIRECTORY, 'gdata', fake);
+ else
+ groups.gdata = fake;
} else {
groups.gdata = [];
}
};
/**
-* @param {function} opt_callback Called when all roots are resolved.
-* @param {boolean} opt_resolveGData If true GData should be resolved for real,
-* If false a stub entry should be created.
+* @param {functioni?} opt_callback Called when all roots are resolved.
*/
-DirectoryModel.prototype.updateRoots = function(opt_callback,
- opt_resolveGData) {
+DirectoryModel.prototype.updateRoots = function(opt_callback) {
var self = this;
this.resolveRoots_(function(rootEntries) {
var dm = self.rootsList_;
@@ -1049,10 +1097,8 @@ DirectoryModel.prototype.updateRoots = function(opt_callback,
dm.splice.apply(dm, args);
self.updateRootsListSelection_();
-
- if (opt_callback)
- opt_callback();
- }, opt_resolveGData);
+ opt_callback && opt_callback();
+ });
};
/**
@@ -1079,45 +1125,38 @@ DirectoryModel.prototype.updateRootsListSelection_ = function() {
this.rootsListSelection_.selectedIndex = this.findRootsListItem_(rootPath);
};
-/**
- * @param {Array.<DirectoryEntry>} roots Removable volumes entries.
- * @private
- */
-DirectoryModel.prototype.updateVolumeReadOnlyStatus_ = function(roots) {
- var status = this.volumeReadOnlyStatus_ = {};
- for (var i = 0; i < roots.length; i++) {
- status[roots[i].fullPath] = false;
- chrome.fileBrowserPrivate.getVolumeMetadata(roots[i].toURL(),
- function(systemMetadata, path) {
- status[path] = !!(systemMetadata && systemMetadata.isReadOnly);
- }.bind(null, roots[i].fullPath));
- }
+DirectoryModel.prototype.isGDataMounted_ = function() {
+ return this.volumeManager_.isMounted('/' + DirectoryModel.GDATA_DIRECTORY);
};
-/**
- * Prepare the root for the unmount.
- *
- * @param {string} rootPath The path to the root.
- */
-DirectoryModel.prototype.prepareUnmount = function(rootPath) {
- var index = this.findRootsListItem_(rootPath);
- if (index == -1) {
- console.error('Unknown root entry', rootPath);
- return;
- }
- var entry = this.rootsList_.item(index);
+DirectoryModel.prototype.onMountChanged_ = function() {
+ this.updateRoots();
- // We never need to remove this attribute because even if the unmount fails
- // the onMountCompleted handler calls updateRoots which creates a new entry
- // object for this volume.
- entry.unmounting = true;
-
- // Re-place the entry into the roots data model to force re-rendering.
- this.rootsList_.splice(index, 1, entry);
+ if (this.getCurrentRootType() != DirectoryModel.RootType.GDATA)
+ return;
- if (rootPath == this.rootPath) {
- // TODO(kaznacheev): Consider changing to the most recently used root.
- this.changeDirectory(this.getDefaultDirectory());
+ var mounted = this.isGDataMounted_();
+ if (this.currentDirEntry_ == DirectoryModel.fakeGDataEntry_) {
+ if (mounted) {
+ // Change fake entry to real one and rescan.
+ this.root_.getDirectory('/' + DirectoryModel.GDATA_DIRECTORY, {},
+ function(entry) {
+ if (this.currentDirEntry_ == DirectoryModel.fakeGDataEntry_) {
+ this.currentDirEntry_ = entry;
+ this.rescan();
+ }
+ }.bind(this));
+ }
+ } else if (!mounted) {
+ // Current entry unmounted. replace with fake one.
+ if (this.currentDirEntry_.fullPath ==
+ DirectoryModel.fakeGDataEntry_.fullPath) {
+ // Replace silently and rescan.
+ this.currentDirEntry_ = DirectoryModel.fakeGDataEntry_;
+ this.rescan();
+ } else {
+ this.changeDirectoryEntry_(false, DirectoryModel.fakeGDataEntry_);
+ }
}
};
@@ -1282,3 +1321,86 @@ DirectoryModel.Scanner.prototype.recordMetrics_ = function() {
metrics.recordMediumCount('DownloadsCount', this.list_.length);
}
};
+
+function FileWatcher(root, directoryModel, volumeManager) {
+ this.root_ = root;
+ this.dm_ = directoryModel;
+ this.vm_ = volumeManager;
+ this.watchedDirectoryEntry_ = null;
+ this.updateWatchedDirectoryBound_ =
+ this.updateWatchedDirectory_.bind(this);
+ this.onFileChangedBound_ =
+ this.onFileChanged_.bind(this);
+}
+
+FileWatcher.prototype.start = function() {
+ chrome.fileBrowserPrivate.onFileChanged.addListener(
+ this.onFileChangedBound_);
+
+ this.dm_.addEventListener('directory-changed',
+ this.updateWatchedDirectoryBound_);
+ this.vm_.addEventListener('changed',
+ this.updateWatchedDirectoryBound_);
+
+ this.updateWatchedDirectory_();
+};
+
+FileWatcher.prototype.stop = function() {
+ chrome.fileBrowserPrivate.onFileChanged.removeListener(
+ this.onFileChangedBound_);
+
+ this.dm_.removeEventListener('directory-changed',
+ this.updateWatchedDirectoryBound_);
+ this.vm_.removeEventListener('changed',
+ this.updateWatchedDirectoryBound_);
+
+ if (this.watchedDirectoryEntry_)
+ this.changeWatchedEntry(null);
+};
+
+FileWatcher.prototype.onFileChanged_ = function(event) {
+ if (encodeURI(event.fileUrl) == this.watchedDirectoryEntry_.toURL())
+ this.dm_.rescanLater();
+};
+
+FileWatcher.prototype.updateWatchedDirectory_ = function() {
+ var current = this.watchedDirectoryEntry_;
+ switch (this.dm_.getCurrentRootType()) {
+ case DirectoryModel.RootType.GDATA:
+ if (!this.vm_.isMounted('/' + DirectoryModel.GDATA_DIRECTORY))
+ break;
+ case DirectoryModel.RootType.DOWNLOADS:
+ case DirectoryModel.RootType.REMOVABLE:
+ if (!current || current.fullPath != this.dm_.getCurrentDirPath()) {
+ this.root_.getDirectory(this.dm_.getCurrentDirPath(), {},
+ this.changeWatchedEntry.bind(this));
+ }
+ break;
+ }
+ if (current)
+ this.changeWatchedEntry(null);
+};
+
+FileWatcher.prototype.changeWatchedEntry = function(entry) {
+ if (this.watchedDirectoryEntry_) {
+ chrome.fileBrowserPrivate.removeFileWatch(
+ this.watchedDirectoryEntry_.toURL(),
+ function(result) {
+ if (!result) {
+ console.log('Failed to remove file watch');
+ }
+ });
+ }
+ this.watchedDirectoryEntry_ = entry;
+
+ if (this.watchedDirectoryEntry_) {
+ chrome.fileBrowserPrivate.addFileWatch(
+ this.watchedDirectoryEntry_.toURL(),
+ function(result) {
+ if (!result) {
+ console.log('Failed to add file watch');
+ this.watchedDirectoryEntry_ = null;
+ }
+ }.bind(this));
+ }
+};

Powered by Google App Engine
This is Rietveld 408576698