Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / resources / 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 === util.VolumeType.DRIVE)
93       continue;
94     volumeInfoList.push(volumeInfo);
95   }
96   this.volumeInfoList.splice.apply(
97       this.volumeInfoList,
98       [0, this.volumeInfoList.length].concat(volumeInfoList));
99
100   // Subscribe to VolumeInfoList.
101   // In VolumeInfoList, we only use 'splice' event.
102   this.volumeManager_.volumeInfoList.addEventListener(
103       'splice', this.onVolumeInfoListUpdatedBound_);
104
105   // Run pending tasks.
106   var pendingTasks = this.pendingTasks_;
107   this.pendingTasks_ = null;
108   for (var i = 0; i < pendingTasks.length; i++)
109     pendingTasks[i]();
110 };
111
112 /**
113  * Disposes the instance. After the invocation of this method, any other
114  * method should not be called.
115  */
116 VolumeManagerWrapper.prototype.dispose = function() {
117   this.disposed_ = true;
118
119   if (!this.volumeManager_)
120     return;
121   this.volumeManager_.removeEventListener(
122       'drive-connection-changed', this.onEventBound_);
123   this.volumeManager_.removeEventListener(
124       'externally-unmounted', this.onEventBound_);
125   this.volumeManager_.volumeInfoList.removeEventListener(
126       'splice', this.onVolumeInfoListUpdatedBound_);
127 };
128
129 /**
130  * Called on events sent from VolumeManager. This has responsibility to
131  * re-dispatch the event to the listeners.
132  * @param {Event} event Event object sent from VolumeManager.
133  * @private
134  */
135 VolumeManagerWrapper.prototype.onEvent_ = function(event) {
136   if (!this.driveEnabled_) {
137     // If the drive is disabled, ignore all drive related events.
138     if (event.type === 'drive-connection-changed' ||
139         (event.type === 'externally-unmounted' &&
140          event.volumeInfo.volumeType === util.VolumeType.DRIVE))
141       return;
142   }
143
144   this.dispatchEvent(event);
145 };
146
147 /**
148  * Called on events of modifying VolumeInfoList.
149  * @param {Event} event Event object sent from VolumeInfoList.
150  * @private
151  */
152 VolumeManagerWrapper.prototype.onVolumeInfoListUpdated_ = function(event) {
153   if (this.driveEnabled_) {
154     // Apply the splice as is.
155     this.volumeInfoList.splice.apply(
156         this.volumeInfoList,
157         [event.index, event.removed.length].concat(event.added));
158   } else {
159     // Filters drive related volumes.
160     var index = event.index;
161     for (var i = 0; i < event.index; i++) {
162       if (this.volumeManager_.volumeInfoList.item(i).volumeType ===
163           util.VolumeType.DRIVE)
164         index--;
165     }
166
167     var numRemovedVolumes = 0;
168     for (var i = 0; i < event.removed.length; i++) {
169       if (event.removed[i].volumeType !== util.VolumeType.DRIVE)
170         numRemovedVolumes++;
171     }
172
173     var addedVolumes = [];
174     for (var i = 0; i < event.added.length; i++) {
175       var volumeInfo = event.added[i];
176       if (volumeInfo.volumeType !== util.VolumeType.DRIVE)
177         addedVolumes.push(volumeInfo);
178     }
179
180     this.volumeInfoList.splice.apply(
181         this.volumeInfoList,
182         [index, numRemovedVolumes].concat(addedVolumes));
183   }
184 };
185
186 /**
187  * Ensures the VolumeManager is initialized, and then invokes callback.
188  * If the VolumeManager is already initialized, callback will be called
189  * immediately.
190  * @param {function()} callback Called on initialization completion.
191  */
192 VolumeManagerWrapper.prototype.ensureInitialized = function(callback) {
193   if (this.pendingTasks_) {
194     this.pendingTasks_.push(this.ensureInitialized.bind(this, callback));
195     return;
196   }
197
198   callback();
199 };
200
201 /**
202  * @return {util.DriveConnectionType} Current drive connection state.
203  */
204 VolumeManagerWrapper.prototype.getDriveConnectionState = function() {
205   if (!this.driveEnabled_ || !this.volumeManager_) {
206     return {
207       type: util.DriveConnectionType.OFFLINE,
208       reason: util.DriveConnectionReason.NO_SERVICE
209     };
210   }
211
212   return this.volumeManager_.getDriveConnectionState();
213 };
214
215 /**
216  * Obtains a volume info containing the passed entry.
217  * @param {Entry} entry Entry on the volume to be returned.
218  * @return {VolumeInfo} The VolumeInfo instance or null if not found.
219  */
220 VolumeManagerWrapper.prototype.getVolumeInfo = function(entry) {
221   return this.filterDisabledDriveVolume_(
222       this.volumeManager_ && this.volumeManager_.getVolumeInfo(entry));
223 };
224
225 /**
226  * Obtains a volume information of the current profile.
227  * @param {util.VolumeType} volumeType Volume type.
228  * @return {VolumeInfo} Found volume info.
229  */
230 VolumeManagerWrapper.prototype.getCurrentProfileVolumeInfo =
231     function(volumeType) {
232   return this.filterDisabledDriveVolume_(
233       this.volumeManager_ &&
234       this.volumeManager_.getCurrentProfileVolumeInfo(volumeType));
235 };
236
237 /**
238  * Obtains the default display root entry.
239  * @param {function(Entry)} callback Callback passed the default display root.
240  */
241 VolumeManagerWrapper.prototype.getDefaultDisplayRoot =
242     function(callback) {
243   this.ensureInitialized(function() {
244     var defaultVolume = this.getCurrentProfileVolumeInfo(
245         util.VolumeType.DOWNLOADS);
246     defaultVolume.resolveDisplayRoot(callback, function() {
247       // defaultVolume is DOWNLOADS and resolveDisplayRoot should succeed.
248       throw new Error(
249           'Unexpectedly failed to obtain the default display root.');
250     });
251   }.bind(this));
252 };
253
254 /**
255  * Obtains location information from an entry.
256  *
257  * @param {Entry} entry File or directory entry.
258  * @return {EntryLocation} Location information.
259  */
260 VolumeManagerWrapper.prototype.getLocationInfo = function(entry) {
261   var locationInfo =
262       this.volumeManager_ && this.volumeManager_.getLocationInfo(entry);
263   if (!locationInfo)
264     return null;
265   if (!this.filterDisabledDriveVolume_(locationInfo.volumeInfo))
266     return null;
267   return locationInfo;
268 };
269
270 /**
271  * Requests to mount the archive file.
272  * @param {string} fileUrl The path to the archive file to be mounted.
273  * @param {function(VolumeInfo)} successCallback Called with the VolumeInfo
274  *     instance.
275  * @param {function(util.VolumeError)} errorCallback Called when an error
276  *     occurs.
277  */
278 VolumeManagerWrapper.prototype.mountArchive = function(
279     fileUrl, successCallback, errorCallback) {
280   if (this.pendingTasks_) {
281     this.pendingTasks_.push(
282         this.mountArchive.bind(this, fileUrl, successCallback, errorCallback));
283     return;
284   }
285
286   this.volumeManager_.mountArchive(fileUrl, successCallback, errorCallback);
287 };
288
289 /**
290  * Requests unmount the specified volume.
291  * @param {!VolumeInfo} volumeInfo Volume to be unmounted.
292  * @param {function()} successCallback Called on success.
293  * @param {function(util.VolumeError)} errorCallback Called when an error
294  *     occurs.
295  */
296 VolumeManagerWrapper.prototype.unmount = function(
297     volumeInfo, successCallback, errorCallback) {
298   if (this.pendingTasks_) {
299     this.pendingTasks_.push(
300         this.unmount.bind(this, volumeInfo, successCallback, errorCallback));
301     return;
302   }
303
304   this.volumeManager_.unmount(volumeInfo, successCallback, errorCallback);
305 };
306
307 /**
308  * Filters volume info by referring driveEnabled.
309  *
310  * @param {VolumeInfo} volumeInfo Volume info.
311  * @return {VolumeInfo} Null if the drive is disabled and the given volume is
312  *     drive. Otherwise just returns the volume.
313  * @private
314  */
315 VolumeManagerWrapper.prototype.filterDisabledDriveVolume_ =
316     function(volumeInfo) {
317   var isDrive = volumeInfo && volumeInfo.volumeType === util.VolumeType.DRIVE;
318   return this.driveEnabled_ || !isDrive ? volumeInfo : null;
319 };