blob: bd5d55f4a6f4539f58e3bb25d841fd5f86e3f3fb [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. */
class FileWatcher extends cr.EventTarget {
constructor() {
super();
this.queue_ = new AsyncUtil.Queue();
this.watchedDirectoryEntry_ = null;
this.onDirectoryChangedBound_ = this.onDirectoryChanged_.bind(this);
chrome.fileManagerPrivate.onDirectoryChanged.addListener(
this.onDirectoryChangedBound_);
}
/**
* Stops watching (must be called before page unload).
*/
dispose() {
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
*/
onDirectoryChanged_(event) {
const fireWatcherDirectoryChanged = changedFiles => {
const e = new Event('watcher-directory-changed');
if (changedFiles) {
e.changedFiles = changedFiles;
}
this.dispatchEvent(e);
};
if (this.watchedDirectoryEntry_) {
const eventURL = event.entry.toURL();
const 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, () => {
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}
*/
changeWatchedDirectory(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
*/
resetWatchedEntry_() {
// Run the tasks in the queue to avoid races.
return new Promise((fulfill, reject) => {
this.queue_.run(callback => {
// Release the watched directory.
if (this.watchedDirectoryEntry_) {
chrome.fileManagerPrivate.removeFileWatch(
this.watchedDirectoryEntry_, 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();
});
} else {
fulfill();
callback();
}
});
});
}
/**
* Sets the watched entry to the passed directory. It's a best effort method.
* @param {!DirectoryEntry} entry Directory to be watched.
* @return {!Promise}
* @private
*/
changeWatchedEntry_(entry) {
return new Promise((fulfill, reject) => {
const setEntryClosure = () => {
// Run the tasks in the queue to avoid races.
this.queue_.run(callback => {
chrome.fileManagerPrivate.addFileWatch(entry, 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();
});
});
};
// Reset the watched directory first, then set the new watched directory.
return this.resetWatchedEntry_().then(setEntryClosure);
});
}
/**
* @return {DirectoryEntry} Current watched directory entry.
*/
getWatchedDirectoryEntry() {
return this.watchedDirectoryEntry_;
}
}