Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / file_manager / foreground / js / directory_model.js
index 23230b3..b06971f 100644 (file)
@@ -33,6 +33,8 @@ function DirectoryModel(singleSelection, fileFilter, fileWatcher,
   this.changeDirectorySequence_ = 0;
 
   this.directoryChangeQueue_ = new AsyncUtil.Queue();
+  this.rescanAggregator_ = new AsyncUtil.Aggregator(
+      this.rescanSoon.bind(this, true), 500);
 
   this.fileFilter_ = fileFilter;
   this.fileFilter_.addEventListener('changed',
@@ -171,31 +173,31 @@ DirectoryModel.prototype.onWatcherDirectoryChanged_ = function(event) {
   var directoryEntry = this.getCurrentDirEntry();
 
   if (event.changedFiles) {
-    var urls = event.changedFiles.map(function(change) { return change.url; });
-    util.URLsToEntries(urls).then(function(result) {
-      // Removes the metadata of invalid entries.
-      if (result.failureUrls.length > 0)
-        this.metadataCache_.clearByUrl(result.failureUrls, '*');
-
-      // Rescans after force-refreshing the metadata of the changed entries.
-      var entries = result.entries;
-      if (entries.length) {
-        this.currentDirContents_.prefetchMetadata(entries, true, function() {
-          this.rescanSoon(false);
-        }.bind(this));
-      } else {
-        this.rescanSoon(false);
-      }
+    var addedOrUpdatedFileUrls = [];
+    var deletedFileUrls = [];
+    event.changedFiles.forEach(function(change) {
+      if (change.changes.length === 1 && change.changes[0] === 'delete')
+        deletedFileUrls.push(change.url);
+      else
+        addedOrUpdatedFileUrls.push(change.url);
+    });
+
+    util.URLsToEntries(addedOrUpdatedFileUrls).then(function(result) {
+      deletedFileUrls = deletedFileUrls.concat(result.failureUrls);
+
+      // Passing the resolved entries and failed URLs as the removed files.
+      // The URLs are removed files and they chan't be resolved.
+      this.partialUpdate_(result.entries, deletedFileUrls);
     }.bind(this)).catch(function(error) {
       console.error('Error in proceeding the changed event.', error,
                     'Fallback to force-refresh');
-      this.rescanSoon(true);
+      this.rescanAggregator_.run();
     }.bind(this));
   } else {
     // Invokes force refresh if the detailed information isn't provided.
     // This can occur very frequently (e.g. when copying files into Downlaods)
     // and rescan is heavy operation, so we keep some interval for each rescan.
-    this.rescanLater(true);
+    this.rescanAggregator_.run();
   }
 };
 
@@ -383,6 +385,7 @@ DirectoryModel.prototype.clearAndScan_ = function(newDirContents,
                                                   callback) {
   if (this.currentDirContents_.isScanning())
     this.currentDirContents_.cancelScan();
+  this.currentDirContents_.dispose();
   this.currentDirContents_ = newDirContents;
   this.clearRescanTimeout_();
 
@@ -446,6 +449,63 @@ DirectoryModel.prototype.clearAndScan_ = function(newDirContents,
 };
 
 /**
+ * Adds/removes/updates items of file list.
+ * @param {Array.<Entry>} changedEntries Entries of updated/added files.
+ * @param {Array.<string>} removedUrls URLs of removed files.
+ * @private
+ */
+DirectoryModel.prototype.partialUpdate_ =
+    function(changedEntries, removedUrls) {
+  // This update should be included in the current running update.
+  if (this.pendingScan_)
+    return;
+
+  if (this.runningScan_) {
+    // Do update after the current scan is finished.
+    var previousScan = this.runningScan_;
+    var onPreviousScanCompleted = function() {
+      previousScan.removeEventListener('scan-completed',
+                                       onPreviousScanCompleted);
+      // Run the update asynchronously.
+      Promise.resolve().then(function() {
+        this.partialUpdate_(changedEntries, removedUrls);
+      }.bind(this));
+    }.bind(this);
+    previousScan.addEventListener('scan-completed', onPreviousScanCompleted);
+    return;
+  }
+
+  var onFinish = function() {
+    this.runningScan_ = null;
+
+    this.currentDirContents_.removeEventListener(
+        'scan-completed', onCompleted);
+    this.currentDirContents_.removeEventListener('scan-failed', onFailure);
+    this.currentDirContents_.removeEventListener(
+        'scan-cancelled', onCancelled);
+  }.bind(this);
+
+  var onCompleted = function() {
+    onFinish();
+    cr.dispatchSimpleEvent(this, 'rescan-completed');
+  }.bind(this);
+
+  var onFailure = function() {
+    onFinish();
+  };
+
+  var onCancelled = function() {
+    onFinish();
+  };
+
+  this.runningScan_ = this.currentDirContents_;
+  this.currentDirContents_.addEventListener('scan-completed', onCompleted);
+  this.currentDirContents_.addEventListener('scan-failed', onFailure);
+  this.currentDirContents_.addEventListener('scan-cancelled', onCancelled);
+  this.currentDirContents_.update(changedEntries, removedUrls);
+};
+
+/**
  * Perform a directory contents scan. Should be called only from rescan() and
  * clearAndScan_().
  *
@@ -480,7 +540,16 @@ DirectoryModel.prototype.scan_ = function(
     return false;
   }.bind(this);
 
+  var onFinished = function() {
+    dirContents.removeEventListener('scan-completed', onSuccess);
+    dirContents.removeEventListener('scan-updated', updatedCallback);
+    dirContents.removeEventListener('scan-failed', onFailure);
+    dirContents.removeEventListener('scan-cancelled', cancelledCallback);
+  };
+
   var onSuccess = function() {
+    onFinished();
+
     // Record metric for Downloads directory.
     if (!dirContents.isSearch()) {
       var locationInfo =
@@ -500,6 +569,8 @@ DirectoryModel.prototype.scan_ = function(
   }.bind(this);
 
   var onFailure = function() {
+    onFinished();
+
     this.runningScan_ = null;
     this.scanFailures_++;
     failureCallback();
@@ -511,20 +582,28 @@ DirectoryModel.prototype.scan_ = function(
       this.rescanLater(refresh);
   }.bind(this);
 
+  var onCancelled = function() {
+    onFinished();
+    cancelledCallback();
+  };
+
   this.runningScan_ = dirContents;
 
   dirContents.addEventListener('scan-completed', onSuccess);
   dirContents.addEventListener('scan-updated', updatedCallback);
   dirContents.addEventListener('scan-failed', onFailure);
-  dirContents.addEventListener('scan-cancelled', cancelledCallback);
+  dirContents.addEventListener('scan-cancelled', onCancelled);
   dirContents.scan(refresh);
 };
 
 /**
- * @param {DirectoryContents} dirContents DirectoryContents instance.
+ * @param {DirectoryContents} dirContents DirectoryContents instance. This must
+ *     be a different instance from this.currentDirContents_.
  * @private
  */
 DirectoryModel.prototype.replaceDirectoryContents_ = function(dirContents) {
+  console.assert(this.currentDirContents_ !== dirContents,
+      'Give directory contents instance must be different from current one.');
   cr.dispatchSimpleEvent(this, 'begin-update-files');
   this.updateSelectionAndPublishEvent_(this.fileListSelection_, function() {
     var selectedEntries = this.getSelectedEntries_();
@@ -534,9 +613,10 @@ DirectoryModel.prototype.replaceDirectoryContents_ = function(dirContents) {
     var leadIndex = this.fileListSelection_.leadIndex;
     var leadEntry = this.getLeadEntry_();
 
-    this.currentDirContents_.dispose();
+    var previousDirContents = this.currentDirContents_;
     this.currentDirContents_ = dirContents;
-    dirContents.replaceContextFileList();
+    this.currentDirContents_.replaceContextFileList();
+    previousDirContents.dispose();
 
     this.setSelectedEntries_(selectedEntries);
     this.fileListSelection_.leadIndex = leadIndex;
@@ -595,7 +675,7 @@ DirectoryModel.prototype.onEntriesChanged = function(kind, entries) {
             entriesToAdd.push(entries[i]);
           }
         }
-        this.getFileList().push.apply(this.getFileList(), entriesToAdd);
+        this.partialUpdate_(entriesToAdd, []);
       }.bind(this)).catch(function(error) {
         console.error(error.stack || error);
       });
@@ -603,11 +683,7 @@ DirectoryModel.prototype.onEntriesChanged = function(kind, entries) {
 
     case util.EntryChangedKind.DELETED:
       // This is the delete event.
-      for (var i = 0; i < entries.length; i++) {
-        var index = this.findIndexByEntry_(entries[i]);
-        if (index >= 0)
-          this.getFileList().splice(index, 1);
-      }
+      this.partialUpdate_([], util.entriesToURLs(entries));
       break;
 
     default:
@@ -639,7 +715,7 @@ DirectoryModel.prototype.findIndexByEntry_ = function(entry) {
  *
  * @param {Entry} oldEntry The old entry.
  * @param {Entry} newEntry The new entry.
- * @param {function()} opt_callback Called on completion.
+ * @param {function()=} opt_callback Called on completion.
  */
 DirectoryModel.prototype.onRenameEntry = function(
     oldEntry, newEntry, opt_callback) {