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