Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / ui / file_manager / file_manager / foreground / js / file_watcher.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 'use strict';
6
7 /**
8  * Watches for changes in the tracked directory, including local metadata
9  * changes.
10  *
11  * @param {MetadataCache} metadataCache Instance of MetadataCache.
12  * @extends {cr.EventTarget}
13  * @constructor
14  */
15 function FileWatcher(metadataCache) {
16   this.queue_ = new AsyncUtil.Queue();
17   this.metadataCache_ = metadataCache;
18   this.watchedDirectoryEntry_ = null;
19
20   this.onDirectoryChangedBound_ = this.onDirectoryChanged_.bind(this);
21   chrome.fileBrowserPrivate.onDirectoryChanged.addListener(
22       this.onDirectoryChangedBound_);
23
24   this.filesystemMetadataObserverId_ = null;
25   this.thumbnailMetadataObserverId_ = null;
26   this.driveMetadataObserverId_ = null;
27 }
28
29 /**
30  * FileWatcher extends cr.EventTarget.
31  */
32 FileWatcher.prototype.__proto__ = cr.EventTarget.prototype;
33
34 /**
35  * Stops watching (must be called before page unload).
36  */
37 FileWatcher.prototype.dispose = function() {
38   chrome.fileBrowserPrivate.onDirectoryChanged.removeListener(
39       this.onDirectoryChangedBound_);
40   if (this.watchedDirectoryEntry_)
41     this.resetWatchedEntry_(function() {}, function() {});
42 };
43
44 /**
45  * Called when a file in the watched directory is changed.
46  * @param {Event} event Change event.
47  * @private
48  */
49 FileWatcher.prototype.onDirectoryChanged_ = function(event) {
50   if (this.watchedDirectoryEntry_ &&
51       event.entry.toURL() === this.watchedDirectoryEntry_.toURL()) {
52     var e = new Event('watcher-directory-changed');
53     e.changedFiles = event.changedFiles;
54     this.dispatchEvent(e);
55   }
56 };
57
58 /**
59  * Called when general metadata in the watched directory has been changed.
60  *
61  * @param {Array.<Entry>} entries Array of entries.
62  * @param {Object.<string, Object>} properties Map from entry URLs to metadata
63  *     properties.
64  * @private
65  */
66 FileWatcher.prototype.onFilesystemMetadataChanged_ = function(
67     entries, properties) {
68   this.dispatchMetadataEvent_('filesystem', entries, properties);
69 };
70
71 /**
72  * Called when thumbnail metadata in the watched directory has been changed.
73  *
74  * @param {Array.<Entry>} entries Array of entries.
75  * @param {Object.<string, Object>} properties Map from entry URLs to metadata
76  *     properties.
77  * @private
78  */
79 FileWatcher.prototype.onThumbnailMetadataChanged_ = function(
80     entries, properties) {
81   this.dispatchMetadataEvent_('thumbnail', entries, properties);
82 };
83
84 /**
85  * Called when drive metadata in the watched directory has been changed.
86  *
87  * @param {Array.<Entry>} entries Array of entries.
88  * @param {Object.<string, Object>} properties Map from entry URLs to metadata
89  *     properties.
90  * @private
91  */
92 FileWatcher.prototype.onDriveMetadataChanged_ = function(
93     entries, properties) {
94   this.dispatchMetadataEvent_('drive', entries, properties);
95 };
96
97 /**
98  * Dispatches an event about detected change in metadata within the tracked
99  * directory.
100  *
101  * @param {string} type Type of the metadata change.
102  * @param {Array.<Entry>} entries Array of entries.
103  * @param {Object.<string, Object>} properties Map from entry URLs to metadata
104  *     properties.
105  * @private
106  */
107 FileWatcher.prototype.dispatchMetadataEvent_ = function(
108     type, entries, properties) {
109   var e = new Event('watcher-metadata-changed');
110   e.metadataType = type;
111   e.entries = entries;
112   e.properties = properties;
113   this.dispatchEvent(e);
114 };
115
116 /**
117  * Changes the watched directory. In case of a fake entry, the watch is
118  * just released, since there is no reason to track a fake directory.
119  *
120  * @param {!DirectoryEntry|!Object} entry Directory entry to be tracked, or the
121  *     fake entry.
122  * @param {function()} callback Completion callback.
123  */
124 FileWatcher.prototype.changeWatchedDirectory = function(entry, callback) {
125   if (!util.isFakeEntry(entry)) {
126     this.changeWatchedEntry_(
127         entry,
128         callback,
129         function() {
130           console.error(
131              'Unable to change the watched directory to: ' + entry.toURL());
132           callback();
133         });
134   } else {
135     this.resetWatchedEntry_(
136         callback,
137         function() {
138           console.error('Unable to reset the watched directory.');
139           callback();
140         });
141   }
142 };
143
144 /**
145  * Resets the watched entry to the passed directory.
146  *
147  * @param {function()} onSuccess Success callback.
148  * @param {function()} onError Error callback.
149  * @private
150  */
151 FileWatcher.prototype.resetWatchedEntry_ = function(onSuccess, onError) {
152   // Run the tasks in the queue to avoid races.
153   this.queue_.run(function(callback) {
154     // Release the watched directory.
155     if (this.watchedDirectoryEntry_) {
156       chrome.fileBrowserPrivate.removeFileWatch(
157           this.watchedDirectoryEntry_.toURL(),
158           function(result) {
159             this.watchedDirectoryEntry_ = null;
160             if (result)
161               onSuccess();
162             else
163               onError();
164             callback();
165           }.bind(this));
166       this.metadataCache_.removeObserver(this.filesystemMetadataObserverId_);
167       this.metadataCache_.removeObserver(this.thumbnailMetadataObserverId_);
168       this.metadataCache_.removeObserver(this.driveMetadataObserverId_);
169     } else {
170       onSuccess();
171       callback();
172     }
173   }.bind(this));
174 };
175
176 /**
177  * Sets the watched entry to the passed directory.
178  *
179  * @param {!DirectoryEntry} entry Directory to be watched.
180  * @param {function()} onSuccess Success callback.
181  * @param {function()} onError Error callback.
182  * @private
183  */
184 FileWatcher.prototype.changeWatchedEntry_ = function(
185     entry, onSuccess, onError) {
186   var setEntryClosure = function() {
187     // Run the tasks in the queue to avoid races.
188     this.queue_.run(function(callback) {
189       chrome.fileBrowserPrivate.addFileWatch(
190           entry.toURL(),
191           function(result) {
192             if (!result) {
193               this.watchedDirectoryEntry_ = null;
194               onError();
195             } else {
196               this.watchedDirectoryEntry_ = entry;
197               onSuccess();
198             }
199             callback();
200           }.bind(this));
201       this.filesystemMetadataObserverId_ = this.metadataCache_.addObserver(
202         entry,
203         MetadataCache.CHILDREN,
204         'filesystem',
205         this.onFilesystemMetadataChanged_.bind(this));
206       this.thumbnailMetadataObserverId_ = this.metadataCache_.addObserver(
207         entry,
208         MetadataCache.CHILDREN,
209         'thumbnail',
210         this.onThumbnailMetadataChanged_.bind(this));
211       this.driveMetadataObserverId_ = this.metadataCache_.addObserver(
212         entry,
213         MetadataCache.CHILDREN,
214         'drive',
215         this.onDriveMetadataChanged_.bind(this));
216     }.bind(this));
217   }.bind(this);
218
219   // Reset the watched directory first, then set the new watched directory.
220   this.resetWatchedEntry_(setEntryClosure, onError);
221 };
222
223 /**
224  * @return {DirectoryEntry} Current watched directory entry.
225  */
226 FileWatcher.prototype.getWatchedDirectoryEntry = function() {
227   return this.watchedDirectoryEntry_;
228 };