1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
8 * Represents each volume, such as "drive", "download directory", each "USB
9 * flush storage", or "mounted zip archive" etc.
11 * @param {VolumeManagerCommon.VolumeType} volumeType The type of the volume.
12 * @param {string} volumeId ID of the volume.
13 * @param {DOMFileSystem} fileSystem The file system object for this volume.
14 * @param {string} error The error if an error is found.
15 * @param {string} deviceType The type of device ('usb'|'sd'|'optical'|'mobile'
16 * |'unknown') (as defined in chromeos/disks/disk_mount_manager.cc).
18 * @param {boolean} isReadOnly True if the volume is read only.
19 * @param {!{displayName:string, isCurrentProfile:boolean}} profile Profile
21 * @param {string} label Label of the volume.
33 this.volumeType_ = volumeType;
34 this.volumeId_ = volumeId;
35 this.fileSystem_ = fileSystem;
37 this.displayRoot_ = null;
38 this.fakeEntries_ = {};
39 this.displayRoot_ = null;
40 this.displayRootPromise_ = null;
42 if (volumeType === VolumeManagerCommon.VolumeType.DRIVE) {
43 // TODO(mtomasz): Convert fake entries to DirectoryProvider.
44 this.fakeEntries_[VolumeManagerCommon.RootType.DRIVE_OFFLINE] = {
46 rootType: VolumeManagerCommon.RootType.DRIVE_OFFLINE,
47 toURL: function() { return 'fake-entry://drive_offline' }
49 this.fakeEntries_[VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME] = {
51 rootType: VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME,
52 toURL: function() { return 'fake-entry://drive_shared_with_me'; }
54 this.fakeEntries_[VolumeManagerCommon.RootType.DRIVE_RECENT] = {
56 rootType: VolumeManagerCommon.RootType.DRIVE_RECENT,
57 toURL: function() { return 'fake-entry://drive_recent'; }
61 // Note: This represents if the mounting of the volume is successfully done
62 // or not. (If error is empty string, the mount is successfully done).
63 // TODO(hidehiko): Rename to make this more understandable.
65 this.deviceType_ = deviceType;
66 this.isReadOnly_ = isReadOnly;
67 this.profile_ = Object.freeze(profile);
72 VolumeInfo.prototype = {
74 * @return {VolumeManagerCommon.VolumeType} Volume type.
77 return this.volumeType_;
80 * @return {string} Volume ID.
83 return this.volumeId_;
86 * @return {DOMFileSystem} File system object.
89 return this.fileSystem_;
92 * @return {DirectoryEntry} Display root path. It is null before finishing to
96 return this.displayRoot_;
99 * @return {Object.<string, Object>} Fake entries.
102 return this.fakeEntries_;
105 * @return {string} Error identifier.
111 * @return {string} Device type identifier.
114 return this.deviceType_;
117 * @return {boolean} Whether read only or not.
120 return this.isReadOnly_;
123 * @return {!{displayName:string, isCurrentProfile:boolean}} Profile data.
126 return this.profile_;
129 * @return {string} Label for the volume.
137 * Starts resolving the display root and obtains it. It may take long time for
138 * Drive. Once resolved, it is cached.
140 * @param {function(DirectoryEntry)} onSuccess Success callback with the
141 * display root directory as an argument.
142 * @param {function(FileError)} onFailure Failure callback.
144 VolumeInfo.prototype.resolveDisplayRoot = function(onSuccess, onFailure) {
145 if (!this.displayRootPromise_) {
146 // TODO(mtomasz): Do not add VolumeInfo which failed to resolve root, and
147 // remove this if logic. Call onSuccess() always, instead.
148 if (this.volumeType !== VolumeManagerCommon.VolumeType.DRIVE) {
149 if (this.fileSystem_)
150 this.displayRootPromise_ = Promise.resolve(this.fileSystem_.root);
152 this.displayRootPromise_ = Promise.reject(this.error);
154 // For Drive, we need to resolve.
155 var displayRootURL = this.fileSystem_.root.toURL() + '/root';
156 this.displayRootPromise_ = new Promise(
157 webkitResolveLocalFileSystemURL.bind(null, displayRootURL));
160 // Store the obtained displayRoot.
161 this.displayRootPromise_.then(function(displayRoot) {
162 this.displayRoot_ = displayRoot;
165 this.displayRootPromise_.then(onSuccess, onFailure);
169 * Utilities for volume manager implementation.
171 var volumeManagerUtil = {};
174 * Throws an Error when the given error is not in
175 * VolumeManagerCommon.VolumeError.
177 * @param {VolumeManagerCommon.VolumeError} error Status string usually received
180 volumeManagerUtil.validateError = function(error) {
181 for (var key in VolumeManagerCommon.VolumeError) {
182 if (error === VolumeManagerCommon.VolumeError[key])
186 throw new Error('Invalid mount error: ' + error);
190 * Builds the VolumeInfo data from VolumeMetadata.
191 * @param {VolumeMetadata} volumeMetadata Metadata instance for the volume.
192 * @param {function(VolumeInfo)} callback Called on completion.
194 volumeManagerUtil.createVolumeInfo = function(volumeMetadata, callback) {
196 switch (volumeMetadata.volumeType) {
197 case VolumeManagerCommon.VolumeType.DOWNLOADS:
198 localizedLabel = str('DOWNLOADS_DIRECTORY_LABEL');
200 case VolumeManagerCommon.VolumeType.DRIVE:
201 localizedLabel = str('DRIVE_DIRECTORY_LABEL');
204 localizedLabel = volumeMetadata.volumeId.split(':', 2)[1];
208 chrome.fileBrowserPrivate.requestFileSystem(
209 volumeMetadata.volumeId,
210 function(fileSystem) {
211 // TODO(mtomasz): chrome.runtime.lastError should have error reason.
213 console.error('File system not found: ' + volumeMetadata.volumeId);
214 callback(new VolumeInfo(
215 volumeMetadata.volumeType,
216 volumeMetadata.volumeId,
217 null, // File system is not found.
218 volumeMetadata.mountCondition,
219 volumeMetadata.deviceType,
220 volumeMetadata.isReadOnly,
221 volumeMetadata.profile,
225 if (volumeMetadata.volumeType ==
226 VolumeManagerCommon.VolumeType.DRIVE) {
227 // After file system is mounted, we "read" drive grand root
228 // entry at first. This triggers full feed fetch on background.
229 // Note: we don't need to handle errors here, because even if
230 // it fails, accessing to some path later will just become
231 // a fast-fetch and it re-triggers full-feed fetch.
232 fileSystem.root.createReader().readEntries(
233 function() { /* do nothing */ },
236 'Triggering full feed fetch is failed: ' + error.name);
239 callback(new VolumeInfo(
240 volumeMetadata.volumeType,
241 volumeMetadata.volumeId,
243 volumeMetadata.mountCondition,
244 volumeMetadata.deviceType,
245 volumeMetadata.isReadOnly,
246 volumeMetadata.profile,
252 * The order of the volume list based on root type.
253 * @type {Array.<VolumeManagerCommon.VolumeType>}
257 volumeManagerUtil.volumeListOrder_ = [
258 VolumeManagerCommon.VolumeType.DRIVE,
259 VolumeManagerCommon.VolumeType.DOWNLOADS,
260 VolumeManagerCommon.VolumeType.ARCHIVE,
261 VolumeManagerCommon.VolumeType.REMOVABLE,
262 VolumeManagerCommon.VolumeType.MTP,
263 VolumeManagerCommon.VolumeType.CLOUD_DEVICE
267 * Orders two volumes by volumeType and volumeId.
269 * The volumes at first are compared by volume type in the order of
270 * volumeListOrder_. Then they are compared by volume ID.
272 * @param {VolumeInfo} volumeInfo1 Volume info to be compared.
273 * @param {VolumeInfo} volumeInfo2 Volume info to be compared.
274 * @return {number} Returns -1 if volume1 < volume2, returns 1 if volume2 >
275 * volume1, returns 0 if volume1 === volume2.
278 volumeManagerUtil.compareVolumeInfo_ = function(volumeInfo1, volumeInfo2) {
280 volumeManagerUtil.volumeListOrder_.indexOf(volumeInfo1.volumeType);
282 volumeManagerUtil.volumeListOrder_.indexOf(volumeInfo2.volumeType);
283 if (typeIndex1 !== typeIndex2)
284 return typeIndex1 < typeIndex2 ? -1 : 1;
285 if (volumeInfo1.volumeId !== volumeInfo2.volumeId)
286 return volumeInfo1.volumeId < volumeInfo2.volumeId ? -1 : 1;
291 * The container of the VolumeInfo for each mounted volume.
294 function VolumeInfoList() {
295 var field = 'volumeType,volumeId';
298 * Holds VolumeInfo instances.
299 * @type {cr.ui.ArrayDataModel}
302 this.model_ = new cr.ui.ArrayDataModel([]);
303 this.model_.setCompareFunction(field, volumeManagerUtil.compareVolumeInfo_);
304 this.model_.sort(field, 'asc');
309 VolumeInfoList.prototype = {
310 get length() { return this.model_.length; }
314 * Adds the event listener to listen the change of volume info.
315 * @param {string} type The name of the event.
316 * @param {function(Event)} handler The handler for the event.
318 VolumeInfoList.prototype.addEventListener = function(type, handler) {
319 this.model_.addEventListener(type, handler);
323 * Removes the event listener.
324 * @param {string} type The name of the event.
325 * @param {function(Event)} handler The handler to be removed.
327 VolumeInfoList.prototype.removeEventListener = function(type, handler) {
328 this.model_.removeEventListener(type, handler);
332 * Adds the volumeInfo to the appropriate position. If there already exists,
334 * @param {VolumeInfo} volumeInfo The information of the new volume.
336 VolumeInfoList.prototype.add = function(volumeInfo) {
337 var index = this.findIndex(volumeInfo.volumeId);
339 this.model_.splice(index, 1, volumeInfo);
341 this.model_.push(volumeInfo);
345 * Removes the VolumeInfo having the given ID.
346 * @param {string} volumeId ID of the volume.
348 VolumeInfoList.prototype.remove = function(volumeId) {
349 var index = this.findIndex(volumeId);
351 this.model_.splice(index, 1);
355 * Obtains an index from the volume ID.
356 * @param {string} volumeId Volume ID.
357 * @return {number} Index of the volume.
359 VolumeInfoList.prototype.findIndex = function(volumeId) {
360 for (var i = 0; i < this.model_.length; i++) {
361 if (this.model_.item(i).volumeId === volumeId)
368 * Searches the information of the volume that contains the passed entry.
369 * @param {Entry|Object} entry Entry on the volume to be found.
370 * @return {VolumeInfo} The volume's information, or null if not found.
372 VolumeInfoList.prototype.findByEntry = function(entry) {
373 for (var i = 0; i < this.length; i++) {
374 var volumeInfo = this.item(i);
375 if (volumeInfo.fileSystem &&
376 util.isSameFileSystem(volumeInfo.fileSystem, entry.filesystem)) {
379 // Additionally, check fake entries.
380 for (var key in volumeInfo.fakeEntries_) {
381 var fakeEntry = volumeInfo.fakeEntries_[key];
382 if (util.isSameEntry(fakeEntry, entry))
390 * @param {number} index The index of the volume in the list.
391 * @return {VolumeInfo} The VolumeInfo instance.
393 VolumeInfoList.prototype.item = function(index) {
394 return this.model_.item(index);
398 * VolumeManager is responsible for tracking list of mounted volumes.
401 * @extends {cr.EventTarget}
403 function VolumeManager() {
405 * The list of archives requested to mount. We will show contents once
406 * archive is mounted, but only for mounts from within this filebrowser tab.
407 * @type {Object.<string, Object>}
413 * The list of VolumeInfo instances for each mounted volume.
414 * @type {VolumeInfoList}
416 this.volumeInfoList = new VolumeInfoList();
419 * Queue for mounting.
420 * @type {AsyncUtil.Queue}
423 this.mountQueue_ = new AsyncUtil.Queue();
425 // The status should be merged into VolumeManager.
426 // TODO(hidehiko): Remove them after the migration.
427 this.driveConnectionState_ = {
428 type: VolumeManagerCommon.DriveConnectionType.OFFLINE,
429 reason: VolumeManagerCommon.DriveConnectionReason.NO_SERVICE
432 chrome.fileBrowserPrivate.onDriveConnectionStatusChanged.addListener(
433 this.onDriveConnectionStatusChanged_.bind(this));
434 this.onDriveConnectionStatusChanged_();
438 * Invoked when the drive connection status is changed.
441 VolumeManager.prototype.onDriveConnectionStatusChanged_ = function() {
442 chrome.fileBrowserPrivate.getDriveConnectionState(function(state) {
443 this.driveConnectionState_ = state;
444 cr.dispatchSimpleEvent(this, 'drive-connection-changed');
449 * Returns the drive connection state.
450 * @return {VolumeManagerCommon.DriveConnectionType} Connection type.
452 VolumeManager.prototype.getDriveConnectionState = function() {
453 return this.driveConnectionState_;
457 * VolumeManager extends cr.EventTarget.
459 VolumeManager.prototype.__proto__ = cr.EventTarget.prototype;
462 * Time in milliseconds that we wait a response for. If no response on
463 * mount/unmount received the request supposed failed.
465 VolumeManager.TIMEOUT = 15 * 60 * 1000;
468 * Queue to run getInstance sequentially.
469 * @type {AsyncUtil.Queue}
472 VolumeManager.getInstanceQueue_ = new AsyncUtil.Queue();
475 * The singleton instance of VolumeManager. Initialized by the first invocation
477 * @type {VolumeManager}
480 VolumeManager.instance_ = null;
483 * Returns the VolumeManager instance asynchronously. If it is not created or
484 * under initialization, it will waits for the finish of the initialization.
485 * @param {function(VolumeManager)} callback Called with the VolumeManager
488 VolumeManager.getInstance = function(callback) {
489 VolumeManager.getInstanceQueue_.run(function(continueCallback) {
490 if (VolumeManager.instance_) {
491 callback(VolumeManager.instance_);
496 VolumeManager.instance_ = new VolumeManager();
497 VolumeManager.instance_.initialize_(function() {
498 callback(VolumeManager.instance_);
505 * Initializes mount points.
506 * @param {function()} callback Called upon the completion of the
510 VolumeManager.prototype.initialize_ = function(callback) {
511 chrome.fileBrowserPrivate.getVolumeMetadataList(function(volumeMetadataList) {
512 // We must subscribe to the mount completed event in the callback of
513 // getVolumeMetadataList. crbug.com/330061.
514 // But volumes reported by onMountCompleted events must be added after the
515 // volumes in the volumeMetadataList are mounted. crbug.com/135477.
516 this.mountQueue_.run(function(inCallback) {
517 // Create VolumeInfo for each volume.
518 var group = new AsyncUtil.Group();
519 for (var i = 0; i < volumeMetadataList.length; i++) {
520 group.add(function(volumeMetadata, continueCallback) {
521 volumeManagerUtil.createVolumeInfo(
523 function(volumeInfo) {
524 this.volumeInfoList.add(volumeInfo);
525 if (volumeMetadata.volumeType ===
526 VolumeManagerCommon.VolumeType.DRIVE)
527 this.onDriveConnectionStatusChanged_();
530 }.bind(this, volumeMetadataList[i]));
532 group.run(function() {
533 // Call the callback of the initialize function.
535 // Call the callback of AsyncQueue. Maybe it invokes callbacks
536 // registered by mountCompleted events.
541 chrome.fileBrowserPrivate.onMountCompleted.addListener(
542 this.onMountCompleted_.bind(this));
547 * Event handler called when some volume was mounted or unmounted.
548 * @param {MountCompletedEvent} event Received event.
551 VolumeManager.prototype.onMountCompleted_ = function(event) {
552 this.mountQueue_.run(function(callback) {
553 switch (event.eventType) {
555 var requestKey = this.makeRequestKey_(
557 event.volumeMetadata.sourcePath);
559 var error = event.status === 'success' ? '' : event.status;
560 if (!error || event.status === 'error_unknown_filesystem') {
561 volumeManagerUtil.createVolumeInfo(
562 event.volumeMetadata,
563 function(volumeInfo) {
564 this.volumeInfoList.add(volumeInfo);
565 this.finishRequest_(requestKey, event.status, volumeInfo);
567 if (volumeInfo.volumeType ===
568 VolumeManagerCommon.VolumeType.DRIVE) {
569 // Update the network connection status, because until the
570 // drive is initialized, the status is set to not ready.
571 // TODO(mtomasz): The connection status should be migrated
572 // into VolumeMetadata.
573 this.onDriveConnectionStatusChanged_();
578 console.warn('Failed to mount a volume: ' + event.status);
579 this.finishRequest_(requestKey, event.status);
585 var volumeId = event.volumeMetadata.volumeId;
586 var status = event.status;
587 if (status === VolumeManagerCommon.VolumeError.PATH_UNMOUNTED) {
588 console.warn('Volume already unmounted: ', volumeId);
591 var requestKey = this.makeRequestKey_('unmount', volumeId);
592 var requested = requestKey in this.requests_;
593 var volumeInfoIndex =
594 this.volumeInfoList.findIndex(volumeId);
595 var volumeInfo = volumeInfoIndex !== -1 ?
596 this.volumeInfoList.item(volumeInfoIndex) : null;
597 if (event.status === 'success' && !requested && volumeInfo) {
598 console.warn('Mounted volume without a request: ' + volumeId);
599 var e = new Event('externally-unmounted');
600 e.volumeInfo = volumeInfo;
601 this.dispatchEvent(e);
604 this.finishRequest_(requestKey, status);
605 if (event.status === 'success')
606 this.volumeInfoList.remove(event.volumeMetadata.volumeId);
614 * Creates string to match mount events with requests.
615 * @param {string} requestType 'mount' | 'unmount'. TODO(hidehiko): Replace by
617 * @param {string} argument Argument describing the request, eg. source file
618 * path of the archive to be mounted, or a volumeId for unmounting.
619 * @return {string} Key for |this.requests_|.
622 VolumeManager.prototype.makeRequestKey_ = function(requestType, argument) {
623 return requestType + ':' + argument;
627 * @param {string} fileUrl File url to the archive file.
628 * @param {function(VolumeInfo)} successCallback Success callback.
629 * @param {function(VolumeManagerCommon.VolumeError)} errorCallback Error
632 VolumeManager.prototype.mountArchive = function(
633 fileUrl, successCallback, errorCallback) {
634 chrome.fileBrowserPrivate.addMount(fileUrl, function(sourcePath) {
636 'Mount request: url=' + fileUrl + '; sourcePath=' + sourcePath);
637 var requestKey = this.makeRequestKey_('mount', sourcePath);
638 this.startRequest_(requestKey, successCallback, errorCallback);
644 * @param {!VolumeInfo} volumeInfo Volume to be unmounted.
645 * @param {function()} successCallback Success callback.
646 * @param {function(VolumeManagerCommon.VolumeError)} errorCallback Error
649 VolumeManager.prototype.unmount = function(volumeInfo,
652 chrome.fileBrowserPrivate.removeMount(volumeInfo.volumeId);
653 var requestKey = this.makeRequestKey_('unmount', volumeInfo.volumeId);
654 this.startRequest_(requestKey, successCallback, errorCallback);
658 * Obtains a volume info containing the passed entry.
659 * @param {Entry|Object} entry Entry on the volume to be returned. Can be fake.
660 * @return {VolumeInfo} The VolumeInfo instance or null if not found.
662 VolumeManager.prototype.getVolumeInfo = function(entry) {
663 return this.volumeInfoList.findByEntry(entry);
667 * Obtains volume information of the current profile.
669 * @param {VolumeManagerCommon.VolumeType} volumeType Volume type.
670 * @return {VolumeInfo} Volume info.
672 VolumeManager.prototype.getCurrentProfileVolumeInfo = function(volumeType) {
673 for (var i = 0; i < this.volumeInfoList.length; i++) {
674 var volumeInfo = this.volumeInfoList.item(i);
675 if (volumeInfo.profile.isCurrentProfile &&
676 volumeInfo.volumeType === volumeType)
683 * Obtains location information from an entry.
685 * @param {Entry|Object} entry File or directory entry. It can be a fake entry.
686 * @return {EntryLocation} Location information.
688 VolumeManager.prototype.getLocationInfo = function(entry) {
689 var volumeInfo = this.volumeInfoList.findByEntry(entry);
693 if (util.isFakeEntry(entry)) {
694 return new EntryLocation(
697 true /* the entry points a root directory. */,
698 true /* fake entries are read only. */);
704 if (volumeInfo.volumeType === VolumeManagerCommon.VolumeType.DRIVE) {
705 // For Drive, the roots are /root and /other, instead of /. Root URLs
706 // contain trailing slashes.
707 if (entry.fullPath == '/root' || entry.fullPath.indexOf('/root/') === 0) {
708 rootType = VolumeManagerCommon.RootType.DRIVE;
709 isReadOnly = volumeInfo.isReadOnly;
710 isRootEntry = entry.fullPath === '/root';
711 } else if (entry.fullPath == '/other' ||
712 entry.fullPath.indexOf('/other/') === 0) {
713 rootType = VolumeManagerCommon.RootType.DRIVE_OTHER;
715 isRootEntry = entry.fullPath === '/other';
717 // Accessing Drive files outside of /drive/root and /drive/other is not
718 // allowed, but can happen. Therefore returning null.
722 switch (volumeInfo.volumeType) {
723 case VolumeManagerCommon.VolumeType.DOWNLOADS:
724 rootType = VolumeManagerCommon.RootType.DOWNLOADS;
726 case VolumeManagerCommon.VolumeType.REMOVABLE:
727 rootType = VolumeManagerCommon.RootType.REMOVABLE;
729 case VolumeManagerCommon.VolumeType.ARCHIVE:
730 rootType = VolumeManagerCommon.RootType.ARCHIVE;
732 case VolumeManagerCommon.VolumeType.CLOUD_DEVICE:
733 rootType = VolumeManagerCommon.RootType.CLOUD_DEVICE;
735 case VolumeManagerCommon.VolumeType.MTP:
736 rootType = VolumeManagerCommon.RootType.MTP;
739 // Programming error, throw an exception.
740 throw new Error('Invalid volume type: ' + volumeInfo.volumeType);
742 isReadOnly = volumeInfo.isReadOnly;
743 isRootEntry = util.isSameEntry(entry, volumeInfo.fileSystem.root);
746 return new EntryLocation(volumeInfo, rootType, isRootEntry, isReadOnly);
750 * @param {string} key Key produced by |makeRequestKey_|.
751 * @param {function(VolumeInfo)} successCallback To be called when the request
752 * finishes successfully.
753 * @param {function(VolumeManagerCommon.VolumeError)} errorCallback To be called
754 * when the request fails.
757 VolumeManager.prototype.startRequest_ = function(key,
758 successCallback, errorCallback) {
759 if (key in this.requests_) {
760 var request = this.requests_[key];
761 request.successCallbacks.push(successCallback);
762 request.errorCallbacks.push(errorCallback);
764 this.requests_[key] = {
765 successCallbacks: [successCallback],
766 errorCallbacks: [errorCallback],
768 timeout: setTimeout(this.onTimeout_.bind(this, key),
769 VolumeManager.TIMEOUT)
775 * Called if no response received in |TIMEOUT|.
776 * @param {string} key Key produced by |makeRequestKey_|.
779 VolumeManager.prototype.onTimeout_ = function(key) {
780 this.invokeRequestCallbacks_(this.requests_[key],
781 VolumeManagerCommon.VolumeError.TIMEOUT);
782 delete this.requests_[key];
786 * @param {string} key Key produced by |makeRequestKey_|.
787 * @param {VolumeManagerCommon.VolumeError|'success'} status Status received
789 * @param {VolumeInfo=} opt_volumeInfo Volume info of the mounted volume.
792 VolumeManager.prototype.finishRequest_ = function(key, status, opt_volumeInfo) {
793 var request = this.requests_[key];
797 clearTimeout(request.timeout);
798 this.invokeRequestCallbacks_(request, status, opt_volumeInfo);
799 delete this.requests_[key];
803 * @param {Object} request Structure created in |startRequest_|.
804 * @param {VolumeManagerCommon.VolumeError|string} status If status ===
805 * 'success' success callbacks are called.
806 * @param {VolumeInfo=} opt_volumeInfo Volume info of the mounted volume.
809 VolumeManager.prototype.invokeRequestCallbacks_ = function(
810 request, status, opt_volumeInfo) {
811 var callEach = function(callbacks, self, args) {
812 for (var i = 0; i < callbacks.length; i++) {
813 callbacks[i].apply(self, args);
816 if (status === 'success') {
817 callEach(request.successCallbacks, this, [opt_volumeInfo]);
819 volumeManagerUtil.validateError(status);
820 callEach(request.errorCallbacks, this, [status]);
825 * Location information which shows where the path points in FileManager's
828 * @param {!VolumeInfo} volumeInfo Volume information.
829 * @param {VolumeManagerCommon.RootType} rootType Root type.
830 * @param {boolean} isRootEntry Whether the entry is root entry or not.
831 * @param {boolean} isReadOnly Whether the entry is read only or not.
834 function EntryLocation(volumeInfo, rootType, isRootEntry, isReadOnly) {
836 * Volume information.
837 * @type {!VolumeInfo}
839 this.volumeInfo = volumeInfo;
843 * @type {VolumeManagerCommon.RootType}
845 this.rootType = rootType;
848 * Whether the entry is root entry or not.
851 this.isRootEntry = isRootEntry;
854 * Whether the location obtained from the fake entry correspond to special
858 this.isSpecialSearchRoot =
859 this.rootType === VolumeManagerCommon.RootType.DRIVE_OFFLINE ||
860 this.rootType === VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME ||
861 this.rootType === VolumeManagerCommon.RootType.DRIVE_RECENT;
864 * Whether the location is under Google Drive or a special search root which
865 * represents a special search from Google Drive.
869 this.rootType === VolumeManagerCommon.RootType.DRIVE ||
870 this.rootType === VolumeManagerCommon.RootType.DRIVE_OTHER ||
871 this.rootType === VolumeManagerCommon.RootType.DRIVE_SHARED_WITH_ME ||
872 this.rootType === VolumeManagerCommon.RootType.DRIVE_RECENT ||
873 this.rootType === VolumeManagerCommon.RootType.DRIVE_OFFLINE;
876 * Whether the given path can be a target path of folder shortcut.
879 this.isEligibleForFolderShortcut =
880 !this.isSpecialSearchRoot &&
885 * Whether the entry is read only or not.
888 this.isReadOnly = isReadOnly;