Home | History | Annotate | Download | only in 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.fileManagerPrivate.onDirectoryChanged.addListener(
     22       this.onDirectoryChangedBound_);
     23 
     24   this.filesystemMetadataObserverId_ = null;
     25   this.thumbnailMetadataObserverId_ = null;
     26   this.externalMetadataObserverId_ = 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.fileManagerPrivate.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 external 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.onExternalMetadataChanged_ = function(
     93     entries, properties) {
     94   this.dispatchMetadataEvent_('external', 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.fileManagerPrivate.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.externalMetadataObserverId_);
    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.fileManagerPrivate.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.externalMetadataObserverId_ = this.metadataCache_.addObserver(
    212           entry,
    213           MetadataCache.CHILDREN,
    214           'external',
    215           this.onExternalMetadataChanged_.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 };
    229