OLD | NEW |
(Empty) | |
| 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. |
| 4 |
| 5 /** |
| 6 * FileManager constructor. |
| 7 * |
| 8 * FileManager objects encapsulate the functionality of the file selector |
| 9 * dialogs, as well as the full screen file manager application (though the |
| 10 * latter is not yet implemented). |
| 11 * |
| 12 * @constructor |
| 13 * @param {HTMLElement} dialogDom The DOM node containing the prototypical |
| 14 * dialog UI. |
| 15 */ |
| 16 function VolumeManager() { |
| 17 // The list of archives requested to mount. We will show contents once |
| 18 // archive is mounted, but only for mounts from within this filebrowser tab. |
| 19 this.requests_ = {}; |
| 20 this.mountedVolumes_ = {}; |
| 21 |
| 22 this.initMountPoints_(); |
| 23 chrome.fileBrowserPrivate.onMountCompleted.addListener( |
| 24 this.onMountCompleted_.bind(this)); |
| 25 this.gDataStatus_ = VolumeManager.GDataStatus.UNMOUNTED; |
| 26 } |
| 27 |
| 28 VolumeManager.prototype.__proto__ = cr.EventTarget.prototype; |
| 29 |
| 30 VolumeManager.Error = { |
| 31 /* Internal errors */ |
| 32 NOT_MOUNTED: 'not_mounted', |
| 33 TIMEOUT: 'timeout', |
| 34 |
| 35 /* System events */ |
| 36 UNKNOWN: 'error_unknown', |
| 37 INTERNAL: 'error_internal', |
| 38 UNKNOWN_FILESYSTEM: 'error_unknown_filesystem', |
| 39 UNSUPPORTED_FILESYSTEM: 'error_unsuported_filesystem', |
| 40 INVALID_ARCHIVE: 'error_invalid_archive', |
| 41 LIBCROS_MISSING: 'error_libcros_missing', |
| 42 AUTHENTICATION: 'error_authentication', |
| 43 PATH_UNMOUNTED: 'error_path_unmounted' |
| 44 }; |
| 45 |
| 46 VolumeManager.GDataStatus = { |
| 47 UNMOUNTED: 'unmounted', |
| 48 MOUNTING: 'mounting', |
| 49 ERROR: 'error', |
| 50 MOUNTED: 'mounted' |
| 51 }; |
| 52 |
| 53 VolumeManager.getInstance = function() { |
| 54 return VolumeManager.instance_ = VolumeManager.instance_ || new VolumeManager(
); |
| 55 }; |
| 56 |
| 57 VolumeManager.prototype.setGDataStatus_ = function(newStatus) { |
| 58 if (this.gDataStatus_ != newStatus) { |
| 59 this.gDataStatus_ = newStatus; |
| 60 cr.dispatchSimpleEvent(this, 'gdata-status-changed'); |
| 61 } |
| 62 }; |
| 63 |
| 64 VolumeManager.prototype.getGDataStatus = function() { |
| 65 return this.gDataStatus_; |
| 66 }; |
| 67 |
| 68 VolumeManager.prototype.isMounted = function(mountPath) { |
| 69 return mountPath in this.mountedVolumes_; |
| 70 }; |
| 71 |
| 72 VolumeManager.prototype.initMountPoints_ = function() { |
| 73 var mountedVolumes = {}; |
| 74 var self = this; |
| 75 var index = 0; |
| 76 var gDataMounted = false; |
| 77 function step(mountPoints) { |
| 78 if (index < mountPoints.length) { |
| 79 var info = mountPoints[index]; |
| 80 if (info.mountType == 'gdata') |
| 81 gDataMounted = true; |
| 82 self.makeVolumeInfo_('/' + info.mountPath, info.mountCondition, |
| 83 function(volume) { |
| 84 mountedVolumes[volume.mountPath] = volume; |
| 85 index++; |
| 86 step(mountPoints); |
| 87 }); |
| 88 } else { |
| 89 self.mountedVolumes_ = mountedVolumes; |
| 90 // Do not change ERROR status if gData is not mounted. |
| 91 if (self.getGDataStatus() != VolumeManager.GDataStatus.ERROR && |
| 92 !gDataMounted) { |
| 93 self.setGDataStatus_(gDataMounted ? |
| 94 VolumeManager.GDataStatus.MOUNTED : |
| 95 VolumeManager.GDataStatus.UNMOUNTED); |
| 96 } |
| 97 } |
| 98 } |
| 99 |
| 100 chrome.fileBrowserPrivate.getMountPoints(step); |
| 101 }; |
| 102 |
| 103 /** |
| 104 * Event handler called when some volume was mounted or unmouted. |
| 105 */ |
| 106 VolumeManager.prototype.onMountCompleted_ = function(event) { |
| 107 if (event.eventType == 'mount') { |
| 108 if (event.mountPath) { |
| 109 var requestKey = this.makeRequestKey_('mount', event.mountType, event.sour
cePath); |
| 110 this.makeVolumeInfo_(event.mountPath, event.status, function(volume) { |
| 111 this.mountedVolumes_[volume.mountPath] = volume; |
| 112 this.finishRequest_(requestKey, event.status, event.mountPath); |
| 113 this.notifyChange_(); |
| 114 }.bind(this)); |
| 115 } else { |
| 116 console.log('No mount path'); |
| 117 this.finishRequest_(requestKey, event.status); |
| 118 } |
| 119 } else if (event.eventType == 'unmount') { |
| 120 var mountPath = '/' + event.mountPath; |
| 121 var status = event.status; |
| 122 if (status == VolumeManager.Error.PATH_UNMOUNTED) { |
| 123 console.log('Volume already unmounted: ', mountPath); |
| 124 status = 'success'; |
| 125 } |
| 126 this.finishRequest_(this.makeRequestKey_('mount', '', mountPath), status); |
| 127 |
| 128 if (event.status == 'success') { |
| 129 delete this.mountedVolumes_[mountPath]; |
| 130 this.notifyChange_(); |
| 131 } |
| 132 } |
| 133 |
| 134 if (event.mountType == 'gdata') { |
| 135 if (event.status == 'success') { |
| 136 if (event.eventType == 'mount') |
| 137 this.setGDataStatus_(VolumeManager.GDataStatus.MOUNTED); |
| 138 else if (event.eventType == 'unmount' && event.status == 'success') |
| 139 this.setGDataStatus_(VolumeManager.GDataStatus.UMOUNTED); |
| 140 } |
| 141 } |
| 142 }; |
| 143 |
| 144 VolumeManager.prototype.makeVolumeInfo_ = function( |
| 145 mountPath, status, callback) { |
| 146 chrome.fileBrowserPrivate.getVolumeMetadata(this.makeUrl_(mountPath), |
| 147 function(metadata) { |
| 148 callback({ |
| 149 mountPath: mountPath, |
| 150 status: status, |
| 151 readonly: !!metadata && metadata.isReadOnly |
| 152 }); |
| 153 }.bind(this)); |
| 154 }; |
| 155 |
| 156 /** |
| 157 * @param {string} requestType 'mount' | 'unmount' |
| 158 * @param {string} mountType 'device' | 'file' | 'network' | 'gdata'. |
| 159 * @param {string?} opt_sourcePath |
| 160 */ |
| 161 VolumeManager.prototype.makeRequestKey_ = function(requestType, mountType, opt_m
ountPath) { |
| 162 return requestType + ':' + mountType + ':' + (opt_mountPath || ''); |
| 163 }; |
| 164 |
| 165 VolumeManager.prototype.mountGData = function(successCallback, errorCallback) { |
| 166 if (this.getGDataStatus() == VolumeManager.GDataStatus.ERROR) { |
| 167 this.setGDataStatus_(VolumeManager.GDataStatus.UNMOUNTED); |
| 168 } |
| 169 var self = this; |
| 170 var timeout = setTimeout(function() { |
| 171 if (self.getGDataStatus() == VolumeManager.GDataStatus.UNMOUNTED) |
| 172 self.setGDataStatus_(VolumeManager.GDataStatus.MOUNTING); |
| 173 timeout = null; |
| 174 }, 500); |
| 175 this.mount_('', 'gdata', function(mountPath) { |
| 176 if (timeout !== null) |
| 177 clearTimeout(timeout); |
| 178 successCallback(mountPath); |
| 179 }, function(error) { |
| 180 if (self.getGDataStatus() != VolumeManager.GDataStatus.MOUNTED) |
| 181 self.setGDataStatus_(VolumeManager.GDataStatus.ERROR); |
| 182 if (timeout != null) |
| 183 clearTimeout(timeout); |
| 184 errorCallback(error); |
| 185 }); |
| 186 }; |
| 187 |
| 188 VolumeManager.prototype.getMountCondition = function(mountPath) { |
| 189 return this.getVolumeInfo_(mountPath).status; |
| 190 }; |
| 191 |
| 192 VolumeManager.prototype.isReadOnly = function(mountPath) { |
| 193 return this.getVolumeInfo_(mountPath).readonly; |
| 194 }; |
| 195 |
| 196 VolumeManager.prototype.getVolumeInfo_ = function(mountPath) { |
| 197 return this.mountedVolumes_[mountPath] || {}; |
| 198 }; |
| 199 |
| 200 /** |
| 201 * Unmounts device. |
| 202 * @param {Entry} entry The entry to unmount. |
| 203 */ |
| 204 VolumeManager.prototype.unmount = function(mountPath, successCallback, errorCall
back) { |
| 205 var volumeInfo = this.mountedVolumes_[mountPath]; |
| 206 if (!volumeInfo) { |
| 207 errorCallback(VolumeManager.Error.NOT_NOUNTED); |
| 208 return; |
| 209 } |
| 210 |
| 211 chrome.fileBrowserPrivate.removeMount(this.makeUrl_(mountPath)); |
| 212 this.startRequest_(this.makeRequestKey_('unmount', '', mountPath)); |
| 213 }; |
| 214 |
| 215 VolumeManager.prototype.mountArchive = function(fullPath, successCallback, |
| 216 errorCallback) { |
| 217 var requestKey = this.makeRequestKey_('mount', 'file'); |
| 218 if (requestKey in this.requests_) { |
| 219 // Parallel requests are not supported. API doesn't provide a way |
| 220 // to find request for specific response. |
| 221 errorCallback('inprogress'); |
| 222 return; |
| 223 } |
| 224 this.mount_(this.makeUrl_(fullPath), |
| 225 'file', successCallback, errorCallback); |
| 226 }; |
| 227 |
| 228 VolumeManager.prototype.makeUrl_ = function(path) { |
| 229 return 'filesystem:' + chrome.extension.getURL('external' + path); |
| 230 }; |
| 231 |
| 232 VolumeManager.prototype.mount_ = function(url, mountType, |
| 233 successCallback, errorCallback) { |
| 234 console.log('mount ', url, mountType); |
| 235 chrome.fileBrowserPrivate.addMount(url, mountType, {}, |
| 236 function(sourcePath) { |
| 237 var requestKey = this.makeRequestKey_('mount', mountType, sourcePath); |
| 238 this.startRequest_(requestKey, successCallback, errorCallback); |
| 239 }.bind(this)); |
| 240 }; |
| 241 |
| 242 VolumeManager.prototype.startRequest_ = function(key, |
| 243 successCallback, errorCallback) { |
| 244 console.log('Start request: ', key); |
| 245 if (key in this.requests_) { |
| 246 var request = this.requests_[key]; |
| 247 request.successCallbacks.push(successCallback); |
| 248 request.errorCallbacks.push(errorCallback); |
| 249 } else { |
| 250 this.requests_[key] = { |
| 251 successCallbacks: [successCallback], |
| 252 errorCallbacks: [errorCallback], |
| 253 |
| 254 timeout: setTimeout(this.onTimeout_.bind(this, key), 10000) |
| 255 }; |
| 256 } |
| 257 }; |
| 258 |
| 259 VolumeManager.prototype.onTimeout_ = function(key) { |
| 260 this.invokeRequestCallbacks_(this.requests_[key], |
| 261 VolumeManager.Error.TIMEOUT); |
| 262 delete this.requests_[key]; |
| 263 }; |
| 264 |
| 265 VolumeManager.prototype.finishRequest_ = function(key, status, opt_mountPath) { |
| 266 var request = this.requests_[key]; |
| 267 if (!request) |
| 268 return; |
| 269 |
| 270 clearTimeout(request.timeout); |
| 271 this.invokeRequestCallbacks_(request, status, opt_mountPath); |
| 272 delete this.requests_[key]; |
| 273 } |
| 274 |
| 275 VolumeManager.prototype.invokeRequestCallbacks_= function(request, status, |
| 276 opt_mountPath) { |
| 277 function callEach(callbacks, self, args) { |
| 278 for (var i = 0; i < callbacks.length; i++) { |
| 279 callbacks[i].call(self, args); |
| 280 } |
| 281 } |
| 282 if (status == 'success') |
| 283 callEach(request.successCallbacks, this, [opt_mountPath]); |
| 284 else |
| 285 callEach(request.errorCallbacks, this, [status]); |
| 286 }; |
| 287 |
| 288 VolumeManager.prototype.notifyChange_ = function() { |
| 289 cr.dispatchSimpleEvent(this, 'change'); |
| 290 }; |
| 291 |
OLD | NEW |