Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / file_manager / foreground / js / file_manager.js
index a1cfe96..7d645d4 100644 (file)
@@ -46,17 +46,6 @@ function FileManager() {
   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() {
@@ -76,6 +65,9 @@ FileManager.prototype = {
   },
   get volumeManager() {
     return this.volumeManager_;
+  },
+  get ui() {
+    return this.ui_;
   }
 };
 
@@ -227,9 +219,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       }.bind(this));
     }.bind(this));
 
-    // TODO(yoshiki): Remove the flag when the feature is launched.
-    this.enableExperimentalWebstoreIntegration_ = true;
-
     group.run(callback);
   };
 
@@ -376,8 +365,8 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @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.
@@ -410,6 +399,26 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
         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_++;
   };
 
   /**
@@ -445,9 +454,14 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     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);
@@ -482,7 +496,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   };
 
   FileManager.prototype.onMaximize = function() {
-    this.document_.activeElement.blur();
     var appWindow = chrome.app.window.current();
     if (appWindow.isMaximized())
       appWindow.restore();
@@ -586,14 +599,20 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    */
   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_.
@@ -653,7 +672,9 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @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,
@@ -976,7 +997,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
                                  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) {
@@ -1396,12 +1417,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   };
 
   /**
-   * 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() {
@@ -1414,138 +1430,117 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       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) {
@@ -1554,9 +1549,9 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       }
       // Finish setup current directory.
       this.finishSetupCurrentDirectory_(
-          nextCurrentDirEntry || defaultDisplayRoot,
+          nextCurrentDirEntry,
           selectionEntry,
-          suggestedName);
+          this.initTargetName_);
       callback();
     }.bind(this));
   };
@@ -1581,13 +1576,15 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
     }
 
     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.
@@ -1599,7 +1596,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
         } 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);
         }
@@ -1611,6 +1607,12 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       // 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();
@@ -1619,7 +1621,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       }
     } else if (this.dialogType === DialogType.SELECT_SAVEAS_FILE) {
       this.filenameInput_.value = opt_suggestedName || '';
-      this.selectDefaultPathInFilenameInput_();
+      this.selectTargetNameInFilenameInput_();
     }
   };
 
@@ -2046,7 +2048,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
   /**
    * @private
    */
-  FileManager.prototype.selectDefaultPathInFilenameInput_ = function() {
+  FileManager.prototype.selectTargetNameInFilenameInput_ = function() {
     var input = this.filenameInput_;
     input.focus();
     var selectionEnd = input.value.lastIndexOf('.');
@@ -2056,8 +2058,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
       input.selectionStart = 0;
       input.selectionEnd = selectionEnd;
     }
-    // Clear, so we never do this again.
-    this.defaultPath = '';
   };
 
   /**
@@ -2257,18 +2257,16 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
         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));
@@ -2299,7 +2297,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
 
     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_)
@@ -2318,18 +2316,6 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
    * @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);
 
@@ -2342,12 +2328,27 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
         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() {
@@ -2522,7 +2523,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
             // 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.
@@ -2534,7 +2535,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
                   (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);
@@ -3236,7 +3237,7 @@ var BOTTOM_MARGIN_FOR_PREVIEW_PANEL_PX = 52;
                 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));