blob: fd3949b278ac0781a5b2cb0e4c3bea7047c84d11 [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.
/**
* Watches for changes in the tracked directory.
*
* @extends {cr.EventTarget}
* @constructor
*/
function FileWatcher() {
this.queue_ = new AsyncUtil.Queue();
this.watchedDirectoryEntry_ = null;
this.onDirectoryChangedBound_ = this.onDirectoryChanged_.bind(this);
chrome.fileManagerPrivate.onDirectoryChanged.addListener(
this.onDirectoryChangedBound_);
}
/**
* FileWatcher extends cr.EventTarget.
*/
FileWatcher.prototype.__proto__ = cr.EventTarget.prototype;
/**
* Stops watching (must be called before page unload).
*/
FileWatcher.prototype.dispose = function() {
chrome.fileManagerPrivate.onDirectoryChanged.removeListener(
this.onDirectoryChangedBound_);
if (this.watchedDirectoryEntry_)
this.resetWatchedEntry_();
};
/**
* Called when a file in the watched directory is changed.
* @param {chrome.fileManagerPrivate.FileWatchEvent} event Change event.
* @private
*/
FileWatcher.prototype.onDirectoryChanged_ = function(event) {
var fireWatcherDirectoryChanged = function(changedFiles) {
var e = new Event('watcher-directory-changed');
if (changedFiles)
e.changedFiles = changedFiles;
this.dispatchEvent(e);
}.bind(this);
if (this.watchedDirectoryEntry_) {
var eventURL = event.entry.toURL();
var watchedDirURL = this.watchedDirectoryEntry_.toURL();
if (eventURL === watchedDirURL) {
fireWatcherDirectoryChanged(event.changedFiles);
} else if (watchedDirURL.match(new RegExp('^' + eventURL))) {
// When watched directory is deleted by the change in parent directory,
// notify it as watcher directory changed.
this.watchedDirectoryEntry_.getDirectory(
this.watchedDirectoryEntry_.fullPath,
{create: false},
null,
function() { fireWatcherDirectoryChanged(null); });
}
}
};
/**
* 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|!FilesAppEntry} entry Directory entry to be tracked,
* or the fake entry.
* @return {!Promise}
*/
FileWatcher.prototype.changeWatchedDirectory = function(entry) {
if (!util.isFakeEntry(entry))
return this.changeWatchedEntry_(/** @type {!DirectoryEntry} */ (entry));
else
return this.resetWatchedEntry_();
};
/**
* Resets the watched entry. It's a best effort method.
* @return {!Promise}
* @private
*/
FileWatcher.prototype.resetWatchedEntry_ = function() {
// Run the tasks in the queue to avoid races.
return new Promise(function(fulfill, reject) {
this.queue_.run(function(callback) {
// Release the watched directory.
if (this.watchedDirectoryEntry_) {
chrome.fileManagerPrivate.removeFileWatch(
this.watchedDirectoryEntry_,
function(result) {
if (chrome.runtime.lastError) {
console.error('Failed to remove the watcher because of: ' +
chrome.runtime.lastError.message);
}
// Even on error reset the watcher locally, so at least the
// notifications are discarded.
this.watchedDirectoryEntry_ = null;
fulfill();
callback();
}.bind(this));
} else {
fulfill();
callback();
}
}.bind(this));
}.bind(this));
};
/**
* Sets the watched entry to the passed directory. It's a best effort method.
* @param {!DirectoryEntry} entry Directory to be watched.
* @return {!Promise}
* @private
*/
FileWatcher.prototype.changeWatchedEntry_ = function(entry) {
return new Promise(function(fulfill, reject) {
var setEntryClosure = function() {
// Run the tasks in the queue to avoid races.
this.queue_.run(function(callback) {
chrome.fileManagerPrivate.addFileWatch(
entry,
function(result) {
if (chrome.runtime.lastError) {
// Most probably setting the watcher is not supported on the
// file system type.
console.info('File watchers not supported for: ' +
entry.toURL());
this.watchedDirectoryEntry_ = null;
fulfill();
} else {
this.watchedDirectoryEntry_ = assert(entry);
fulfill();
}
callback();
}.bind(this));
}.bind(this));
}.bind(this);
// Reset the watched directory first, then set the new watched directory.
return this.resetWatchedEntry_().then(setEntryClosure);
}.bind(this));
};
/**
* @return {DirectoryEntry} Current watched directory entry.
*/
FileWatcher.prototype.getWatchedDirectoryEntry = function() {
return this.watchedDirectoryEntry_;
};