OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 // Setting the src of an img to an empty string can crash the browser, so we | 5 // Setting the src of an img to an empty string can crash the browser, so we |
6 // use an empty 1x1 gif instead. | 6 // use an empty 1x1 gif instead. |
7 | 7 |
8 /** | 8 /** |
9 * FileManager constructor. | 9 * FileManager constructor. |
10 * | 10 * |
(...skipping 4024 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4035 } | 4035 } |
4036 } | 4036 } |
4037 callback(fileUrls); | 4037 callback(fileUrls); |
4038 }); | 4038 }); |
4039 } else { | 4039 } else { |
4040 callback(fileUrls); | 4040 callback(fileUrls); |
4041 } | 4041 } |
4042 }, | 4042 }, |
4043 | 4043 |
4044 /** | 4044 /** |
4045 * Selects a file. | 4045 * Closes this modal dialog with some files selected. |
4046 * | 4046 * TODO(jamescook): Make unload handler work automatically, crbug.com/104811 |
4047 * @param {string} fileUrl The filename as a URL. | 4047 * @param {Object} selection Contains urls, filterIndex and multiple fields. |
4048 * @param {number} filterIndex The integer file filter index. | |
4049 */ | 4048 */ |
4050 FileManager.prototype.selectFile_ = function(fileUrl, filterIndex) { | 4049 FileManager.prototype.callSelectFilesApiAndClose_ = function(selection) { |
4051 this.resolveSelectResults_( | 4050 if (selection.multiple) { |
4052 [fileUrl], | 4051 chrome.fileBrowserPrivate.selectFiles(selection.urls); |
4053 function(resolvedUrls) { | 4052 } else { |
4054 // Call doSelectFiles_ on a timeout, as it's unsafe to | 4053 chrome.fileBrowserPrivate.selectFile( |
4055 // close a window from a callback. | 4054 selection.urls[0], selection.filterIndex); |
4056 setTimeout( | 4055 } |
4057 this.doSelectFile_.bind(this, resolvedUrls[0], filterIndex), 0); | |
4058 }.bind(this)); | |
4059 }; | |
4060 | |
4061 /** | |
4062 * Selects a file. Closes the window. | |
4063 * TODO(jamescook): Make unload handler work automatically, crbug.com/104811 | |
4064 * | |
4065 * @param {string} fileUrl The filename as a URL. | |
4066 * @param {number} filterIndex The integer file filter index. | |
4067 */ | |
4068 FileManager.prototype.doSelectFile_ = function(fileUrl, filterIndex) { | |
4069 chrome.fileBrowserPrivate.selectFile(fileUrl, filterIndex); | |
4070 this.onUnload_(); | |
4071 window.close(); | |
4072 }; | |
4073 | |
4074 | |
4075 /** | |
4076 * Selects a file. Starts getting gdata files if needed. | |
4077 * | |
4078 * @param {Array.<string>} fileUrls Array of filename URLs. | |
4079 */ | |
4080 FileManager.prototype.selectFiles_ = function(fileUrls) { | |
4081 this.resolveSelectResults_( | |
4082 fileUrls, | |
4083 function(resolvedUrls) { | |
4084 // Call doSelectFiles_ on a timeout, as it's unsafe to | |
4085 // close a window from a callback. | |
4086 setTimeout(this.doSelectFiles_.bind(this, resolvedUrls), 0); | |
4087 }.bind(this)); | |
4088 }; | |
4089 | |
4090 /** | |
4091 * Selects multiple files. Closes the window. | |
4092 * TODO(jamescook): Make unload handler work automatically, crbug.com/104811 | |
4093 * | |
4094 * @param {Array.<string>} fileUrls Array of filename URLs. | |
4095 */ | |
4096 FileManager.prototype.doSelectFiles_ = function(fileUrls) { | |
4097 chrome.fileBrowserPrivate.selectFiles(fileUrls); | |
4098 this.onUnload_(); | 4056 this.onUnload_(); |
4099 window.close(); | 4057 window.close(); |
4100 }; | 4058 }; |
4101 | 4059 |
4102 /** | 4060 /** |
| 4061 * Tries to close this modal dialog with some files selected. |
| 4062 * Performs preprocessing if needed (e.g. for GData). |
| 4063 * @param {Object} selection Contains urls, filterIndex and multiple fields. |
| 4064 */ |
| 4065 FileManager.prototype.selectFilesAndClose_ = function(selection) { |
| 4066 if (!this.isOnGData()) { |
| 4067 setTimeout(this.callSelectFilesApiAndClose_.bind(this, selection), 0); |
| 4068 return; |
| 4069 } |
| 4070 |
| 4071 var shade = this.document_.createElement('div'); |
| 4072 shade.className = 'shade'; |
| 4073 var footer = this.document_.querySelector('.dialog-footer'); |
| 4074 var progress = footer.querySelector('.progress-track'); |
| 4075 progress.style.width = '0%'; |
| 4076 var cancelled = false; |
| 4077 |
| 4078 var progressMap = {}; |
| 4079 var filesStarted = 0; |
| 4080 var filesTotal = selection.urls.length; |
| 4081 for (var index = 0; index < selection.urls.length; index++) { |
| 4082 progressMap[selection.urls[index]] = -1; |
| 4083 } |
| 4084 var lastPercent = 0; |
| 4085 var bytesTotal = 0; |
| 4086 var bytesDone = 0; |
| 4087 |
| 4088 var onFileTransfersUpdated = function(statusList) { |
| 4089 for (var index = 0; index < statusList.length; index++) { |
| 4090 var status = statusList[index]; |
| 4091 var escaped = encodeURI(status.fileUrl); |
| 4092 if (!(escaped in progressMap)) continue; |
| 4093 if (status.total == -1) continue; |
| 4094 |
| 4095 var old = progressMap[escaped]; |
| 4096 if (old == -1) { |
| 4097 // -1 means we don't know file size yet. |
| 4098 bytesTotal += status.total; |
| 4099 filesStarted++; |
| 4100 old = 0; |
| 4101 } |
| 4102 bytesDone += status.processed - old; |
| 4103 progressMap[escaped] = status.processed; |
| 4104 } |
| 4105 |
| 4106 var percent = bytesTotal == 0 ? 0 : bytesDone / bytesTotal; |
| 4107 // For files we don't have information about, assume the progress is zero. |
| 4108 percent = percent * filesStarted / filesTotal * 100; |
| 4109 // Do not decrease the progress. This may happen, if first downloaded |
| 4110 // file is small, and the second one is large. |
| 4111 lastPercent = Math.max(lastPercent, percent); |
| 4112 progress.style.width = lastPercent + '%'; |
| 4113 }.bind(this); |
| 4114 |
| 4115 var setup = function() { |
| 4116 this.document_.querySelector('.dialog-container').appendChild(shade); |
| 4117 setTimeout(function() { shade.setAttribute('fadein', 'fadein') }, 100); |
| 4118 footer.setAttribute('progress', 'progress'); |
| 4119 this.cancelButton_.removeEventListener('click', this.onCancelBound_); |
| 4120 this.cancelButton_.addEventListener('click', onCancel); |
| 4121 chrome.fileBrowserPrivate.onFileTransfersUpdated.addListener( |
| 4122 onFileTransfersUpdated); |
| 4123 }.bind(this); |
| 4124 |
| 4125 var cleanup = function() { |
| 4126 shade.parentNode.removeChild(shade); |
| 4127 footer.removeAttribute('progress'); |
| 4128 this.cancelButton_.removeEventListener('click', onCancel); |
| 4129 this.cancelButton_.addEventListener('click', this.onCancelBound_); |
| 4130 chrome.fileBrowserPrivate.onFileTransfersUpdated.removeListener( |
| 4131 onFileTransfersUpdated); |
| 4132 }.bind(this); |
| 4133 |
| 4134 var onCancel = function() { |
| 4135 cancelled = true; |
| 4136 // According to API cancel may fail, but there is no proper UI to reflect |
| 4137 // this. So, we just silently assume that everything is cancelled. |
| 4138 chrome.fileBrowserPrivate.cancelFileTransfers( |
| 4139 selection.urls, function(response) {}); |
| 4140 cleanup(); |
| 4141 }.bind(this); |
| 4142 |
| 4143 var onResolved = function(resolvedUrls) { |
| 4144 if (cancelled) return; |
| 4145 cleanup(); |
| 4146 selection.urls = resolvedUrls; |
| 4147 // Call next method on a timeout, as it's unsafe to |
| 4148 // close a window from a callback. |
| 4149 setTimeout(this.callSelectFilesApiAndClose_.bind(this, selection), 0); |
| 4150 }.bind(this); |
| 4151 |
| 4152 var onGotProperties = function(properties) { |
| 4153 for (var i = 0; i < properties.length; i++) { |
| 4154 if (properties[i].isPresent) { |
| 4155 // For files already in GCache, we don't get any transfer updates. |
| 4156 filesTotal--; |
| 4157 } |
| 4158 } |
| 4159 this.resolveSelectResults_(selection.urls, onResolved); |
| 4160 }.bind(this); |
| 4161 |
| 4162 setup(); |
| 4163 chrome.fileBrowserPrivate.getGDataFileProperties( |
| 4164 selection.urls, onGotProperties); |
| 4165 }; |
| 4166 |
| 4167 /** |
4103 * Handle a click of the ok button. | 4168 * Handle a click of the ok button. |
4104 * | 4169 * |
4105 * The ok button has different UI labels depending on the type of dialog, but | 4170 * The ok button has different UI labels depending on the type of dialog, but |
4106 * in code it's always referred to as 'ok'. | 4171 * in code it's always referred to as 'ok'. |
4107 * | 4172 * |
4108 * @param {Event} event The click event. | 4173 * @param {Event} event The click event. |
4109 */ | 4174 */ |
4110 FileManager.prototype.onOk_ = function(event) { | 4175 FileManager.prototype.onOk_ = function(event) { |
4111 var currentDirUrl = this.getCurrentDirectoryURL(); | 4176 var currentDirUrl = this.getCurrentDirectoryURL(); |
4112 | 4177 |
4113 if (currentDirUrl.charAt(currentDirUrl.length - 1) != '/') | 4178 if (currentDirUrl.charAt(currentDirUrl.length - 1) != '/') |
4114 currentDirUrl += '/'; | 4179 currentDirUrl += '/'; |
4115 | 4180 |
4116 var self = this; | 4181 var self = this; |
4117 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { | 4182 if (this.dialogType_ == FileManager.DialogType.SELECT_SAVEAS_FILE) { |
4118 // Save-as doesn't require a valid selection from the list, since | 4183 // Save-as doesn't require a valid selection from the list, since |
4119 // we're going to take the filename from the text input. | 4184 // we're going to take the filename from the text input. |
4120 var filename = this.filenameInput_.value; | 4185 var filename = this.filenameInput_.value; |
4121 if (!filename) | 4186 if (!filename) |
4122 throw new Error('Missing filename!'); | 4187 throw new Error('Missing filename!'); |
4123 if (!this.validateFileName_(filename)) | 4188 if (!this.validateFileName_(filename)) |
4124 return; | 4189 return; |
4125 | 4190 |
| 4191 var singleSelection = { |
| 4192 urls: [currentDirUrl + encodeURIComponent(filename)], |
| 4193 multiple: false, |
| 4194 filterIndex: self.getSelectedFilterIndex_(filename) |
| 4195 }; |
| 4196 |
4126 function resolveCallback(victim) { | 4197 function resolveCallback(victim) { |
4127 if (victim instanceof FileError) { | 4198 if (victim instanceof FileError) { |
4128 // File does not exist. Closes the window and does not return. | 4199 // File does not exist. |
4129 self.selectFile_( | 4200 self.selectFilesAndClose_(singleSelection); |
4130 currentDirUrl + encodeURIComponent(filename), | 4201 return; |
4131 self.getSelectedFilterIndex_(filename)); | |
4132 } | 4202 } |
4133 | 4203 |
4134 if (victim.isDirectory) { | 4204 if (victim.isDirectory) { |
4135 // Do not allow to overwrite directory. | 4205 // Do not allow to overwrite directory. |
4136 self.alert.show(strf('DIRECTORY_ALREADY_EXISTS', filename)); | 4206 self.alert.show(strf('DIRECTORY_ALREADY_EXISTS', filename)); |
4137 } else { | 4207 } else { |
4138 self.confirm.show(strf('CONFIRM_OVERWRITE_FILE', filename), | 4208 self.confirm.show(strf('CONFIRM_OVERWRITE_FILE', filename), |
4139 function() { | 4209 function() { |
4140 // User selected Ok from the confirm dialog. | 4210 // User selected Ok from the confirm dialog. |
4141 self.selectFile_( | 4211 self.selectFilesAndClose_(singleSelection); |
4142 currentDirUrl + encodeURIComponent(filename), | |
4143 self.getSelectedFilterIndex_(filename)); | |
4144 }); | 4212 }); |
4145 } | 4213 } |
4146 return; | |
4147 } | 4214 } |
4148 | 4215 |
4149 this.resolvePath(this.getCurrentDirectory() + '/' + filename, | 4216 this.resolvePath(this.getCurrentDirectory() + '/' + filename, |
4150 resolveCallback, resolveCallback); | 4217 resolveCallback, resolveCallback); |
4151 return; | 4218 return; |
4152 } | 4219 } |
4153 | 4220 |
4154 var files = []; | 4221 var files = []; |
4155 var selectedIndexes = this.currentList_.selectionModel.selectedIndexes; | 4222 var selectedIndexes = this.currentList_.selectionModel.selectedIndexes; |
4156 | 4223 |
4157 // All other dialog types require at least one selected list item. | 4224 // All other dialog types require at least one selected list item. |
4158 // The logic to control whether or not the ok button is enabled should | 4225 // The logic to control whether or not the ok button is enabled should |
4159 // prevent us from ever getting here, but we sanity check to be sure. | 4226 // prevent us from ever getting here, but we sanity check to be sure. |
4160 if (!selectedIndexes.length) | 4227 if (!selectedIndexes.length) |
4161 throw new Error('Nothing selected!'); | 4228 throw new Error('Nothing selected!'); |
4162 | 4229 |
4163 var dm = this.directoryModel_.fileList; | 4230 var dm = this.directoryModel_.fileList; |
4164 for (var i = 0; i < selectedIndexes.length; i++) { | 4231 for (var i = 0; i < selectedIndexes.length; i++) { |
4165 var entry = dm.item(selectedIndexes[i]); | 4232 var entry = dm.item(selectedIndexes[i]); |
4166 if (!entry) { | 4233 if (!entry) { |
4167 console.log('Error locating selected file at index: ' + i); | 4234 console.log('Error locating selected file at index: ' + i); |
4168 continue; | 4235 continue; |
4169 } | 4236 } |
4170 | 4237 |
4171 files.push(currentDirUrl + encodeURIComponent(entry.name)); | 4238 files.push(currentDirUrl + encodeURIComponent(entry.name)); |
4172 } | 4239 } |
4173 | 4240 |
4174 // Multi-file selection has no other restrictions. | 4241 // Multi-file selection has no other restrictions. |
4175 if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_MULTI_FILE) { | 4242 if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_MULTI_FILE) { |
4176 // Closes the window and does not return. | 4243 var multipleSelection = { |
4177 this.selectFiles_(files); | 4244 urls: files, |
| 4245 multiple: true |
| 4246 }; |
| 4247 this.selectFilesAndClose_(multipleSelection); |
4178 return; | 4248 return; |
4179 } | 4249 } |
4180 | 4250 |
4181 // Everything else must have exactly one. | 4251 // Everything else must have exactly one. |
4182 if (files.length > 1) | 4252 if (files.length > 1) |
4183 throw new Error('Too many files selected!'); | 4253 throw new Error('Too many files selected!'); |
4184 | 4254 |
4185 var selectedEntry = dm.item(selectedIndexes[0]); | 4255 var selectedEntry = dm.item(selectedIndexes[0]); |
4186 | 4256 |
4187 if (this.dialogType_ == FileManager.DialogType.SELECT_FOLDER) { | 4257 if (this.dialogType_ == FileManager.DialogType.SELECT_FOLDER) { |
4188 if (!selectedEntry.isDirectory) | 4258 if (!selectedEntry.isDirectory) |
4189 throw new Error('Selected entry is not a folder!'); | 4259 throw new Error('Selected entry is not a folder!'); |
4190 } else if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE) { | 4260 } else if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_FILE) { |
4191 if (!selectedEntry.isFile) | 4261 if (!selectedEntry.isFile) |
4192 throw new Error('Selected entry is not a file!'); | 4262 throw new Error('Selected entry is not a file!'); |
4193 } | 4263 } |
4194 | 4264 |
4195 // Closes the window and does not return. | 4265 var singleSelection = { |
4196 this.selectFile_(files[0], this.getSelectedFilterIndex_(files[0])); | 4266 urls: [files[0]], |
| 4267 multiple: false, |
| 4268 filterIndex: this.getSelectedFilterIndex_(files[0]) |
| 4269 }; |
| 4270 this.selectFilesAndClose_(singleSelection); |
4197 }; | 4271 }; |
4198 | 4272 |
4199 /** | 4273 /** |
4200 * Verifies the user entered name for file or folder to be created or | 4274 * Verifies the user entered name for file or folder to be created or |
4201 * renamed to. Name restrictions must correspond to File API restrictions | 4275 * renamed to. Name restrictions must correspond to File API restrictions |
4202 * (see DOMFilePath::isValidPath). Curernt WebKit implementation is | 4276 * (see DOMFilePath::isValidPath). Curernt WebKit implementation is |
4203 * out of date (spec is | 4277 * out of date (spec is |
4204 * http://dev.w3.org/2009/dap/file-system/file-dir-sys.html, 8.3) and going to | 4278 * http://dev.w3.org/2009/dap/file-system/file-dir-sys.html, 8.3) and going to |
4205 * be fixed. Shows message box if the name is invalid. | 4279 * be fixed. Shows message box if the name is invalid. |
4206 * | 4280 * |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4279 }); | 4353 }); |
4280 }, onError); | 4354 }, onError); |
4281 | 4355 |
4282 function onError(err) { | 4356 function onError(err) { |
4283 console.log('Error while checking free space: ' + err); | 4357 console.log('Error while checking free space: ' + err); |
4284 setTimeout(doCheck, 1000 * 60); | 4358 setTimeout(doCheck, 1000 * 60); |
4285 } | 4359 } |
4286 } | 4360 } |
4287 } | 4361 } |
4288 })(); | 4362 })(); |
OLD | NEW |