Index: chrome/browser/resources/file_manager/js/volume_manager.js |
diff --git a/chrome/browser/resources/file_manager/js/volume_manager.js b/chrome/browser/resources/file_manager/js/volume_manager.js |
deleted file mode 100644 |
index f55b8490e006765d493b968dcd518d9a87be7285..0000000000000000000000000000000000000000 |
--- a/chrome/browser/resources/file_manager/js/volume_manager.js |
+++ /dev/null |
@@ -1,635 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-'use strict'; |
- |
-/** |
- * Represents each volume, such as "drive", "download directory", each "USB |
- * flush storage", or "mounted zip archive" etc. |
- * |
- * @param {util.VolumeType} volumeType The type of the volume. |
- * @param {string} mountPath Where the volume is mounted. |
- * @param {DirectoryEntry} root The root directory entry of this volume. |
- * @param {string} error The error if an error is found. |
- * @param {string} deviceType The type of device ('usb'|'sd'|'optical'|'mobile' |
- * |'unknown') (as defined in chromeos/disks/disk_mount_manager.cc). |
- * Can be null. |
- * @param {boolean} isReadOnly True if the volume is read only. |
- * @constructor |
- */ |
-function VolumeInfo( |
- volumeType, mountPath, root, error, deviceType, isReadOnly) { |
- this.volumeType = volumeType; |
- // TODO(hidehiko): This should include FileSystem instance. |
- this.mountPath = mountPath; |
- this.root = root; |
- |
- // Note: This represents if the mounting of the volume is successfully done |
- // or not. (If error is empty string, the mount is successfully done). |
- // TODO(hidehiko): Rename to make this more understandable. |
- this.error = error; |
- this.deviceType = deviceType; |
- this.isReadOnly = isReadOnly; |
- |
- // VolumeInfo is immutable. |
- Object.freeze(this); |
-} |
- |
-/** |
- * Utilities for volume manager implementation. |
- */ |
-var volumeManagerUtil = {}; |
- |
-/** |
- * Throws an Error when the given error is not in util.VolumeError. |
- * @param {util.VolumeError} error Status string usually received from APIs. |
- */ |
-volumeManagerUtil.validateError = function(error) { |
- for (var key in util.VolumeError) { |
- if (error == util.VolumeError[key]) |
- return; |
- } |
- |
- throw new Error('Invalid mount error: ' + error); |
-}; |
- |
-/** |
- * The regex pattern which matches valid mount paths. |
- * The valid paths are: |
- * - Either of '/drive', '/drive_shared_with_me', '/drive_offline', |
- * '/drive_recent' or '/Download' |
- * - For archive, drive, removable can have (exactly one) sub directory in the |
- * root path. E.g. '/archive/foo', '/removable/usb1' etc. |
- * |
- * @type {RegExp} |
- * @private |
- */ |
-volumeManagerUtil.validateMountPathRegExp_ = new RegExp( |
- '^/(drive|drive_shared_with_me|drive_offline|drive_recent|Downloads|' + |
- '((archive|drive|removable)\/[^/]+))$'); |
- |
-/** |
- * Throws an Error if the validation fails. |
- * @param {string} mountPath The target path of the validation. |
- */ |
-volumeManagerUtil.validateMountPath = function(mountPath) { |
- if (!volumeManagerUtil.validateMountPathRegExp_.test(mountPath)) |
- throw new Error('Invalid mount path: ' + mountPath); |
-}; |
- |
-/** |
- * Returns the root entry of a volume mounted at mountPath. |
- * |
- * @param {string} mountPath The mounted path of the volume. |
- * @param {function(DirectoryEntry)} successCallback Called when the root entry |
- * is found. |
- * @param {function(FileError)} errorCallback Called when an error is found. |
- * @private |
- */ |
-volumeManagerUtil.getRootEntry_ = function( |
- mountPath, successCallback, errorCallback) { |
- // We always request FileSystem here, because requestFileSystem() grants |
- // permissions if necessary, especially for Drive File System at first mount |
- // time. |
- // Note that we actually need to request FileSystem after multi file system |
- // support, so this will be more natural code then. |
- chrome.fileBrowserPrivate.requestFileSystem( |
- 'compatible', |
- function(fileSystem) { |
- // TODO(hidehiko): chrome.runtime.lastError should have error reason. |
- if (!fileSystem) { |
- errorCallback(util.createFileError(FileError.NOT_FOUND_ERR)); |
- return; |
- } |
- |
- fileSystem.root.getDirectory( |
- mountPath.substring(1), // Strip leading '/'. |
- {create: false}, successCallback, errorCallback); |
- }); |
-}; |
- |
-/** |
- * Builds the VolumeInfo data from VolumeMetadata. |
- * @param {VolumeMetadata} volumeMetadata Metadata instance for the volume. |
- * @param {function(VolumeInfo)} callback Called on completion. |
- */ |
-volumeManagerUtil.createVolumeInfo = function(volumeMetadata, callback) { |
- volumeManagerUtil.getRootEntry_( |
- volumeMetadata.mountPath, |
- function(entry) { |
- if (volumeMetadata.volumeType === util.VolumeType.DRIVE) { |
- // After file system is mounted, we "read" drive grand root |
- // entry at first. This triggers full feed fetch on background. |
- // Note: we don't need to handle errors here, because even if |
- // it fails, accessing to some path later will just become |
- // a fast-fetch and it re-triggers full-feed fetch. |
- entry.createReader().readEntries( |
- function() { /* do nothing */ }, |
- function(error) { |
- console.error( |
- 'Triggering full feed fetch is failed: ' + |
- util.getFileErrorMnemonic(error.code)); |
- }); |
- } |
- callback(new VolumeInfo( |
- volumeMetadata.volumeType, |
- volumeMetadata.mountPath, |
- entry, |
- volumeMetadata.mountCondition, |
- volumeMetadata.deviceType, |
- volumeMetadata.isReadOnly)); |
- }, |
- function(fileError) { |
- console.error('Root entry is not found: ' + |
- mountPath + ', ' + util.getFileErrorMnemonic(fileError.code)); |
- callback(new VolumeInfo( |
- volumeMetadata.volumeType, |
- volumeMetadata.mountPath, |
- null, // Root entry is not found. |
- volumeMetadata.mountCondition, |
- volumeMetadata.deviceType, |
- volumeMetadata.isReadOnly)); |
- }); |
-}; |
- |
-/** |
- * The order of the volume list based on root type. |
- * @type {Array.<string>} |
- * @const |
- * @private |
- */ |
-volumeManagerUtil.volumeListOrder_ = [ |
- RootType.DRIVE, RootType.DOWNLOADS, RootType.ARCHIVE, RootType.REMOVABLE |
-]; |
- |
-/** |
- * Compares mount paths to sort the volume list order. |
- * @param {string} mountPath1 The mount path for the first volume. |
- * @param {string} mountPath2 The mount path for the second volume. |
- * @return {number} 0 if mountPath1 and mountPath2 are same, -1 if VolumeInfo |
- * mounted at mountPath1 should be listed before the one mounted at |
- * mountPath2, otherwise 1. |
- */ |
-volumeManagerUtil.compareMountPath = function(mountPath1, mountPath2) { |
- var order1 = volumeManagerUtil.volumeListOrder_.indexOf( |
- PathUtil.getRootType(mountPath1)); |
- var order2 = volumeManagerUtil.volumeListOrder_.indexOf( |
- PathUtil.getRootType(mountPath2)); |
- if (order1 != order2) |
- return order1 < order2 ? -1 : 1; |
- |
- if (mountPath1 != mountPath2) |
- return mountPath1 < mountPath2 ? -1 : 1; |
- |
- // The path is same. |
- return 0; |
-}; |
- |
-/** |
- * The container of the VolumeInfo for each mounted volume. |
- * @constructor |
- */ |
-function VolumeInfoList() { |
- /** |
- * Holds VolumeInfo instances. |
- * @type {cr.ui.ArrayDataModel} |
- * @private |
- */ |
- this.model_ = new cr.ui.ArrayDataModel([]); |
- |
- Object.freeze(this); |
-} |
- |
-VolumeInfoList.prototype = { |
- get length() { return this.model_.length; } |
-}; |
- |
-/** |
- * Adds the event listener to listen the change of volume info. |
- * @param {string} type The name of the event. |
- * @param {function(Event)} handler The handler for the event. |
- */ |
-VolumeInfoList.prototype.addEventListener = function(type, handler) { |
- this.model_.addEventListener(type, handler); |
-}; |
- |
-/** |
- * Removes the event listener. |
- * @param {string} type The name of the event. |
- * @param {function(Event)} handler The handler to be removed. |
- */ |
-VolumeInfoList.prototype.removeEventListener = function(type, handler) { |
- this.model_.removeEventListener(type, handler); |
-}; |
- |
-/** |
- * Adds the volumeInfo to the appropriate position. If there already exists, |
- * just replaces it. |
- * @param {VolumeInfo} volumeInfo The information of the new volume. |
- */ |
-VolumeInfoList.prototype.add = function(volumeInfo) { |
- var index = this.findLowerBoundIndex_(volumeInfo.mountPath); |
- if (index < this.length && |
- this.item(index).mountPath == volumeInfo.mountPath) { |
- // Replace the VolumeInfo. |
- this.model_.splice(index, 1, volumeInfo); |
- } else { |
- // Insert the VolumeInfo. |
- this.model_.splice(index, 0, volumeInfo); |
- } |
-}; |
- |
-/** |
- * Removes the VolumeInfo of the volume mounted at mountPath. |
- * @param {string} mountPath The path to the location where the volume is |
- * mounted. |
- */ |
-VolumeInfoList.prototype.remove = function(mountPath) { |
- var index = this.findLowerBoundIndex_(mountPath); |
- if (index < this.length && this.item(index).mountPath == mountPath) |
- this.model_.splice(index, 1); |
-}; |
- |
-/** |
- * Searches the information of the volume mounted at mountPath. |
- * @param {string} mountPath The path to the location where the volume is |
- * mounted. |
- * @return {VolumeInfo} The volume's information, or null if not found. |
- */ |
-VolumeInfoList.prototype.find = function(mountPath) { |
- var index = this.findLowerBoundIndex_(mountPath); |
- if (index < this.length && this.item(index).mountPath == mountPath) |
- return this.item(index); |
- |
- // Not found. |
- return null; |
-}; |
- |
-/** |
- * @param {string} mountPath The mount path of searched volume. |
- * @return {number} The index of the volume if found, or the inserting |
- * position of the volume. |
- * @private |
- */ |
-VolumeInfoList.prototype.findLowerBoundIndex_ = function(mountPath) { |
- // Assuming the number of elements in the array data model is very small |
- // in most cases, use simple linear search, here. |
- for (var i = 0; i < this.length; i++) { |
- if (volumeManagerUtil.compareMountPath( |
- this.item(i).mountPath, mountPath) >= 0) |
- return i; |
- } |
- return this.length; |
-}; |
- |
-/** |
- * @param {number} index The index of the volume in the list. |
- * @return {VolumeInfo} The VolumeInfo instance. |
- */ |
-VolumeInfoList.prototype.item = function(index) { |
- return this.model_.item(index); |
-}; |
- |
-/** |
- * VolumeManager is responsible for tracking list of mounted volumes. |
- * |
- * @constructor |
- * @extends {cr.EventTarget} |
- */ |
-function VolumeManager() { |
- /** |
- * The list of archives requested to mount. We will show contents once |
- * archive is mounted, but only for mounts from within this filebrowser tab. |
- * @type {Object.<string, Object>} |
- * @private |
- */ |
- this.requests_ = {}; |
- |
- /** |
- * The list of VolumeInfo instances for each mounted volume. |
- * @type {VolumeInfoList} |
- */ |
- this.volumeInfoList = new VolumeInfoList(); |
- |
- // The status should be merged into VolumeManager. |
- // TODO(hidehiko): Remove them after the migration. |
- this.driveConnectionState_ = { |
- type: util.DriveConnectionType.OFFLINE, |
- reasons: [util.DriveConnectionReason.NO_SERVICE] |
- }; |
- |
- chrome.fileBrowserPrivate.onDriveConnectionStatusChanged.addListener( |
- this.onDriveConnectionStatusChanged_.bind(this)); |
- this.onDriveConnectionStatusChanged_(); |
-} |
- |
-/** |
- * Invoked when the drive connection status is changed. |
- * @private_ |
- */ |
-VolumeManager.prototype.onDriveConnectionStatusChanged_ = function() { |
- chrome.fileBrowserPrivate.getDriveConnectionState(function(state) { |
- this.driveConnectionState_ = state; |
- cr.dispatchSimpleEvent(this, 'drive-connection-changed'); |
- }.bind(this)); |
-}; |
- |
-/** |
- * Returns the drive connection state. |
- * @return {util.DriveConnectionType} Connection type. |
- */ |
-VolumeManager.prototype.getDriveConnectionState = function() { |
- return this.driveConnectionState_; |
-}; |
- |
-/** |
- * VolumeManager extends cr.EventTarget. |
- */ |
-VolumeManager.prototype.__proto__ = cr.EventTarget.prototype; |
- |
-/** |
- * Time in milliseconds that we wait a response for. If no response on |
- * mount/unmount received the request supposed failed. |
- */ |
-VolumeManager.TIMEOUT = 15 * 60 * 1000; |
- |
-/** |
- * Queue to run getInstance sequentially. |
- * @type {AsyncUtil.Queue} |
- * @private |
- */ |
-VolumeManager.getInstanceQueue_ = new AsyncUtil.Queue(); |
- |
-/** |
- * The singleton instance of VolumeManager. Initialized by the first invocation |
- * of getInstance(). |
- * @type {VolumeManager} |
- * @private |
- */ |
-VolumeManager.instance_ = null; |
- |
-/** |
- * Returns the VolumeManager instance asynchronously. If it is not created or |
- * under initialization, it will waits for the finish of the initialization. |
- * @param {function(VolumeManager)} callback Called with the VolumeManager |
- * instance. |
- */ |
-VolumeManager.getInstance = function(callback) { |
- VolumeManager.getInstanceQueue_.run(function(continueCallback) { |
- if (VolumeManager.instance_) { |
- callback(VolumeManager.instance_); |
- continueCallback(); |
- return; |
- } |
- |
- VolumeManager.instance_ = new VolumeManager(); |
- VolumeManager.instance_.initialize_(function() { |
- callback(VolumeManager.instance_); |
- continueCallback(); |
- }); |
- }); |
-}; |
- |
-/** |
- * Initializes mount points. |
- * @param {function()} callback Called upon the completion of the |
- * initialization. |
- * @private |
- */ |
-VolumeManager.prototype.initialize_ = function(callback) { |
- chrome.fileBrowserPrivate.getVolumeMetadataList(function(volumeMetadataList) { |
- // Create VolumeInfo for each volume. |
- var group = new AsyncUtil.Group(); |
- for (var i = 0; i < volumeMetadataList.length; i++) { |
- group.add(function(volumeMetadata, continueCallback) { |
- volumeManagerUtil.createVolumeInfo( |
- volumeMetadata, |
- function(volumeInfo) { |
- this.volumeInfoList.add(volumeInfo); |
- if (volumeMetadata.volumeType === util.VolumeType.DRIVE) |
- this.onDriveConnectionStatusChanged_(); |
- continueCallback(); |
- }.bind(this)); |
- }.bind(this, volumeMetadataList[i])); |
- } |
- |
- // Then, finalize the initialization. |
- group.run(function() { |
- // Subscribe to the mount completed event when mount points initialized. |
- chrome.fileBrowserPrivate.onMountCompleted.addListener( |
- this.onMountCompleted_.bind(this)); |
- callback(); |
- }.bind(this)); |
- }.bind(this)); |
-}; |
- |
-/** |
- * Event handler called when some volume was mounted or unmounted. |
- * @param {MountCompletedEvent} event Received event. |
- * @private |
- */ |
-VolumeManager.prototype.onMountCompleted_ = function(event) { |
- if (event.eventType === 'mount') { |
- if (event.volumeMetadata.mountPath) { |
- var requestKey = this.makeRequestKey_( |
- 'mount', |
- event.volumeMetadata.sourcePath); |
- |
- var error = event.status === 'success' ? '' : event.status; |
- |
- volumeManagerUtil.createVolumeInfo( |
- event.volumeMetadata, |
- function(volumeInfo) { |
- this.volumeInfoList.add(volumeInfo); |
- this.finishRequest_(requestKey, event.status, volumeInfo.mountPath); |
- |
- if (volumeInfo.volumeType === util.VolumeType.DRIVE) { |
- // Update the network connection status, because until the |
- // drive is initialized, the status is set to not ready. |
- // TODO(hidehiko): The connection status should be migrated into |
- // VolumeMetadata. |
- this.onDriveConnectionStatusChanged_(); |
- } |
- }.bind(this)); |
- } else { |
- console.warn('No mount path.'); |
- this.finishRequest_(requestKey, event.status); |
- } |
- } else if (event.eventType === 'unmount') { |
- var mountPath = event.volumeMetadata.mountPath; |
- volumeManagerUtil.validateMountPath(mountPath); |
- var status = event.status; |
- if (status === util.VolumeError.PATH_UNMOUNTED) { |
- console.warn('Volume already unmounted: ', mountPath); |
- status = 'success'; |
- } |
- var requestKey = this.makeRequestKey_('unmount', mountPath); |
- var requested = requestKey in this.requests_; |
- if (event.status === 'success' && !requested && |
- this.volumeInfoList.find(mountPath)) { |
- console.warn('Mounted volume without a request: ', mountPath); |
- var e = new Event('externally-unmounted'); |
- e.mountPath = mountPath; |
- this.dispatchEvent(e); |
- } |
- this.finishRequest_(requestKey, status); |
- |
- if (event.status === 'success') |
- this.volumeInfoList.remove(mountPath); |
- } |
-}; |
- |
-/** |
- * Creates string to match mount events with requests. |
- * @param {string} requestType 'mount' | 'unmount'. TODO(hidehiko): Replace by |
- * enum. |
- * @param {string} path Source path provided by API for mount request, or |
- * mount path for unmount request. |
- * @return {string} Key for |this.requests_|. |
- * @private |
- */ |
-VolumeManager.prototype.makeRequestKey_ = function(requestType, path) { |
- return requestType + ':' + path; |
-}; |
- |
-/** |
- * @param {string} fileUrl File url to the archive file. |
- * @param {function(string)} successCallback Success callback. |
- * @param {function(util.VolumeError)} errorCallback Error callback. |
- */ |
-VolumeManager.prototype.mountArchive = function( |
- fileUrl, successCallback, errorCallback) { |
- chrome.fileBrowserPrivate.addMount(fileUrl, function(sourcePath) { |
- console.info( |
- 'Mount request: url=' + fileUrl + '; sourceUrl=' + sourcePath); |
- var requestKey = this.makeRequestKey_('mount', sourcePath); |
- this.startRequest_(requestKey, successCallback, errorCallback); |
- }.bind(this)); |
-}; |
- |
-/** |
- * Unmounts volume. |
- * @param {string} mountPath Volume mounted path. |
- * @param {function(string)} successCallback Success callback. |
- * @param {function(util.VolumeError)} errorCallback Error callback. |
- */ |
-VolumeManager.prototype.unmount = function(mountPath, |
- successCallback, |
- errorCallback) { |
- volumeManagerUtil.validateMountPath(mountPath); |
- var volumeInfo = this.volumeInfoList.find(mountPath); |
- if (!volumeInfo) { |
- errorCallback(util.VolumeError.NOT_MOUNTED); |
- return; |
- } |
- |
- chrome.fileBrowserPrivate.removeMount(util.makeFilesystemUrl(mountPath)); |
- var requestKey = this.makeRequestKey_('unmount', volumeInfo.mountPath); |
- this.startRequest_(requestKey, successCallback, errorCallback); |
-}; |
- |
-/** |
- * Resolve the path to its entry. |
- * @param {string} path The path to be resolved. |
- * @param {function(Entry)} successCallback Called with the resolved entry on |
- * success. |
- * @param {function(FileError)} errorCallback Called on error. |
- */ |
-VolumeManager.prototype.resolvePath = function( |
- path, successCallback, errorCallback) { |
- // Make sure the path is in the mounted volume. |
- var mountPath = PathUtil.isDriveBasedPath(path) ? |
- RootDirectory.DRIVE : PathUtil.getRootPath(path); |
- var volumeInfo = this.getVolumeInfo(mountPath); |
- if (!volumeInfo || !volumeInfo.root) { |
- errorCallback(util.createFileError(FileError.NOT_FOUND_ERR)); |
- return; |
- } |
- |
- webkitResolveLocalFileSystemURL( |
- util.makeFilesystemUrl(path), successCallback, errorCallback); |
-}; |
- |
-/** |
- * @param {string} mountPath Volume mounted path. |
- * @return {VolumeInfo} The data about the volume. |
- */ |
-VolumeManager.prototype.getVolumeInfo = function(mountPath) { |
- volumeManagerUtil.validateMountPath(mountPath); |
- return this.volumeInfoList.find(mountPath); |
-}; |
- |
-/** |
- * @param {string} key Key produced by |makeRequestKey_|. |
- * @param {function(string)} successCallback To be called when request finishes |
- * successfully. |
- * @param {function(util.VolumeError)} errorCallback To be called when |
- * request fails. |
- * @private |
- */ |
-VolumeManager.prototype.startRequest_ = function(key, |
- successCallback, errorCallback) { |
- if (key in this.requests_) { |
- var request = this.requests_[key]; |
- request.successCallbacks.push(successCallback); |
- request.errorCallbacks.push(errorCallback); |
- } else { |
- this.requests_[key] = { |
- successCallbacks: [successCallback], |
- errorCallbacks: [errorCallback], |
- |
- timeout: setTimeout(this.onTimeout_.bind(this, key), |
- VolumeManager.TIMEOUT) |
- }; |
- } |
-}; |
- |
-/** |
- * Called if no response received in |TIMEOUT|. |
- * @param {string} key Key produced by |makeRequestKey_|. |
- * @private |
- */ |
-VolumeManager.prototype.onTimeout_ = function(key) { |
- this.invokeRequestCallbacks_(this.requests_[key], |
- util.VolumeError.TIMEOUT); |
- delete this.requests_[key]; |
-}; |
- |
-/** |
- * @param {string} key Key produced by |makeRequestKey_|. |
- * @param {util.VolumeError|'success'} status Status received from the API. |
- * @param {string=} opt_mountPath Mount path. |
- * @private |
- */ |
-VolumeManager.prototype.finishRequest_ = function(key, status, opt_mountPath) { |
- var request = this.requests_[key]; |
- if (!request) |
- return; |
- |
- clearTimeout(request.timeout); |
- this.invokeRequestCallbacks_(request, status, opt_mountPath); |
- delete this.requests_[key]; |
-}; |
- |
-/** |
- * @param {Object} request Structure created in |startRequest_|. |
- * @param {util.VolumeError|string} status If status == 'success' |
- * success callbacks are called. |
- * @param {string=} opt_mountPath Mount path. Required if success. |
- * @private |
- */ |
-VolumeManager.prototype.invokeRequestCallbacks_ = function(request, status, |
- opt_mountPath) { |
- var callEach = function(callbacks, self, args) { |
- for (var i = 0; i < callbacks.length; i++) { |
- callbacks[i].apply(self, args); |
- } |
- }; |
- if (status == 'success') { |
- callEach(request.successCallbacks, this, [opt_mountPath]); |
- } else { |
- volumeManagerUtil.validateError(status); |
- callEach(request.errorCallbacks, this, [status]); |
- } |
-}; |