a6197bbdeca58668b21cfa75e9581a3541c703cd
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / file_manager / background / js / device_handler.js
1 // Copyright 2014 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 'use strict';
6
7 /**
8  * Handler of device event.
9  * @constructor
10  */
11 function DeviceHandler() {
12   /**
13    * Map of device path and mount status of devices.
14    * @type {Object.<string, DeviceHandler.MountStatus>}
15    * @private
16    */
17   this.mountStatus_ = {};
18
19   chrome.fileBrowserPrivate.onDeviceChanged.addListener(
20       this.onDeviceChanged_.bind(this));
21   chrome.fileBrowserPrivate.onMountCompleted.addListener(
22       this.onMountCompleted_.bind(this));
23
24   Object.seal(this);
25 }
26
27 /**
28  * Notification type.
29  * @param {string} prefix Prefix of notification ID.
30  * @param {string} title String ID of title.
31  * @param {string} message String ID of message.
32  * @constructor
33  */
34 DeviceHandler.Notification = function(prefix, title, message) {
35   /**
36    * Prefix of notification ID.
37    * @type {string}
38    */
39   this.prefix = prefix;
40
41   /**
42    * String ID of title.
43    * @type {string}
44    */
45   this.title = title;
46
47   /**
48    * String ID of message.
49    * @type {string}
50    */
51   this.message = message;
52
53   Object.freeze(this);
54 };
55
56 /**
57  * @type {DeviceHandler.Notification}
58  * @const
59  */
60 DeviceHandler.Notification.DEVICE = new DeviceHandler.Notification(
61     'device',
62     'REMOVABLE_DEVICE_DETECTION_TITLE',
63     'REMOVABLE_DEVICE_SCANNING_MESSAGE');
64
65 /**
66  * @type {DeviceHandler.Notification}
67  * @const
68  */
69 DeviceHandler.Notification.DEVICE_FAIL = new DeviceHandler.Notification(
70     'deviceFail',
71     'REMOVABLE_DEVICE_DETECTION_TITLE',
72     'DEVICE_UNSUPPORTED_DEFAULT_MESSAGE');
73
74 /**
75  * @type {DeviceHandler.Notification}
76  * @const
77  */
78 DeviceHandler.Notification.DEVICE_EXTERNAL_STORAGE_DISABLED =
79     new DeviceHandler.Notification(
80         'deviceFail',
81         'REMOVABLE_DEVICE_DETECTION_TITLE',
82         'EXTERNAL_STORAGE_DISABLED_MESSAGE');
83
84 /**
85  * @type {DeviceHandler.Notification}
86  * @const
87  */
88 DeviceHandler.Notification.FORMAT_START = new DeviceHandler.Notification(
89     'formatStart',
90     'FORMATTING_OF_DEVICE_PENDING_TITLE',
91     'FORMATTING_OF_DEVICE_PENDING_MESSAGE');
92
93 /**
94  * @type {DeviceHandler.Notification}
95  * @const
96  */
97 DeviceHandler.Notification.FORMAT_SUCCESS = new DeviceHandler.Notification(
98     'formatSuccess',
99     'FORMATTING_OF_DEVICE_FINISHED_TITLE',
100     'FORMATTING_FINISHED_SUCCESS_MESSAGE');
101
102 /**
103  * @type {DeviceHandler.Notification}
104  * @const
105  */
106 DeviceHandler.Notification.FORMAT_FAIL = new DeviceHandler.Notification(
107     'formatFail',
108     'FORMATTING_OF_DEVICE_FAILED_TITLE',
109     'FORMATTING_FINISHED_FAILURE_MESSAGE');
110
111 /**
112  * Shows the notification for the device path.
113  * @param {string} devicePath Device path.
114  * @param {string=} opt_message Message overrides the default message.
115  */
116 DeviceHandler.Notification.prototype.show = function(devicePath, opt_message) {
117   chrome.notifications.create(
118       this.makeId_(devicePath),
119       {
120         type: 'basic',
121         title: str(this.title),
122         message: opt_message || str(this.message),
123         iconUrl: chrome.runtime.getURL('/common/images/icon96.png')
124       },
125       function() {});
126 };
127
128 /**
129  * Hides the notification for the device path.
130  * @param {string} devicePath Device path.
131  */
132 DeviceHandler.Notification.prototype.hide = function(devicePath) {
133   chrome.notifications.clear(this.makeId_(devicePath), function() {});
134 };
135
136 /**
137  * Makes a notification ID for the device path.
138  * @param {string} devicePath Device path.
139  * @return {string} Notification ID.
140  * @private
141  */
142 DeviceHandler.Notification.prototype.makeId_ = function(devicePath) {
143   return this.prefix + ':' + devicePath;
144 };
145
146 /**
147  * Handles notifications from C++ sides.
148  * @param {DeviceEvent} event Device event.
149  * @private
150  */
151 DeviceHandler.prototype.onDeviceChanged_ = function(event) {
152   switch (event.type) {
153     case 'added':
154       DeviceHandler.Notification.DEVICE.show(event.devicePath);
155       this.mountStatus_[event.devicePath] = DeviceHandler.MountStatus.NO_RESULT;
156       break;
157     case 'disabled':
158       DeviceHandler.Notification.DEVICE_EXTERNAL_STORAGE_DISABLED.show(
159           event.devicePath);
160       break;
161     case 'scan_canceled':
162       DeviceHandler.Notification.DEVICE.hide(event.devicePath);
163       break;
164     case 'removed':
165       DeviceHandler.Notification.DEVICE.hide(event.devicePath);
166       DeviceHandler.Notification.DEVICE_FAIL.hide(event.devicePath);
167       DeviceHandler.Notification.DEVICE_EXTERNAL_STORAGE_DISABLED.hide(
168           event.devicePath);
169       delete this.mountStatus_[event.devicePath];
170       break;
171     case 'format_start':
172       DeviceHandler.Notification.FORMAT_START.show(event.devicePath);
173       break;
174     case 'format_success':
175       DeviceHandler.Notification.FORMAT_START.hide(event.devicePath);
176       DeviceHandler.Notification.FORMAT_SUCCESS.show(event.devicePath);
177       break;
178     case 'format_fail':
179       DeviceHandler.Notification.FORMAT_START.hide(event.devicePath);
180       DeviceHandler.Notification.FORMAT_FAIL.show(event.devicePath);
181       break;
182   }
183 };
184
185 /**
186  * Mount status for the device.
187  * Each multi-partition devices can obtain multiple mount completed events.
188  * This status shows what results are already obtained for the device.
189  * @enum {string}
190  * @const
191  */
192 DeviceHandler.MountStatus = Object.freeze({
193   // There is no mount results on the device.
194   NO_RESULT: 'noResult',
195   // There is no error on the device.
196   SUCCESS: 'success',
197   // There is only parent errors, that can be overrided by child results.
198   ONLY_PARENT_ERROR: 'onlyParentError',
199   // There is one child error.
200   CHILD_ERROR: 'childError',
201   // There is multiple child results and at least one is failure.
202   MULTIPART_ERROR: 'multipartError'
203 });
204
205 /**
206  * Handles mount completed events to show notifications for removable devices.
207  * @param {MountCompletedEvent} event Mount completed event.
208  * @private
209  */
210 DeviceHandler.prototype.onMountCompleted_ = function(event) {
211   // If this is remounting, which happens when resuming ChromeOS, the device has
212   // already inserted to the computer. So we suppress the notification.
213   var volume = event.volumeMetadata;
214   if (!volume.deviceType || event.isRemounting)
215     return;
216
217   var getFirstStatus = function(event) {
218     if (event.status === 'success')
219       return DeviceHandler.MountStatus.SUCCESS;
220     else if (event.volumeMetadata.isParentDevice)
221       return DeviceHandler.MountStatus.ONLY_PARENT_ERROR;
222     else
223       return DeviceHandler.MountStatus.CHILD_ERROR;
224   };
225
226   // Update the current status.
227   switch (this.mountStatus_[volume.devicePath]) {
228     // If there is no related device, do nothing.
229     case undefined:
230       return;
231     // If the multipart error message has already shown, do nothing because the
232     // message does not changed by the following mount results.
233     case DeviceHandler.MULTIPART_ERROR:
234       return;
235     // If this is the first result, hide the scanning notification.
236     case DeviceHandler.MountStatus.NO_RESULT:
237       DeviceHandler.Notification.DEVICE.hide(volume.devicePath);
238       this.mountStatus_[volume.devicePath] = getFirstStatus(event);
239       break;
240     // If there are only parent errors, and the new result is child's one, hide
241     // the parent error. (parent device contains partition table, which is
242     // unmountable)
243     case DeviceHandler.MountStatus.ONLY_PARENT_ERROR:
244       if (!volume.isParentDevice)
245         DeviceHandler.Notification.DEVICE_FAIL.hide(volume.devicePath);
246       this.mountStatus_[volume.devicePath] = getFirstStatus(event);
247       break;
248     // We have a multi-partition device for which at least one mount
249     // failed.
250     case DeviceHandler.MountStatus.SUCCESS:
251     case DeviceHandler.MountStatus.CHILD_ERROR:
252       if (this.mountStatus_[volume.devicePath] ===
253               DeviceHandler.MountStatus.SUCCESS &&
254           event.status === 'success') {
255         this.mountStatus_[volume.devicePath] =
256             DeviceHandler.MountStatus.SUCCESS;
257       } else {
258         this.mountStatus_[volume.devicePath] =
259             DeviceHandler.MountStatus.MULTIPART_ERROR;
260       }
261       break;
262   }
263
264   // Show the notification for the current errors.
265   // If there is no error, do not show/update the notification.
266   var message;
267   switch (this.mountStatus_[volume.devicePath]) {
268     case DeviceHandler.MountStatus.MULTIPART_ERROR:
269       message = volume.deviceLabel ?
270           strf('MULTIPART_DEVICE_UNSUPPORTED_MESSAGE', volume.deviceLabel) :
271           str('MULTIPART_DEVICE_UNSUPPORTED_DEFAULT_MESSAGE');
272       break;
273     case DeviceHandler.MountStatus.CHILD_ERROR:
274     case DeviceHandler.MountStatus.ONLY_PARENT_ERROR:
275       if (event.status === 'error_unsuported_filesystem') {
276         message = volume.deviceLabel ?
277             strf('DEVICE_UNSUPPORTED_MESSAGE', volume.deviceLabel) :
278             str('DEVICE_UNSUPPORTED_DEFAULT_MESSAGE');
279       } else {
280         message = volume.deviceLabel ?
281             strf('DEVICE_UNKNOWN_MESSAGE', volume.deviceLabel) :
282             str('DEVICE_UNKNOWN_DEFAULT_MESSAGE');
283       }
284       break;
285   }
286   if (message) {
287     DeviceHandler.Notification.DEVICE_FAIL.hide(volume.devicePath);
288     DeviceHandler.Notification.DEVICE_FAIL.show(volume.devicePath, message);
289   }
290 };