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

Unified Diff: ui/file_manager/file_manager/background/js/volume_manager_impl.js

Issue 2292873003: Divide volume_manager.js into files for each classes and extract interfaces from them. (Closed)
Patch Set: Address comments. Created 4 years, 3 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: ui/file_manager/file_manager/background/js/volume_manager_impl.js
diff --git a/ui/file_manager/file_manager/background/js/volume_manager_impl.js b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
new file mode 100644
index 0000000000000000000000000000000000000000..84f578aa3d0de6afb5ae97dfbaed9a54569de794
--- /dev/null
+++ b/ui/file_manager/file_manager/background/js/volume_manager_impl.js
@@ -0,0 +1,420 @@
+// Copyright 2016 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.
+
+/**
+ * VolumeManager is responsible for tracking list of mounted volumes.
+ *
+ * @constructor
+ * @implements {VolumeManager}
+ * @extends {cr.EventTarget}
+ */
+function VolumeManagerImpl() {
+ /** @override */
+ this.volumeInfoList = new VolumeInfoListImpl();
+
+ /**
+ * 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<Object>}
+ * @private
+ */
+ this.requests_ = {};
+
+ /**
+ * Queue for mounting.
+ * @type {AsyncUtil.Queue}
+ * @private
+ */
+ this.mountQueue_ = new AsyncUtil.Queue();
+
+ // The status should be merged into VolumeManager.
+ // TODO(hidehiko): Remove them after the migration.
+ /**
+ * Connection state of the Drive.
+ * @type {VolumeManagerCommon.DriveConnectionState}
+ * @private
+ */
+ this.driveConnectionState_ = {
+ type: VolumeManagerCommon.DriveConnectionType.OFFLINE,
+ reason: VolumeManagerCommon.DriveConnectionReason.NO_SERVICE,
+ hasCellularNetworkAccess: false
+ };
+
+ chrome.fileManagerPrivate.onDriveConnectionStatusChanged.addListener(
+ this.onDriveConnectionStatusChanged_.bind(this));
+ this.onDriveConnectionStatusChanged_();
+}
+
+/**
+ * Invoked when the drive connection status is changed.
+ * @private
+ */
+VolumeManagerImpl.prototype.onDriveConnectionStatusChanged_ = function() {
+ chrome.fileManagerPrivate.getDriveConnectionState(function(state) {
+ this.driveConnectionState_ = state;
+ cr.dispatchSimpleEvent(this, 'drive-connection-changed');
+ }.bind(this));
+};
+
+/** @override */
+VolumeManagerImpl.prototype.getDriveConnectionState = function() {
+ return this.driveConnectionState_;
+};
+
+/**
+ * VolumeManager extends cr.EventTarget.
+ */
+VolumeManagerImpl.prototype.__proto__ = cr.EventTarget.prototype;
+
+/**
+ * Adds new volume info from the given volumeMetadata. If the corresponding
+ * volume info has already been added, the volumeMetadata is ignored.
+ * @param {!VolumeMetadata} volumeMetadata
+ * @return {!Promise<!VolumeInfo>}
+ * @private
+ */
+VolumeManagerImpl.prototype.addVolumeMetadata_ = function(volumeMetadata) {
+ return volumeManagerUtil.createVolumeInfo(volumeMetadata).then(
+ /**
+ * @param {!VolumeInfo} volumeInfo
+ * @return {!VolumeInfo}
+ */
+ function(volumeInfo) {
+ // We don't show Downloads and Drive on volume list if they have mount
+ // error, since users can do nothing in this situation.
+ // We show Removable and Provided volumes regardless of mount error so
+ // that users can unmount or format the volume.
+ // TODO(fukino): Once Files.app get ready, show erroneous Drive volume
+ // so that users can see auth warning banner on the volume.
+ // crbug.com/517772.
+ var shouldShow = true;
+ switch (volumeInfo.volumeType) {
+ case VolumeManagerCommon.VolumeType.DOWNLOADS:
+ case VolumeManagerCommon.VolumeType.DRIVE:
+ shouldShow = !!volumeInfo.fileSystem;
+ break;
+ }
+ if (shouldShow &&
+ this.volumeInfoList.findIndex(volumeInfo.volumeId) === -1) {
+ this.volumeInfoList.add(volumeInfo);
+
+ // Update the network connection status, because until the drive is
+ // initialized, the status is set to not ready.
+ // TODO(mtomasz): The connection status should be migrated into
+ // VolumeMetadata.
+ if (volumeMetadata.volumeType ===
+ VolumeManagerCommon.VolumeType.DRIVE) {
+ this.onDriveConnectionStatusChanged_();
+ }
+ }
+ return volumeInfo;
+ }.bind(this));
+};
+
+/**
+ * Initializes mount points.
+ * @param {function()} callback Called upon the completion of the
+ * initialization.
+ * @private
+ */
+VolumeManagerImpl.prototype.initialize_ = function(callback) {
+ chrome.fileManagerPrivate.onMountCompleted.addListener(
+ this.onMountCompleted_.bind(this));
+ console.debug('Requesting volume list.');
+ chrome.fileManagerPrivate.getVolumeMetadataList(function(volumeMetadataList) {
+ console.debug('Volume list fetched with: ' + volumeMetadataList.length +
+ ' items.');
+ // We must subscribe to the mount completed event in the callback of
+ // getVolumeMetadataList. crbug.com/330061.
+ // But volumes reported by onMountCompleted events must be added after the
+ // volumes in the volumeMetadataList are mounted. crbug.com/135477.
+ this.mountQueue_.run(function(inCallback) {
+ // Create VolumeInfo for each volume.
+ Promise.all(
+ volumeMetadataList.map(function(volumeMetadata) {
+ console.debug(
+ 'Initializing volume: ' + volumeMetadata.volumeId);
+ return this.addVolumeMetadata_(volumeMetadata).then(
+ function(volumeInfo) {
+ console.debug('Initialized volume: ' + volumeInfo.volumeId);
+ });
+ }.bind(this)))
+ .then(function() {
+ console.debug('Initialized all volumes.');
+ // Call the callback of the initialize function.
+ callback();
+ // Call the callback of AsyncQueue. Maybe it invokes callbacks
+ // registered by mountCompleted events.
+ inCallback();
+ });
+ }.bind(this));
+ }.bind(this));
+};
+
+/**
+ * Event handler called when some volume was mounted or unmounted.
+ * @param {MountCompletedEvent} event Received event.
+ * @private
+ */
+VolumeManagerImpl.prototype.onMountCompleted_ = function(event) {
+ this.mountQueue_.run(function(callback) {
+ switch (event.eventType) {
+ case 'mount':
+ var requestKey = this.makeRequestKey_(
+ 'mount',
+ event.volumeMetadata.sourcePath || '');
+
+ if (event.status === 'success' ||
+ event.status ===
+ VolumeManagerCommon.VolumeError.UNKNOWN_FILESYSTEM ||
+ event.status ===
+ VolumeManagerCommon.VolumeError.UNSUPPORTED_FILESYSTEM) {
+ this.addVolumeMetadata_(event.volumeMetadata).then(
+ function(volumeInfo) {
+ this.finishRequest_(requestKey, event.status, volumeInfo);
+ callback();
+ }.bind(this));
+ } else {
+ console.warn('Failed to mount a volume: ' + event.status);
+ this.finishRequest_(requestKey, event.status);
+ callback();
+ }
+ break;
+
+ case 'unmount':
+ var volumeId = event.volumeMetadata.volumeId;
+ var status = event.status;
+ if (status === VolumeManagerCommon.VolumeError.PATH_UNMOUNTED) {
+ console.warn('Volume already unmounted: ', volumeId);
+ status = 'success';
+ }
+ var requestKey = this.makeRequestKey_('unmount', volumeId);
+ var requested = requestKey in this.requests_;
+ var volumeInfoIndex =
+ this.volumeInfoList.findIndex(volumeId);
+ var volumeInfo = volumeInfoIndex !== -1 ?
+ this.volumeInfoList.item(volumeInfoIndex) : null;
+ if (event.status === 'success' && !requested && volumeInfo) {
+ console.warn('Mounted volume without a request: ' + volumeId);
+ var e = new Event('externally-unmounted');
+ e.volumeInfo = volumeInfo;
+ this.dispatchEvent(e);
+ }
+
+ this.finishRequest_(requestKey, status);
+ if (event.status === 'success')
+ this.volumeInfoList.remove(event.volumeMetadata.volumeId);
+ callback();
+ break;
+ }
+ }.bind(this));
+};
+
+/**
+ * Creates string to match mount events with requests.
+ * @param {string} requestType 'mount' | 'unmount'. TODO(hidehiko): Replace by
+ * enum.
+ * @param {string} argument Argument describing the request, eg. source file
+ * path of the archive to be mounted, or a volumeId for unmounting.
+ * @return {string} Key for |this.requests_|.
+ * @private
+ */
+VolumeManagerImpl.prototype.makeRequestKey_ = function(requestType, argument) {
+ return requestType + ':' + argument;
+};
+
+/** @override */
+VolumeManagerImpl.prototype.mountArchive = function(
+ fileUrl, successCallback, errorCallback) {
+ chrome.fileManagerPrivate.addMount(fileUrl, function(sourcePath) {
+ console.info(
+ 'Mount request: url=' + fileUrl + '; sourcePath=' + sourcePath);
+ var requestKey = this.makeRequestKey_('mount', sourcePath);
+ this.startRequest_(requestKey, successCallback, errorCallback);
+ }.bind(this));
+};
+
+/** @override */
+VolumeManagerImpl.prototype.unmount = function(volumeInfo,
+ successCallback,
+ errorCallback) {
+ chrome.fileManagerPrivate.removeMount(volumeInfo.volumeId);
+ var requestKey = this.makeRequestKey_('unmount', volumeInfo.volumeId);
+ this.startRequest_(requestKey, successCallback, errorCallback);
+};
+
+/** @override */
+VolumeManagerImpl.prototype.configure = function(volumeInfo) {
+ return new Promise(function(fulfill, reject) {
+ chrome.fileManagerPrivate.configureVolume(
+ volumeInfo.volumeId,
+ function() {
+ if (chrome.runtime.lastError)
+ reject(chrome.runtime.lastError.message);
+ else
+ fulfill();
+ });
+ });
+};
+
+/** @override */
+VolumeManagerImpl.prototype.getVolumeInfo = function(entry) {
+ return this.volumeInfoList.findByEntry(entry);
+};
+
+/** @override */
+VolumeManagerImpl.prototype.getCurrentProfileVolumeInfo = function(volumeType) {
+ for (var i = 0; i < this.volumeInfoList.length; i++) {
+ var volumeInfo = this.volumeInfoList.item(i);
+ if (volumeInfo.profile.isCurrentProfile &&
+ volumeInfo.volumeType === volumeType)
+ return volumeInfo;
+ }
+ return null;
+};
+
+/** @override */
+VolumeManagerImpl.prototype.getLocationInfo = function(entry) {
+ var volumeInfo = this.volumeInfoList.findByEntry(entry);
+ if (!volumeInfo)
+ return null;
+
+ if (util.isFakeEntry(entry)) {
+ return new EntryLocationImpl(
+ volumeInfo,
+ entry.rootType,
+ true /* the entry points a root directory. */,
+ true /* fake entries are read only. */);
+ }
+
+ var rootType;
+ var isReadOnly;
+ var isRootEntry;
+ if (volumeInfo.volumeType === VolumeManagerCommon.VolumeType.DRIVE) {
+ // For Drive, the roots are /root and /other, instead of /. Root URLs
+ // contain trailing slashes.
+ if (entry.fullPath == '/root' || entry.fullPath.indexOf('/root/') === 0) {
+ rootType = VolumeManagerCommon.RootType.DRIVE;
+ isReadOnly = volumeInfo.isReadOnly;
+ isRootEntry = entry.fullPath === '/root';
+ } else if (entry.fullPath == '/other' ||
+ entry.fullPath.indexOf('/other/') === 0) {
+ rootType = VolumeManagerCommon.RootType.DRIVE_OTHER;
+ isReadOnly = true;
+ isRootEntry = entry.fullPath === '/other';
+ } else {
+ // Accessing Drive files outside of /drive/root and /drive/other is not
+ // allowed, but can happen. Therefore returning null.
+ return null;
+ }
+ } else {
+ switch (volumeInfo.volumeType) {
+ case VolumeManagerCommon.VolumeType.DOWNLOADS:
+ rootType = VolumeManagerCommon.RootType.DOWNLOADS;
+ break;
+ case VolumeManagerCommon.VolumeType.REMOVABLE:
+ rootType = VolumeManagerCommon.RootType.REMOVABLE;
+ break;
+ case VolumeManagerCommon.VolumeType.ARCHIVE:
+ rootType = VolumeManagerCommon.RootType.ARCHIVE;
+ break;
+ case VolumeManagerCommon.VolumeType.MTP:
+ rootType = VolumeManagerCommon.RootType.MTP;
+ break;
+ case VolumeManagerCommon.VolumeType.PROVIDED:
+ rootType = VolumeManagerCommon.RootType.PROVIDED;
+ break;
+ default:
+ // Programming error, throw an exception.
+ throw new Error('Invalid volume type: ' + volumeInfo.volumeType);
+ }
+ isReadOnly = volumeInfo.isReadOnly;
+ isRootEntry = util.isSameEntry(entry, volumeInfo.fileSystem.root);
+ }
+
+ return new EntryLocationImpl(volumeInfo, rootType, isRootEntry, isReadOnly);
+};
+
+/**
+ * @param {string} key Key produced by |makeRequestKey_|.
+ * @param {function(VolumeInfo)} successCallback To be called when the request
+ * finishes successfully.
+ * @param {function(VolumeManagerCommon.VolumeError)} errorCallback To be called
+ * when the request fails.
+ * @private
+ */
+VolumeManagerImpl.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),
+ volumeManagerUtil.TIMEOUT)
+ };
+ }
+};
+
+/**
+ * Called if no response received in |TIMEOUT|.
+ * @param {string} key Key produced by |makeRequestKey_|.
+ * @private
+ */
+VolumeManagerImpl.prototype.onTimeout_ = function(key) {
+ this.invokeRequestCallbacks_(this.requests_[key],
+ VolumeManagerCommon.VolumeError.TIMEOUT);
+ delete this.requests_[key];
+};
+
+/**
+ * @param {string} key Key produced by |makeRequestKey_|.
+ * @param {VolumeManagerCommon.VolumeError|string} status Status received
+ * from the API.
+ * @param {VolumeInfo=} opt_volumeInfo Volume info of the mounted volume.
+ * @private
+ */
+VolumeManagerImpl.prototype.finishRequest_ =
+ function(key, status, opt_volumeInfo) {
+ var request = this.requests_[key];
+ if (!request)
+ return;
+
+ clearTimeout(request.timeout);
+ this.invokeRequestCallbacks_(request, status, opt_volumeInfo);
+ delete this.requests_[key];
+};
+
+/**
+ * @param {Object} request Structure created in |startRequest_|.
+ * @param {VolumeManagerCommon.VolumeError|string} status If status ===
+ * 'success' success callbacks are called.
+ * @param {VolumeInfo=} opt_volumeInfo Volume info of the mounted volume.
+ * @private
+ */
+VolumeManagerImpl.prototype.invokeRequestCallbacks_ = function(
+ request, status, opt_volumeInfo) {
+ 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_volumeInfo]);
+ } else {
+ volumeManagerUtil.validateError(status);
+ callEach(request.errorCallbacks, this, [status]);
+ }
+};
+
+/** @override */
+VolumeManagerImpl.prototype.toString = function() {
+ return 'VolumeManager\n' +
+ '- MountQueue_:\n' +
+ ' ' + this.mountQueue_.toString().replace(/\n/g, '\n ');
+};

Powered by Google App Engine
This is Rietveld 408576698