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

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

Issue 7745051: Added refresh on filesystem change (Closed) Base URL: http://git.chromium.org/git/chromium.git@trunk
Patch Set: Flacky test Created 9 years, 1 month 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
« no previous file with comments | « chrome/browser/chromeos/extensions/file_browser_event_router.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: chrome/browser/resources/file_manager/js/file_manager.js
diff --git a/chrome/browser/resources/file_manager/js/file_manager.js b/chrome/browser/resources/file_manager/js/file_manager.js
index 41fd6f3c47a007ec15af898b2d2df861eb3554ae..7c10fa3eee56a85f165af7093b6b4d4431c9367b 100644
--- a/chrome/browser/resources/file_manager/js/file_manager.js
+++ b/chrome/browser/resources/file_manager/js/file_manager.js
@@ -9,6 +9,12 @@ const EMPTY_IMAGE_URI = 'data:image/gif;base64,'
var g_slideshow_data = null;
+const GALLERY_ENABLED = true;
+
+// If directory files changes too often, don't rescan directory more than once
+// per specified interval
+const SIMULTANEOUS_RESCAN_INTERVAL = 1000;
+
/**
* FileManager constructor.
*
@@ -50,6 +56,9 @@ function FileManager(dialogDom, filesystem, rootEntries) {
// True if we should filter out files that start with a dot.
this.filterFiles_ = true;
+ this.subscribedOnDirectoryChanges_ = false;
+ this.pendingRescanQueue_ = [];
+ this.rescanRunning_ = false;
this.commands_ = {};
@@ -111,6 +120,8 @@ function FileManager(dialogDom, filesystem, rootEntries) {
this.onCopyProgress_.bind(this));
window.addEventListener('popstate', this.onPopState_.bind(this));
+ window.addEventListener('unload', this.onUnload_.bind(this));
+
this.addEventListener('directory-changed',
this.onDirectoryChanged_.bind(this));
this.addEventListener('selection-summarized',
@@ -122,6 +133,9 @@ function FileManager(dialogDom, filesystem, rootEntries) {
chrome.fileBrowserPrivate.onMountCompleted.addListener(
this.onMountCompleted_.bind(this));
+ chrome.fileBrowserPrivate.onFileChanged.addListener(
+ this.onFileChanged_.bind(this));
+
var self = this;
// The list of callbacks to be invoked during the directory rescan after
@@ -2853,6 +2867,26 @@ FileManager.prototype = {
this.document_.title = this.currentDirEntry_.fullPath;
var self = this;
+
+ if (this.subscribedOnDirectoryChanges_) {
+ chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(),
+ function(result) {
+ if (!result) {
+ console.log('Failed to remove file watch');
+ }
+ });
+ }
+
+ if (event.newDirEntry.fullPath != '/') {
+ this.subscribedOnDirectoryChanges_ = true;
+ chrome.fileBrowserPrivate.addFileWatch(event.newDirEntry.toURL(),
+ function(result) {
+ if (!result) {
+ console.log('Failed to add file watch');
+ }
+ });
+ }
+
this.rescanDirectory_(function() {
if (event.selectedEntry)
self.selectEntry(event.selectedEntry);
@@ -2872,117 +2906,145 @@ FileManager.prototype = {
};
/**
- * Rescans the current directory, refreshing the list. It decreases the
- * probability that two such calls are pending simultaneously.
+ * Rescans directory later.
+ * This method should be used if we just want rescan but not actually now.
+ * This helps us not to flood queue with rescan requests.
*
- * @param {function()} opt_callback Optional function to invoke when the
- * rescan is complete.
- * @param {string} delayMS Delay during which next rescanDirectory calls
- * can happen.
+ * @param opt_callback
+ * @param opt_onError
*/
+ FileManager.prototype.rescanDirectoryLater_ = function(opt_callback,
+ opt_onError) {
+ // It might be massive change, so let's note somehow, that we need
+ // rescanning and then wait some time
- FileManager.prototype.rescanDirectory_ = function(opt_callback, delayMS) {
- var self = this;
- function done(count) {
- // Variable count is introduced because we only want to do callbacks, that
- // were in the queue at the moment of rescanDirectoryNow invocation.
- while (count--) {
- var callback = self.rescanDirectory_.callbacks.shift();
- callback();
+ if (this.pendingRescanQueue_.length == 0) {
+ this.pendingRescanQueue_.push({onSuccess:opt_callback,
+ onError:opt_onError});
+
+ // If rescan isn't going to run without
+ // our interruption, then say that we need to run it
+ if (!this.rescanRunning_) {
+ setTimeout(this.rescanDirectory_.bind(this),
+ SIMULTANEOUS_RESCAN_INTERVAL);
}
}
+ }
- // callbacks is a queue of callbacks that need to be called after rescaning
- // directory is done. We push callback to the end and pop form the front.
- // When we get to actually call rescanDirectoryNow_ we need to remember how
- // many callbacks were in a queue at the time, not to call callbacks that
- // arrived in the middle of execution (they may depend on rescaning being
- // done after they called rescanDirectory_).
- if (!this.rescanDirectory_.callbacks)
- this.rescanDirectory_.callbacks = [];
-
- if (opt_callback)
- this.rescanDirectory_.callbacks.push(opt_callback);
-
- delayMS = delayMS || 100;
-
- // Assumes rescanDirectoryNow_ takes less than 100 ms. If not there is
- // a possible overlap between two calls.
- if (delayMS < 100)
- delayMS = 100;
-
- if (this.rescanDirectory_.handle)
- clearTimeout(this.rescanDirectory_.handle);
- var currentQueueLength = self.rescanDirectory_.callbacks.length;
- this.rescanDirectory_.handle = setTimeout(function () {
- self.rescanDirectoryNow_(function() {
- done(currentQueueLength);
- });
- }, delayMS);
- };
/**
- * Rescans the current directory immediately, refreshing the list. Should NOT
- * be used in most cases. Instead use rescanDirectory_.
+ * Rescans the current directory, refreshing the list. It decreases the
+ * probability that two such calls are pending simultaneously.
+ *
+ * This method tries to queue request if rescan is already running, and
+ * processes this request later. Anyway callback would be called after
+ * processing.
+ *
+ * If no rescan is running, then method starts rescanning immediately.
*
* @param {function()} opt_callback Optional function to invoke when the
* rescan is complete.
+ *
+ * @param {function()} opt_onError Optional function to invoke when the
+ * rescan fails.
*/
- FileManager.prototype.rescanDirectoryNow_ = function(opt_callback) {
- var self = this;
- var reader;
+ FileManager.prototype.rescanDirectory_ = function(opt_callback, opt_onError) {
+ // Updated when a user clicks on the label of a file, used to detect
+ // when a click is eligible to trigger a rename. Can be null, or
+ // an object with 'path' and 'date' properties.
+ this.lastLabelClick_ = null;
- function rescanDone() {
- metrics.recordTime('ScanDirectory');
- if (self.currentDirEntry_.fullPath == DOWNLOADS_DIRECTORY)
- metrics.reportCount("DownloadsCount", self.dataModel_.length);
- if (opt_callback)
- opt_callback();
- }
+ // Clear the table first.
+ this.dataModel_.splice(0, this.dataModel_.length);
+ this.currentList_.selectionModel.clear();
- metrics.startInterval('ScanDirectory');
+ this.updateBreadcrumbs_();
- function onReadSome(entries) {
- if (entries.length == 0) {
- rescanDone();
+ if (this.currentDirEntry_.fullPath != '/') {
+ // Add current request to pending result list
+ this.pendingRescanQueue_.push({
+ onSuccess:opt_callback,
+ onError:opt_onError
+ });
+
+ if (this.rescanRunning_)
return;
- }
- // Splice takes the to-be-spliced-in array as individual parameters,
- // rather than as an array, so we need to perform some acrobatics...
- var spliceArgs = [].slice.call(entries);
+ this.rescanRunning_ = true;
- // Hide files that start with a dot ('.').
- // TODO(rginda): User should be able to override this. Support for other
- // commonly hidden patterns might be nice too.
- if (self.filterFiles_) {
- spliceArgs = spliceArgs.filter(function(e) {
- return e.name.substr(0, 1) != '.';
- });
+ // The current list of callbacks is saved and reset. Subsequent
+ // calls to rescanDirectory_ while we're still pending will be
+ // saved and will cause an additional rescan to happen after a delay.
+ var callbacks = this.pendingRescanQueue_;
+
+ this.pendingRescanQueue_ = [];
+
+ var self = this;
+ var reader;
+
+ function onError() {
+ if (self.pendingRescanQueue_.length > 0) {
+ setTimeout(self.rescanDirectory_.bind(self),
+ SIMULTANEOUS_RESCAN_INTERVAL);
+ }
+
+ self.rescanRunning_ = false;
+
+ for (var i= 0; i < callbacks.length; i++) {
+ if (callbacks[i].onError)
+ try {
+ callbacks[i].onError();
+ } catch (ex) {
+ console.error('Caught exception while notifying about error: ' +
+ name, ex);
+ }
+ }
}
- spliceArgs.unshift(0, 0); // index, deleteCount
- self.dataModel_.splice.apply(self.dataModel_, spliceArgs);
+ function onReadSome(entries) {
+ if (entries.length == 0) {
+ if (self.pendingRescanQueue_.length > 0) {
+ setTimeout(self.rescanDirectory_.bind(self),
+ SIMULTANEOUS_RESCAN_INTERVAL);
+ }
- // Keep reading until entries.length is 0.
- reader.readEntries(onReadSome);
- };
+ self.rescanRunning_ = false;
+ for (var i= 0; i < callbacks.length; i++) {
+ if (callbacks[i].onSuccess)
+ try {
+ callbacks[i].onSuccess();
+ } catch (ex) {
+ console.error('Caught exception while notifying about error: ' +
+ name, ex);
+ }
+ }
- // Updated when a user clicks on the label of a file, used to detect
- // when a click is eligible to trigger a rename. Can be null, or
- // an object with 'path' and 'date' properties.
- this.lastLabelClick_ = null;
+ return;
+ }
- // Clear the table first.
- this.dataModel_.splice(0, this.dataModel_.length);
- this.currentList_.selectionModel.clear();
+ // Splice takes the to-be-spliced-in array as individual parameters,
+ // rather than as an array, so we need to perform some acrobatics...
+ var spliceArgs = [].slice.call(entries);
+
+ // Hide files that start with a dot ('.').
+ // TODO(rginda): User should be able to override this. Support for other
+ // commonly hidden patterns might be nice too.
+ if (self.filterFiles_) {
+ spliceArgs = spliceArgs.filter(function(e) {
+ return e.name.substr(0, 1) != '.';
+ });
+ }
- this.updateBreadcrumbs_();
+ spliceArgs.unshift(0, 0); // index, deleteCount
+ self.dataModel_.splice.apply(self.dataModel_, spliceArgs);
+
+ // Keep reading until entries.length is 0.
+ reader.readEntries(onReadSome, onError);
+ };
- if (this.currentDirEntry_.fullPath != '/') {
// If not the root directory, just read the contents.
reader = this.currentDirEntry_.createReader();
- reader.readEntries(onReadSome);
+ reader.readEntries(onReadSome, onError);
return;
}
@@ -2991,7 +3053,7 @@ FileManager.prototype = {
// harness) can't be enumerated yet.
var spliceArgs = [].slice.call(this.rootEntries_);
spliceArgs.unshift(0, 0); // index, deleteCount
- self.dataModel_.splice.apply(self.dataModel_, spliceArgs);
+ this.dataModel_.splice.apply(this.dataModel_, spliceArgs);
rescanDone();
};
@@ -3042,6 +3104,23 @@ FileManager.prototype = {
}
};
+ FileManager.prototype.onUnload_ = function(event) {
+ if (this.subscribedOnDirectoryChanges_) {
+ chrome.fileBrowserPrivate.removeFileWatch(event.previousDirEntry.toURL(),
+ function(result) {
+ if (!result) {
+ console.log('Failed to remove file watch');
+ }
+ });
+ }
+ };
+
+ FileManager.prototype.onFileChanged_ = function(event) {
+ // We receive a lot of events even in folders we are not interested in.
+ if (event.fileUrl == this.currentDirEntry_.toURL())
+ this.rescanDirectoryLater_();
+ };
+
/**
* Determine whether or not a click should initiate a rename.
*
« no previous file with comments | « chrome/browser/chromeos/extensions/file_browser_event_router.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698