Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / file_manager / foreground / js / volume_manager_wrapper.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  * Thin wrapper for VolumeManager. This should be an interface proxy to talk
7  * to VolumeManager. This class also filters Drive related data/events if
8  * driveEnabled is set to false.
9  *
10  * @param {VolumeManagerWrapper.DriveEnabledStatus} driveEnabled DRIVE_ENABLED
11  *     if drive should be available. DRIVE_DISABLED if drive related
12  *     data/events should be hidden.
13  * @param {DOMWindow} opt_backgroundPage Window object of the background
14  *     page. If this is specified, the class skips to get background page.
15  *     TOOD(hirono): Let all clients of the class pass the background page and
16  *     make the argument not optional.
17  * @constructor
18  * @extends {cr.EventTarget}
19  */
20 function VolumeManagerWrapper(driveEnabled, opt_backgroundPage) {
21   cr.EventTarget.call(this);
22
23   this.driveEnabled_ = driveEnabled;
24   this.volumeInfoList = new cr.ui.ArrayDataModel([]);
25
26   this.volumeManager_ = null;
27   this.pendingTasks_ = [];
28   this.onEventBound_ = this.onEvent_.bind(this);
29   this.onVolumeInfoListUpdatedBound_ =
30       this.onVolumeInfoListUpdated_.bind(this);
31
32   this.disposed_ = false;
33
34   // Start initialize the VolumeManager.
35   var queue = new AsyncUtil.Queue();
36
37   if (opt_backgroundPage) {
38     this.backgroundPage_ = opt_backgroundPage;
39   } else {
40     queue.run(function(callNextStep) {
41       chrome.runtime.getBackgroundPage(function(backgroundPage) {
42         this.backgroundPage_ = backgroundPage;
43         callNextStep();
44       }.bind(this));
45     }.bind(this));
46   }
47
48   queue.run(function(callNextStep) {
49     this.backgroundPage_.VolumeManager.getInstance(function(volumeManager) {
50       this.onReady_(volumeManager);
51       callNextStep();
52     }.bind(this));
53   }.bind(this));
54 }
55
56 /**
57  * If the drive is enabled on the wrapper.
58  * @enum {boolean}
59  */
60 VolumeManagerWrapper.DriveEnabledStatus = {
61   DRIVE_ENABLED: true,
62   DRIVE_DISABLED: false
63 };
64
65 /**
66  * Extends cr.EventTarget.
67  */
68 VolumeManagerWrapper.prototype.__proto__ = cr.EventTarget.prototype;
69
70 /**
71  * Called when the VolumeManager gets ready for post initialization.
72  * @param {VolumeManager} volumeManager The initialized VolumeManager instance.
73  * @private
74  */
75 VolumeManagerWrapper.prototype.onReady_ = function(volumeManager) {
76   if (this.disposed_)
77     return;
78
79   this.volumeManager_ = volumeManager;
80
81   // Subscribe to VolumeManager.
82   this.volumeManager_.addEventListener(
83       'drive-connection-changed', this.onEventBound_);
84   this.volumeManager_.addEventListener(
85       'externally-unmounted', this.onEventBound_);
86
87   // Cache volumeInfoList.
88   var volumeInfoList = [];
89   for (var i = 0; i < this.volumeManager_.volumeInfoList.length; i++) {
90     var volumeInfo = this.volumeManager_.volumeInfoList.item(i);
91     // TODO(hidehiko): Filter mounted volumes located on Drive File System.
92     if (!this.driveEnabled_ && volumeInfo.volumeType ===
93         VolumeManagerCommon.VolumeType.DRIVE)
94       continue;
95     volumeInfoList.push(volumeInfo);
96   }
97   this.volumeInfoList.splice.apply(
98       this.volumeInfoList,
99       [0, this.volumeInfoList.length].concat(volumeInfoList));
100
101   // Subscribe to VolumeInfoList.
102   // In VolumeInfoList, we only use 'splice' event.
103   this.volumeManager_.volumeInfoList.addEventListener(
104       'splice', this.onVolumeInfoListUpdatedBound_);
105
106   // Run pending tasks.
107   var pendingTasks = this.pendingTasks_;
108   this.pendingTasks_ = null;
109   for (var i = 0; i < pendingTasks.length; i++)
110     pendingTasks[i]();
111 };
112
113 /**
114  * Disposes the instance. After the invocation of this method, any other
115  * method should not be called.
116  */
117 VolumeManagerWrapper.prototype.dispose = function() {
118   this.disposed_ = true;
119
120   if (!this.volumeManager_)
121     return;
122   this.volumeManager_.removeEventListener(
123       'drive-connection-changed', this.onEventBound_);
124   this.volumeManager_.removeEventListener(
125       'externally-unmounted', this.onEventBound_);
126   this.volumeManager_.volumeInfoList.removeEventListener(
127       'splice', this.onVolumeInfoListUpdatedBound_);
128 };
129
130 /**
131  * Called on events sent from VolumeManager. This has responsibility to
132  * re-dispatch the event to the listeners.
133  * @param {Event} event Event object sent from VolumeManager.
134  * @private
135  */
136 VolumeManagerWrapper.prototype.onEvent_ = function(event) {
137   if (!this.driveEnabled_) {
138     // If the drive is disabled, ignore all drive related events.
139     if (event.type === 'drive-connection-changed' ||
140         (event.type === 'externally-unmounted' &&
141          event.volumeInfo.volumeType ===
142              VolumeManagerCommon.VolumeType.DRIVE)) {
143       return;
144     }
145   }
146
147   this.dispatchEvent(event);
148 };
149
150 /**
151  * Called on events of modifying VolumeInfoList.
152  * @param {Event} event Event object sent from VolumeInfoList.
153  * @private
154  */
155 VolumeManagerWrapper.prototype.onVolumeInfoListUpdated_ = function(event) {
156   if (this.driveEnabled_) {
157     // Apply the splice as is.
158     this.volumeInfoList.splice.apply(
159         this.volumeInfoList,
160         [event.index, event.removed.length].concat(event.added));
161   } else {
162     // Filters drive related volumes.
163     var index = event.index;
164     for (var i = 0; i < event.index; i++) {
165       if (this.volumeManager_.volumeInfoList.item(i).volumeType ===
166           VolumeManagerCommon.VolumeType.DRIVE)
167         index--;
168     }
169
170     var numRemovedVolumes = 0;
171     for (var i = 0; i < event.removed.length; i++) {
172       if (event.removed[i].volumeType !== VolumeManagerCommon.VolumeType.DRIVE)
173         numRemovedVolumes++;
174     }
175
176     var addedVolumes = [];
177     for (var i = 0; i < event.added.length; i++) {
178       var volumeInfo = event.added[i];
179       if (volumeInfo.volumeType !== VolumeManagerCommon.VolumeType.DRIVE)
180         addedVolumes.push(volumeInfo);
181     }
182
183     this.volumeInfoList.splice.apply(
184         this.volumeInfoList,
185         [index, numRemovedVolumes].concat(addedVolumes));
186   }
187 };
188
189 /**
190  * Ensures the VolumeManager is initialized, and then invokes callback.
191  * If the VolumeManager is already initialized, callback will be called
192  * immediately.
193  * @param {function()} callback Called on initialization completion.
194  */
195 VolumeManagerWrapper.prototype.ensureInitialized = function(callback) {
196   if (this.pendingTasks_) {
197     this.pendingTasks_.push(this.ensureInitialized.bind(this, callback));
198     return;
199   }
200
201   callback();
202 };
203
204 /**
205  * @return {VolumeManagerCommon.DriveConnectionType} Current drive connection
206  *     state.
207  */
208 VolumeManagerWrapper.prototype.getDriveConnectionState = function() {
209   if (!this.driveEnabled_ || !this.volumeManager_) {
210     return {
211       type: VolumeManagerCommon.DriveConnectionType.OFFLINE,
212       reason: VolumeManagerCommon.DriveConnectionReason.NO_SERVICE
213     };
214   }
215
216   return this.volumeManager_.getDriveConnectionState();
217 };
218
219 /**
220  * Obtains a volume info containing the passed entry.
221  * @param {Entry} entry Entry on the volume to be returned.
222  * @return {VolumeInfo} The VolumeInfo instance or null if not found.
223  */
224 VolumeManagerWrapper.prototype.getVolumeInfo = function(entry) {
225   return this.filterDisabledDriveVolume_(
226       this.volumeManager_ && this.volumeManager_.getVolumeInfo(entry));
227 };
228
229 /**
230  * Obtains a volume information of the current profile.
231  * @param {VolumeManagerCommon.VolumeType} volumeType Volume type.
232  * @return {VolumeInfo} Found volume info.
233  */
234 VolumeManagerWrapper.prototype.getCurrentProfileVolumeInfo =
235     function(volumeType) {
236   return this.filterDisabledDriveVolume_(
237       this.volumeManager_ &&
238       this.volumeManager_.getCurrentProfileVolumeInfo(volumeType));
239 };
240
241 /**
242  * Obtains the default display root entry.
243  * @param {function(Entry)} callback Callback passed the default display root.
244  */
245 VolumeManagerWrapper.prototype.getDefaultDisplayRoot =
246     function(callback) {
247   this.ensureInitialized(function() {
248     var defaultVolume = this.getCurrentProfileVolumeInfo(
249         VolumeManagerCommon.VolumeType.DOWNLOADS);
250     defaultVolume.resolveDisplayRoot(callback, function() {
251       // defaultVolume is DOWNLOADS and resolveDisplayRoot should succeed.
252       throw new Error(
253           'Unexpectedly failed to obtain the default display root.');
254     });
255   }.bind(this));
256 };
257
258 /**
259  * Obtains location information from an entry.
260  *
261  * @param {Entry} entry File or directory entry.
262  * @return {EntryLocation} Location information.
263  */
264 VolumeManagerWrapper.prototype.getLocationInfo = function(entry) {
265   var locationInfo =
266       this.volumeManager_ && this.volumeManager_.getLocationInfo(entry);
267   if (!locationInfo)
268     return null;
269   if (!this.filterDisabledDriveVolume_(locationInfo.volumeInfo))
270     return null;
271   return locationInfo;
272 };
273
274 /**
275  * Requests to mount the archive file.
276  * @param {string} fileUrl The path to the archive file to be mounted.
277  * @param {function(VolumeInfo)} successCallback Called with the VolumeInfo
278  *     instance.
279  * @param {function(VolumeManagerCommon.VolumeError)} errorCallback Called when
280  *     an error occurs.
281  */
282 VolumeManagerWrapper.prototype.mountArchive = function(
283     fileUrl, successCallback, errorCallback) {
284   if (this.pendingTasks_) {
285     this.pendingTasks_.push(
286         this.mountArchive.bind(this, fileUrl, successCallback, errorCallback));
287     return;
288   }
289
290   this.volumeManager_.mountArchive(fileUrl, successCallback, errorCallback);
291 };
292
293 /**
294  * Requests unmount the specified volume.
295  * @param {!VolumeInfo} volumeInfo Volume to be unmounted.
296  * @param {function()} successCallback Called on success.
297  * @param {function(VolumeManagerCommon.VolumeError)} errorCallback Called when
298  *     an error occurs.
299  */
300 VolumeManagerWrapper.prototype.unmount = function(
301     volumeInfo, successCallback, errorCallback) {
302   if (this.pendingTasks_) {
303     this.pendingTasks_.push(
304         this.unmount.bind(this, volumeInfo, successCallback, errorCallback));
305     return;
306   }
307
308   this.volumeManager_.unmount(volumeInfo, successCallback, errorCallback);
309 };
310
311 /**
312  * Filters volume info by referring driveEnabled.
313  *
314  * @param {VolumeInfo} volumeInfo Volume info.
315  * @return {VolumeInfo} Null if the drive is disabled and the given volume is
316  *     drive. Otherwise just returns the volume.
317  * @private
318  */
319 VolumeManagerWrapper.prototype.filterDisabledDriveVolume_ =
320     function(volumeInfo) {
321   var isDrive = volumeInfo && volumeInfo.volumeType ===
322       VolumeManagerCommon.VolumeType.DRIVE;
323   return this.driveEnabled_ || !isDrive ? volumeInfo : null;
324 };