1 // Copyright 2013 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.
6 * An event handler of the background page for file operations.
7 * @param {FileBrowserBackground} background Background page.
10 var FileOperationHandler = function(background) {
13 * @type {FileBrowserBackground}
16 this.background_ = background;
19 * File operation manager.
20 * @type {FileOperationManager}
23 this.fileOperationManager_ = background.fileOperationManager;
27 * @type {ProgressCenter}
30 this.progressCenter_ = background.progressCenter;
33 * Pending items of delete operation.
35 * Delete operations are usually complete quickly.
36 * So we would not like to show the progress bar at first.
37 * If the operation takes more than FileOperationHandler.PENDING_TIME_MS_,
38 * we adds the item to the progress center.
40 * @type {Object.<string, ProgressCenterItem>}}
43 this.pendingItems_ = {};
46 this.fileOperationManager_.addEventListener(
48 this.onCopyProgress_.bind(this));
49 this.fileOperationManager_.addEventListener(
51 this.onDeleteProgress_.bind(this));
58 * Pending time before a delete item is added to the progress center.
64 FileOperationHandler.PENDING_TIME_MS_ = 500;
67 * Generate a progress message from the event.
68 * @param {FileOperationProgressEvent} event Progress event.
69 * @return {string} message.
72 FileOperationHandler.getMessage_ = function(event) {
73 if (event.reason === 'ERROR') {
74 switch (event.error.code) {
75 case util.FileOperationErrorType.TARGET_EXISTS:
76 var name = event.error.data.name;
77 if (event.error.data.isDirectory)
79 switch (event.status.operationType) {
80 case 'COPY': return strf('COPY_TARGET_EXISTS_ERROR', name);
81 case 'MOVE': return strf('MOVE_TARGET_EXISTS_ERROR', name);
82 case 'ZIP': return strf('ZIP_TARGET_EXISTS_ERROR', name);
83 default: return strf('TRANSFER_TARGET_EXISTS_ERROR', name);
86 case util.FileOperationErrorType.FILESYSTEM_ERROR:
87 var detail = util.getFileErrorString(event.error.data.name);
88 switch (event.status.operationType) {
89 case 'COPY': return strf('COPY_FILESYSTEM_ERROR', detail);
90 case 'MOVE': return strf('MOVE_FILESYSTEM_ERROR', detail);
91 case 'ZIP': return strf('ZIP_FILESYSTEM_ERROR', detail);
92 default: return strf('TRANSFER_FILESYSTEM_ERROR', detail);
96 switch (event.status.operationType) {
97 case 'COPY': return strf('COPY_UNEXPECTED_ERROR', event.error.code);
98 case 'MOVE': return strf('MOVE_UNEXPECTED_ERROR', event.error.code);
99 case 'ZIP': return strf('ZIP_UNEXPECTED_ERROR', event.error.code);
100 default: return strf('TRANSFER_UNEXPECTED_ERROR', event.error.code);
103 } else if (event.status.numRemainingItems === 1) {
104 var name = event.status.processingEntryName;
105 switch (event.status.operationType) {
106 case 'COPY': return strf('COPY_FILE_NAME', name);
107 case 'MOVE': return strf('MOVE_FILE_NAME', name);
108 case 'ZIP': return strf('ZIP_FILE_NAME', name);
109 default: return strf('TRANSFER_FILE_NAME', name);
112 var remainNumber = event.status.numRemainingItems;
113 switch (event.status.operationType) {
114 case 'COPY': return strf('COPY_ITEMS_REMAINING', remainNumber);
115 case 'MOVE': return strf('MOVE_ITEMS_REMAINING', remainNumber);
116 case 'ZIP': return strf('ZIP_ITEMS_REMAINING', remainNumber);
117 default: return strf('TRANSFER_ITEMS_REMAINING', remainNumber);
123 * Generates a delete message from the event.
124 * @param {Event} event Progress event.
125 * @return {string} message.
128 FileOperationHandler.getDeleteMessage_ = function(event) {
129 event = /** @type {FileOperationProgressEvent} */ (event);
130 if (event.reason === 'ERROR') {
131 return str('DELETE_ERROR');
132 } else if (event.entries.length == 1) {
133 var fileName = event.entries[0].name;
134 return strf('DELETE_FILE_NAME', fileName);
135 } else if (event.entries.length > 1) {
136 return strf('DELETE_ITEMS_REMAINING', event.entries.length);
143 * Obtains ProgressItemType from OperationType of FileTransferManager.
144 * @param {string} operationType OperationType of FileTransferManager.
145 * @return {ProgressItemType} ProgreeType corresponding to the specified
149 FileOperationHandler.getType_ = function(operationType) {
150 switch (operationType) {
151 case 'COPY': return ProgressItemType.COPY;
152 case 'MOVE': return ProgressItemType.MOVE;
153 case 'ZIP': return ProgressItemType.ZIP;
155 console.error('Unknown operation type.');
156 return ProgressItemType.TRANSFER;
161 * Handles the copy-progress event.
162 * @param {Event} event The copy-progress event.
165 FileOperationHandler.prototype.onCopyProgress_ = function(event) {
166 event = /** @type {FileOperationProgressEvent} */ (event);
167 // If the copy is finished, may be we can close the background page.
168 if (event.reason !== 'BEGIN' && event.reason !== 'PROGRESS')
169 this.background_.tryClose();
171 // Update progress center.
172 var progressCenter = this.progressCenter_;
174 switch (event.reason) {
176 item = new ProgressCenterItem();
177 item.id = event.taskId;
178 item.type = FileOperationHandler.getType_(event.status.operationType);
179 item.message = FileOperationHandler.getMessage_(event);
180 item.progressMax = event.status.totalBytes;
181 item.progressValue = event.status.processedBytes;
182 item.cancelCallback = this.fileOperationManager_.requestTaskCancel.bind(
183 this.fileOperationManager_,
185 progressCenter.updateItem(item);
189 item = progressCenter.getItemById(event.taskId);
191 console.error('Cannot find copying item.');
194 item.message = FileOperationHandler.getMessage_(event);
195 item.progressMax = event.status.totalBytes;
196 item.progressValue = event.status.processedBytes;
197 progressCenter.updateItem(item);
203 item = progressCenter.getItemById(event.taskId);
205 // ERROR events can be dispatched before BEGIN events.
206 item = new ProgressCenterItem();
207 item.type = FileOperationHandler.getType_(event.status.operationType);
208 item.id = event.taskId;
209 item.progressMax = 1;
211 if (event.reason === 'SUCCESS') {
213 item.state = ProgressItemState.COMPLETED;
214 item.progressValue = item.progressMax;
215 } else if (event.reason === 'CANCELED') {
217 item.state = ProgressItemState.CANCELED;
219 item.message = FileOperationHandler.getMessage_(event);
220 item.state = ProgressItemState.ERROR;
222 progressCenter.updateItem(item);
228 * Handles the delete event.
229 * @param {Event} event The delete event.
232 FileOperationHandler.prototype.onDeleteProgress_ = function(event) {
233 event = /** @type {FileOperationProgressEvent} */ (event);
234 // If the copy is finished, may be we can close the background page.
235 if (event.reason !== 'BEGIN' && event.reason !== 'PROGRESS')
236 this.background_.tryClose();
238 // Update progress center.
239 var progressCenter = this.progressCenter_;
242 switch (event.reason) {
244 item = new ProgressCenterItem();
245 item.id = event.taskId;
246 item.type = ProgressItemType.DELETE;
247 item.message = FileOperationHandler.getDeleteMessage_(event);
248 item.progressMax = event.totalBytes;
249 item.progressValue = event.processedBytes;
250 item.cancelCallback = this.fileOperationManager_.requestTaskCancel.bind(
251 this.fileOperationManager_,
253 this.pendingItems_[item.id] = item;
254 setTimeout(this.showPendingItem_.bind(this, item),
255 FileOperationHandler.PENDING_TIME_MS_);
259 pending = event.taskId in this.pendingItems_;
260 item = this.pendingItems_[event.taskId] ||
261 progressCenter.getItemById(event.taskId);
263 console.error('Cannot find deleting item.');
266 item.message = FileOperationHandler.getDeleteMessage_(event);
267 item.progressMax = event.totalBytes;
268 item.progressValue = event.processedBytes;
270 progressCenter.updateItem(item);
276 // Obtain working variable.
277 pending = event.taskId in this.pendingItems_;
278 item = this.pendingItems_[event.taskId] ||
279 progressCenter.getItemById(event.taskId);
281 console.error('Cannot find deleting item.');
286 item.message = FileOperationHandler.getDeleteMessage_(event);
287 if (event.reason === 'SUCCESS') {
288 item.state = ProgressItemState.COMPLETED;
289 item.progressValue = item.progressMax;
290 } else if (event.reason === 'CANCELED') {
291 item.state = ProgressItemState.CANCELED;
293 item.state = ProgressItemState.ERROR;
297 if (!pending || event.reason === 'ERROR')
298 progressCenter.updateItem(item);
300 delete this.pendingItems_[event.taskId];
306 * Shows the pending item.
308 * @param {ProgressCenterItem} item Pending item.
311 FileOperationHandler.prototype.showPendingItem_ = function(item) {
312 // The item is already gone.
313 if (!this.pendingItems_[item.id])
315 delete this.pendingItems_[item.id];
316 this.progressCenter_.updateItem(item);