Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / file_manager / background / js / file_operation_handler.js
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.
4
5 /**
6  * An event handler of the background page for file operations.
7  * @param {FileBrowserBackground} background Background page.
8  * @constructor
9  */
10 var FileOperationHandler = function(background) {
11   /**
12    * Background page.
13    * @type {FileBrowserBackground}
14    * @private
15    */
16   this.background_ = background;
17
18   /**
19    * File operation manager.
20    * @type {FileOperationManager}
21    * @private
22    */
23   this.fileOperationManager_ = background.fileOperationManager;
24
25   /**
26    * Progress center.
27    * @type {ProgressCenter}
28    * @private
29    */
30   this.progressCenter_ = background.progressCenter;
31
32   /**
33    * Pending items of delete operation.
34    *
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.
39    *
40    * @type {Object.<string, ProgressCenterItem>}}
41    * @private
42    */
43   this.pendingItems_ = {};
44
45   // Register event.
46   this.fileOperationManager_.addEventListener(
47       'copy-progress',
48       this.onCopyProgress_.bind(this));
49   this.fileOperationManager_.addEventListener(
50       'delete',
51       this.onDeleteProgress_.bind(this));
52
53   // Seal the object.
54   Object.seal(this);
55 };
56
57 /**
58  * Pending time before a delete item is added to the progress center.
59  *
60  * @type {number}
61  * @const
62  * @private
63  */
64 FileOperationHandler.PENDING_TIME_MS_ = 500;
65
66 /**
67  * Generate a progress message from the event.
68  * @param {FileOperationProgressEvent} event Progress event.
69  * @return {string} message.
70  * @private
71  */
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)
78           name += '/';
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);
84         }
85
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);
93         }
94
95       default:
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);
101         }
102     }
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);
110     }
111   } else {
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);
118     }
119   }
120 };
121
122 /**
123  * Generates a delete message from the event.
124  * @param {Event} event Progress event.
125  * @return {string} message.
126  * @private
127  */
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);
137   } else {
138     return '';
139   }
140 };
141
142 /**
143  * Obtains ProgressItemType from OperationType of FileTransferManager.
144  * @param {string} operationType OperationType of FileTransferManager.
145  * @return {ProgressItemType} ProgreeType corresponding to the specified
146  *     operation type.
147  * @private
148  */
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;
154     default:
155       console.error('Unknown operation type.');
156       return ProgressItemType.TRANSFER;
157   }
158 };
159
160 /**
161  * Handles the copy-progress event.
162  * @param {Event} event The copy-progress event.
163  * @private
164  */
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();
170
171   // Update progress center.
172   var progressCenter = this.progressCenter_;
173   var item;
174   switch (event.reason) {
175     case 'BEGIN':
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_,
184           event.taskId);
185       progressCenter.updateItem(item);
186       break;
187
188     case 'PROGRESS':
189       item = progressCenter.getItemById(event.taskId);
190       if (!item) {
191         console.error('Cannot find copying item.');
192         return;
193       }
194       item.message = FileOperationHandler.getMessage_(event);
195       item.progressMax = event.status.totalBytes;
196       item.progressValue = event.status.processedBytes;
197       progressCenter.updateItem(item);
198       break;
199
200     case 'SUCCESS':
201     case 'CANCELED':
202     case 'ERROR':
203       item = progressCenter.getItemById(event.taskId);
204       if (!item) {
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;
210       }
211       if (event.reason === 'SUCCESS') {
212         item.message = '';
213         item.state = ProgressItemState.COMPLETED;
214         item.progressValue = item.progressMax;
215       } else if (event.reason === 'CANCELED') {
216         item.message = '';
217         item.state = ProgressItemState.CANCELED;
218       } else {
219         item.message = FileOperationHandler.getMessage_(event);
220         item.state = ProgressItemState.ERROR;
221       }
222       progressCenter.updateItem(item);
223       break;
224   }
225 };
226
227 /**
228  * Handles the delete event.
229  * @param {Event} event The delete event.
230  * @private
231  */
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();
237
238   // Update progress center.
239   var progressCenter = this.progressCenter_;
240   var item;
241   var pending;
242   switch (event.reason) {
243     case 'BEGIN':
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_,
252           event.taskId);
253       this.pendingItems_[item.id] = item;
254       setTimeout(this.showPendingItem_.bind(this, item),
255                  FileOperationHandler.PENDING_TIME_MS_);
256       break;
257
258     case 'PROGRESS':
259       pending = event.taskId in this.pendingItems_;
260       item = this.pendingItems_[event.taskId] ||
261           progressCenter.getItemById(event.taskId);
262       if (!item) {
263         console.error('Cannot find deleting item.');
264         return;
265       }
266       item.message = FileOperationHandler.getDeleteMessage_(event);
267       item.progressMax = event.totalBytes;
268       item.progressValue = event.processedBytes;
269       if (!pending)
270         progressCenter.updateItem(item);
271       break;
272
273     case 'SUCCESS':
274     case 'CANCELED':
275     case 'ERROR':
276       // Obtain working variable.
277       pending = event.taskId in this.pendingItems_;
278       item = this.pendingItems_[event.taskId] ||
279           progressCenter.getItemById(event.taskId);
280       if (!item) {
281         console.error('Cannot find deleting item.');
282         return;
283       }
284
285       // Update the 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;
292       } else {
293         item.state = ProgressItemState.ERROR;
294       }
295
296       // Apply the change.
297       if (!pending || event.reason === 'ERROR')
298         progressCenter.updateItem(item);
299       if (pending)
300         delete this.pendingItems_[event.taskId];
301       break;
302   }
303 };
304
305 /**
306  * Shows the pending item.
307  *
308  * @param {ProgressCenterItem} item Pending item.
309  * @private
310  */
311 FileOperationHandler.prototype.showPendingItem_ = function(item) {
312   // The item is already gone.
313   if (!this.pendingItems_[item.id])
314     return;
315   delete this.pendingItems_[item.id];
316   this.progressCenter_.updateItem(item);
317 };