blob: a6414e61f4af868808d84082953db1dcb29e8a03 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
'use strict';
/**
* Watches for changes in the tracked directory, including local metadata
* changes.
*
* @param {MetadataCache} metadataCache Instance of MetadataCache.
* @extends {cr.EventTarget}
* @constructor
*/
function FileWatcher(metadataCache) {
this.queue_ = new AsyncUtil.Queue();
this.metadataCache_ = metadataCache;
this.watchedDirectoryEntry_ = null;
this.onDirectoryChangedBound_ = this.onDirectoryChanged_.bind(this);
chrome.fileBrowserPrivate.onDirectoryChanged.addListener(
this.onDirectoryChangedBound_);
this.filesystemMetadataObserverId_ = null;
this.thumbnailMetadataObserverId_ = null;
this.driveMetadataObserverId_ = null;
}
/**
* FileWatcher extends cr.EventTarget.
*/
FileWatcher.prototype.__proto__ = cr.EventTarget.prototype;
/**
* Stops watching (must be called before page unload).
*/
FileWatcher.prototype.dispose = function() {
chrome.fileBrowserPrivate.onDirectoryChanged.removeListener(
this.onDirectoryChangedBound_);
if (this.watchedDirectoryEntry_)
this.resetWatchedEntry_(function() {}, function() {});
};
/**
* Called when a file in the watched directory is changed.
* @param {Event} event Change event.
* @private
*/
FileWatcher.prototype.onDirectoryChanged_ = function(event) {
if (this.watchedDirectoryEntry_ &&
event.entry.toURL() === this.watchedDirectoryEntry_.toURL()) {
var e = new Event('watcher-directory-changed');
this.dispatchEvent(e);
}
};
/**
* Called when general metadata in the watched directory has been changed.
*
* @param {Array.<Entry>} entries Array of entries.
* @param {Object.<string, Object>} properties Map from entry URLs to metadata
* properties.
* @private
*/
FileWatcher.prototype.onFilesystemMetadataChanged_ = function(
entries, properties) {
this.dispatchMetadataEvent_('filesystem', entries, properties);
};
/**
* Called when thumbnail metadata in the watched directory has been changed.
*
* @param {Array.<Entry>} entries Arrray of entries.
* @param {Object.<string, Object>} properties Map from entry URLs to metadata
* properties.
* @private
*/
FileWatcher.prototype.onThumbnailMetadataChanged_ = function(
entries, properties) {
this.dispatchMetadataEvent_('thumbnail', entries, properties);
};
/**
* Called when drive metadata in the watched directory has been changed.
*
* @param {Array.<Entry>} entries Array of entries.
* @param {Object.<string, Object>} properties Map from entry URLs to metadata
* properties.
* @private
*/
FileWatcher.prototype.onDriveMetadataChanged_ = function(
entries, properties) {
this.dispatchMetadataEvent_('drive', entries, properties);
};
/**
* Dispatches an event about detected change in metadata within the tracked
* directory.
*
* @param {string} type Type of the metadata change.
* @param {Array.<Entry>} entries Array of entries.
* @param {Object.<string, Object>} properties Map from entry URLs to metadata
* properties.
* @private
*/
FileWatcher.prototype.dispatchMetadataEvent_ = function(
type, entries, properties) {
var e = new Event('watcher-metadata-changed');
e.metadataType = type;
e.entries = entries;
e.properties = properties;
this.dispatchEvent(e);
};
/**
* Changes the watched directory. In case of a fake entry, the watch is
* just released, since there is no reason to track a fake directory.
*
* @param {!DirectoryEntry|!Object} entry Directory entry to be tracked, or the
* fake entry.
* @param {function()} callback Completion callback.
*/
FileWatcher.prototype.changeWatchedDirectory = function(entry, callback) {
if (entry && entry.toURL) {
this.changeWatchedEntry_(
entry,
callback,
function() {
console.error(
'Unable to change the watched directory to: ' + entry.toURL());
callback();
});
} else {
this.resetWatchedEntry_(
callback,
function() {
console.error('Unable to reset the watched directory.');
callback();
});
}
};
/**
* Resets the watched entry to the passed directory.
*
* @param {function()} onSuccess Success callback.
* @param {function()} onError Error callback.
* @private
*/
FileWatcher.prototype.resetWatchedEntry_ = function(onSuccess, onError) {
// Run the tasks in the queue to avoid races.
this.queue_.run(function(callback) {
// Release the watched directory.
if (this.watchedDirectoryEntry_) {
chrome.fileBrowserPrivate.removeFileWatch(
this.watchedDirectoryEntry_.toURL(),
function(result) {
this.watchedDirectoryEntry_ = null;
if (result)
onSuccess();
else
onError();
callback();
}.bind(this));
this.metadataCache_.removeObserver(this.filesystemMetadataObserverId_);
this.metadataCache_.removeObserver(this.thumbnailMetadataObserverId_);
this.metadataCache_.removeObserver(this.driveMetadataObserverId_);
} else {
onSuccess();
callback();
}
}.bind(this));
};
/**
* Sets the watched entry to the passed directory.
*
* @param {!DirectoryEntry} entry Directory to be watched.
* @param {function()} onSuccess Success callback.
* @param {function()} onError Error callback.
* @private
*/
FileWatcher.prototype.changeWatchedEntry_ = function(
entry, onSuccess, onError) {
var setEntryClosure = function() {
// Run the tasks in the queue to avoid races.
this.queue_.run(function(callback) {
chrome.fileBrowserPrivate.addFileWatch(
entry.toURL(),
function(result) {
if (!result) {
this.watchedDirectoryEntry_ = null;
onError();
} else {
this.watchedDirectoryEntry_ = entry;
onSuccess();
}
callback();
}.bind(this));
this.filesystemMetadataObserverId_ = this.metadataCache_.addObserver(
entry,
MetadataCache.CHILDREN,
'filesystem',
this.onFilesystemMetadataChanged_.bind(this));
this.thumbnailMetadataObserverId_ = this.metadataCache_.addObserver(
entry,
MetadataCache.CHILDREN,
'thumbnail',
this.onThumbnailMetadataChanged_.bind(this));
this.driveMetadataObserverId_ = this.metadataCache_.addObserver(
entry,
MetadataCache.CHILDREN,
'drive',
this.onDriveMetadataChanged_.bind(this));
}.bind(this));
}.bind(this);
// Reset the watched directory first, then set the new watched directory.
this.resetWatchedEntry_(setEntryClosure, onError);
};
/**
* @return {DirectoryEntry} Current watched directory entry.
*/
FileWatcher.prototype.getWatchedDirectoryEntry = function() {
return this.watchedDirectoryEntry_;
};